diff --git a/.release-please-manifest.json b/.release-please-manifest.json index aebbc8bc..f70d9978 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.101.0" + ".": "0.102.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 77497eb7..80c45f21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## [0.102.0](https://github.com/kernel/kernel-python-sdk/compare/v0.101.0...v0.102.0) (2026-09-14) + + +### Features + +* Accept a workload intent and use it to widen vendor coverage ([39ffff8](https://github.com/kernel/kernel-python-sdk/commit/39ffff82be3987e2220d8d5057d8d2505991a3eb)) +* Add guarded vault card fill operations ([3927c96](https://github.com/kernel/kernel-python-sdk/commit/3927c961789b4427a72c979ee5bb3dbcae1b8c74)) +* Add single-use AgentCard prepare_checkout for Square ([c439359](https://github.com/kernel/kernel-python-sdk/commit/c43935913b8a471421ef8d96c9066c442ec95171)) +* Allow replacing AgentCard cards after unknown creates ([22a8731](https://github.com/kernel/kernel-python-sdk/commit/22a87318e6ac2f1318a295b9d22de5b2306e66b2)) +* Document punctuation key sequences ([e7850fd](https://github.com/kernel/kernel-python-sdk/commit/e7850fd5757934c731373c5cb09b4444a29d7e84)) +* Honor managed auth browser regions ([0fa2fa3](https://github.com/kernel/kernel-python-sdk/commit/0fa2fa34cd72d04a12c6f9afd61d87098d43799b)) +* Reapply vendor guidance recommendations ([dfcc7ec](https://github.com/kernel/kernel-python-sdk/commit/dfcc7ecbaa087a07d11a5b0eb4738b41b8fadd8b)) +* Report proxy-restricted targets from config registry lookup ([c276ebc](https://github.com/kernel/kernel-python-sdk/commit/c276ebc6fd266924894b300b13ff02f30d0c8527)) + ## [0.101.0](https://github.com/kernel/kernel-python-sdk/compare/v0.100.0...v0.101.0) (2026-09-11) diff --git a/api.md b/api.md index 63957ab1..1c9b02a3 100644 --- a/api.md +++ b/api.md @@ -525,12 +525,21 @@ Types: ```python from kernel.types.vaults import ( AgentcardCheckoutAuthorization, + AgentcardCheckoutPreparation, + AuthorizeVaultItemOperationRequest, CardVaultItemSpec, CardVaultItemState, + FillVaultItemOperationRequest, + FillVaultItemOperationResult, + PrepareCheckoutVaultItemOperationRequest, VaultCardAliases, + VaultCardFillField, + VaultCheckoutContext, + VaultFillFieldResult, VaultItem, VaultItemAction, VaultItemEvent, + VaultItemOperationResponse, VaultPaymentMethod, WalletVaultItemSpec, WalletVaultItemState, @@ -546,7 +555,7 @@ Methods: - client.vaults.items.list(id_or_name) -> ItemListResponse - client.vaults.items.delete(key, \*, id_or_name) -> None - client.vaults.items.events(key, \*, id_or_name, \*\*params) -> ItemEventsResponse -- client.vaults.items.perform_operation(key, \*, id_or_name, \*\*params) -> VaultItem +- client.vaults.items.perform_operation(key, \*, id_or_name, \*\*params) -> VaultItemOperationResponse - client.vaults.items.upsert(key, \*, id_or_name, \*\*params) -> VaultItem # Credentials diff --git a/pyproject.toml b/pyproject.toml index ce24d358..473c0ec0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "kernel" -version = "0.101.0" +version = "0.102.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 058933d0..b5daff26 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.101.0" # x-release-please-version +__version__ = "0.102.0" # x-release-please-version diff --git a/src/kernel/resources/browsers/computer.py b/src/kernel/resources/browsers/computer.py index dcb242c6..b40ac897 100644 --- a/src/kernel/resources/browsers/computer.py +++ b/src/kernel/resources/browsers/computer.py @@ -397,7 +397,9 @@ def press_key( keys: List of key symbols to press. Each item should be a key symbol supported by xdotool (see X11 keysym definitions). Examples include "Return", "Shift", "Ctrl", "Alt", "F5". Items in this list could also be combinations, e.g. - "Ctrl+t" or "Ctrl+Shift+Tab". + "Ctrl+t" or "Ctrl+Shift+Tab". Use X11 names for punctuation in combinations, + such as "Ctrl+minus" or "Ctrl+plus". A literal hyphen is also accepted as an + alias, so "Ctrl+-" is normalized to "Ctrl+minus". duration: Duration to hold the keys down in milliseconds. If omitted or 0, keys are tapped. @@ -1002,7 +1004,9 @@ async def press_key( keys: List of key symbols to press. Each item should be a key symbol supported by xdotool (see X11 keysym definitions). Examples include "Return", "Shift", "Ctrl", "Alt", "F5". Items in this list could also be combinations, e.g. - "Ctrl+t" or "Ctrl+Shift+Tab". + "Ctrl+t" or "Ctrl+Shift+Tab". Use X11 names for punctuation in combinations, + such as "Ctrl+minus" or "Ctrl+plus". A literal hyphen is also accepted as an + alias, so "Ctrl+-" is normalized to "Ctrl+minus". duration: Duration to hold the keys down in milliseconds. If omitted or 0, keys are tapped. diff --git a/src/kernel/resources/config_registry/config_registry.py b/src/kernel/resources/config_registry/config_registry.py index 0b9d1824..27a8766c 100644 --- a/src/kernel/resources/config_registry/config_registry.py +++ b/src/kernel/resources/config_registry/config_registry.py @@ -165,6 +165,7 @@ def resolve( *, url: str, allowed_proxy_countries: SequenceNotStr[str] | Omit = omit, + intent: 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, @@ -184,6 +185,14 @@ def resolve( configuration. Kernel may test a subset of allowed countries. When omitted, Kernel uses its default country selection. + intent: Plain-language description of the workload you intend to run against this + target, in a sentence or two. Requires an https target, because the pass treats + any non-HTTPS destination as off-site and will not drive an http one. Kernel + uses it to drive the browser further into the site, where it can observe + protections that only appear once a session interacts. When this target already + has a verified configuration, the run confirms that one instead of re-deriving + the whole matrix, so supplying an intent narrows what can be recommended. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -198,6 +207,7 @@ def resolve( { "url": url, "allowed_proxy_countries": allowed_proxy_countries, + "intent": intent, }, config_registry_resolve_params.ConfigRegistryResolveParams, ), @@ -339,6 +349,7 @@ async def resolve( *, url: str, allowed_proxy_countries: SequenceNotStr[str] | Omit = omit, + intent: 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, @@ -358,6 +369,14 @@ async def resolve( configuration. Kernel may test a subset of allowed countries. When omitted, Kernel uses its default country selection. + intent: Plain-language description of the workload you intend to run against this + target, in a sentence or two. Requires an https target, because the pass treats + any non-HTTPS destination as off-site and will not drive an http one. Kernel + uses it to drive the browser further into the site, where it can observe + protections that only appear once a session interacts. When this target already + has a verified configuration, the run confirms that one instead of re-deriving + the whole matrix, so supplying an intent narrows what can be recommended. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -372,6 +391,7 @@ async def resolve( { "url": url, "allowed_proxy_countries": allowed_proxy_countries, + "intent": intent, }, config_registry_resolve_params.ConfigRegistryResolveParams, ), diff --git a/src/kernel/resources/vaults/items.py b/src/kernel/resources/vaults/items.py index dd790910..02315e2b 100644 --- a/src/kernel/resources/vaults/items.py +++ b/src/kernel/resources/vaults/items.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Any, List, cast +from typing import Any, List, Iterable, cast from typing_extensions import Literal, overload import httpx @@ -29,6 +29,9 @@ from ...types.vaults.item_list_response import ItemListResponse from ...types.vaults.item_events_response import ItemEventsResponse from ...types.vaults.card_vault_item_spec_param import CardVaultItemSpecParam +from ...types.vaults.vault_card_fill_field_param import VaultCardFillFieldParam +from ...types.vaults.vault_checkout_context_param import VaultCheckoutContextParam +from ...types.vaults.vault_item_operation_response import VaultItemOperationResponse __all__ = ["ItemsResource", "AsyncItemsResource"] @@ -209,9 +212,11 @@ def delete( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: """ - Unresolved payment operations block deletion, including operations on child - cards of a wallet. Reconcile the original attempt with the provider or support - first; deleting or recreating an item is not proof that a payment did not occur. + Unresolved payment operations normally block deletion, including operations on + child cards of a wallet. An AgentCard card in recovery_required whose checkout + create response returned no authorization ID may be explicitly abandoned by + deleting that card directly; deleting its wallet or vault remains blocked. + Deleting or recreating an item is not proof that a payment did not occur. Args: extra_headers: Send extra headers @@ -287,6 +292,7 @@ def events( cast_to=ItemEventsResponse, ) + @overload def perform_operation( self, key: str, @@ -299,17 +305,80 @@ def perform_operation( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> VaultItem: + ) -> VaultItemOperationResponse: + """ + Retrieve the item first and invoke only an operation listed in + `available_operations`, following its natural-language description. Availability + is rechecked at execution time; unavailable operations return 409. Authorization + and preparation may call an external provider and return updated state. Link + cards advertise authorize without checkout context. Eligible unused AgentCard + cards advertise prepare_checkout, which requires checkout context and obtains + device approval before native Square Pay. Keep the returned approval page open, + poll until ready_to_submit, then submit before preparation.expires_at. Unused + preparations expire automatically and cannot be reused. If spend-request + creation is rate limited, returns HTTP 429 with code + `spend_request_rate_limited`; stop and back off before retrying. + + Fill returns a value-free execution result. Validation failures before writing + return 400 (invalid request or targets), 403 (access or destination denied), 404 + (resource not found), or 409 (item or browser not ready). Once writing starts, + known partial failures and indeterminate field outcomes return 200 with status + `failed` or `unknown`, not an automatic-retry signal. A transport error may + leave the outcome unknown; do not automatically retry. + + 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 + """ + ... + + @overload + def perform_operation( + self, + key: str, + *, + id_or_name: str, + checkout: VaultCheckoutContextParam, + type: Literal["prepare_checkout"], + # 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, + ) -> VaultItemOperationResponse: """ Retrieve the item first and invoke only an operation listed in - `available_operations`, following its natural-language description. Operations - may call an external provider and return updated state. Link cards advertise - authorize. AgentCard cards are created with PUT and request approval when their - aliases are used at checkout; they do not expose this operation. If - spend-request creation is rate limited, returns HTTP 429 with code + `available_operations`, following its natural-language description. Availability + is rechecked at execution time; unavailable operations return 409. Authorization + and preparation may call an external provider and return updated state. Link + cards advertise authorize without checkout context. Eligible unused AgentCard + cards advertise prepare_checkout, which requires checkout context and obtains + device approval before native Square Pay. Keep the returned approval page open, + poll until ready_to_submit, then submit before preparation.expires_at. Unused + preparations expire automatically and cannot be reused. If spend-request + creation is rate limited, returns HTTP 429 with code `spend_request_rate_limited`; stop and back off before retrying. + Fill returns a value-free execution result. Validation failures before writing + return 400 (invalid request or targets), 403 (access or destination denied), 404 + (resource not found), or 409 (item or browser not ready). Once writing starts, + known partial failures and indeterminate field outcomes return 200 with status + `failed` or `unknown`, not an automatic-retry signal. A transport error may + leave the outcome unknown; do not automatically retry. + Args: + checkout: Required when preparing an unused AgentCard card for Square. Consent is bound to + this browser and declared merchant origin, not a tab. Wait for the item's + ready_to_submit status before native Pay and submit within its readiness + deadline. Unused preparations expire automatically; every preparation is + single-use, including after failure or expiry. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -318,19 +387,115 @@ def perform_operation( timeout: Override the client-level default timeout for this request, in seconds """ + ... + + @overload + def perform_operation( + self, + key: str, + *, + id_or_name: str, + browser_id: str, + fields: Iterable[VaultCardFillFieldParam], + page_url: str, + type: Literal["fill"], + timeout_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VaultItemOperationResponse: + """ + Retrieve the item first and invoke only an operation listed in + `available_operations`, following its natural-language description. Availability + is rechecked at execution time; unavailable operations return 409. Authorization + and preparation may call an external provider and return updated state. Link + cards advertise authorize without checkout context. Eligible unused AgentCard + cards advertise prepare_checkout, which requires checkout context and obtains + device approval before native Square Pay. Keep the returned approval page open, + poll until ready_to_submit, then submit before preparation.expires_at. Unused + preparations expire automatically and cannot be reused. If spend-request + creation is rate limited, returns HTTP 429 with code + `spend_request_rate_limited`; stop and back off before retrying. + + Fill returns a value-free execution result. Validation failures before writing + return 400 (invalid request or targets), 403 (access or destination denied), 404 + (resource not found), or 409 (item or browser not ready). Once writing starts, + known partial failures and indeterminate field outcomes return 200 with status + `failed` or `unknown`, not an automatic-retry signal. A transport error may + leave the outcome unknown; do not automatically retry. + + Args: + browser_id: Browser session ID, not a reusable browser name. + + fields: Field bindings for this step. No two bindings may resolve to the same element. + + page_url: Exact current top-level page URL, including path, query, and fragment. Must + match exactly one open page in the browser; zero or multiple matches fail. No + prefix or glob matching. Must use HTTPS without embedded credentials. + + timeout_ms: Total operation deadline in milliseconds, not a per-field timeout. + + 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 + """ + ... + + @required_args( + ["id_or_name", "type"], + ["id_or_name", "checkout", "type"], + ["id_or_name", "browser_id", "fields", "page_url", "type"], + ) + def perform_operation( + self, + key: str, + *, + id_or_name: str, + type: Literal["authorize"] | Literal["prepare_checkout"] | Literal["fill"], + checkout: VaultCheckoutContextParam | Omit = omit, + browser_id: str | Omit = omit, + fields: Iterable[VaultCardFillFieldParam] | Omit = omit, + page_url: str | Omit = omit, + timeout_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VaultItemOperationResponse: if not id_or_name: raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}") if not key: raise ValueError(f"Expected a non-empty value for `key` but received {key!r}") return cast( - VaultItem, + VaultItemOperationResponse, self._post( path_template("/vaults/{id_or_name}/items/{key}/operations", id_or_name=id_or_name, key=key), - body=maybe_transform({"type": type}, item_perform_operation_params.ItemPerformOperationParams), + body=maybe_transform( + { + "type": type, + "checkout": checkout, + "browser_id": browser_id, + "fields": fields, + "page_url": page_url, + "timeout_ms": timeout_ms, + }, + item_perform_operation_params.ItemPerformOperationParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=cast(Any, VaultItem), # Union types cannot be passed in as arguments in the type system + cast_to=cast( + Any, VaultItemOperationResponse + ), # Union types cannot be passed in as arguments in the type system ), ) @@ -625,9 +790,11 @@ async def delete( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> None: """ - Unresolved payment operations block deletion, including operations on child - cards of a wallet. Reconcile the original attempt with the provider or support - first; deleting or recreating an item is not proof that a payment did not occur. + Unresolved payment operations normally block deletion, including operations on + child cards of a wallet. An AgentCard card in recovery_required whose checkout + create response returned no authorization ID may be explicitly abandoned by + deleting that card directly; deleting its wallet or vault remains blocked. + Deleting or recreating an item is not proof that a payment did not occur. Args: extra_headers: Send extra headers @@ -703,6 +870,7 @@ async def events( cast_to=ItemEventsResponse, ) + @overload async def perform_operation( self, key: str, @@ -715,17 +883,80 @@ async def perform_operation( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> VaultItem: + ) -> VaultItemOperationResponse: + """ + Retrieve the item first and invoke only an operation listed in + `available_operations`, following its natural-language description. Availability + is rechecked at execution time; unavailable operations return 409. Authorization + and preparation may call an external provider and return updated state. Link + cards advertise authorize without checkout context. Eligible unused AgentCard + cards advertise prepare_checkout, which requires checkout context and obtains + device approval before native Square Pay. Keep the returned approval page open, + poll until ready_to_submit, then submit before preparation.expires_at. Unused + preparations expire automatically and cannot be reused. If spend-request + creation is rate limited, returns HTTP 429 with code + `spend_request_rate_limited`; stop and back off before retrying. + + Fill returns a value-free execution result. Validation failures before writing + return 400 (invalid request or targets), 403 (access or destination denied), 404 + (resource not found), or 409 (item or browser not ready). Once writing starts, + known partial failures and indeterminate field outcomes return 200 with status + `failed` or `unknown`, not an automatic-retry signal. A transport error may + leave the outcome unknown; do not automatically retry. + + 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 + """ + ... + + @overload + async def perform_operation( + self, + key: str, + *, + id_or_name: str, + checkout: VaultCheckoutContextParam, + type: Literal["prepare_checkout"], + # 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, + ) -> VaultItemOperationResponse: """ Retrieve the item first and invoke only an operation listed in - `available_operations`, following its natural-language description. Operations - may call an external provider and return updated state. Link cards advertise - authorize. AgentCard cards are created with PUT and request approval when their - aliases are used at checkout; they do not expose this operation. If - spend-request creation is rate limited, returns HTTP 429 with code + `available_operations`, following its natural-language description. Availability + is rechecked at execution time; unavailable operations return 409. Authorization + and preparation may call an external provider and return updated state. Link + cards advertise authorize without checkout context. Eligible unused AgentCard + cards advertise prepare_checkout, which requires checkout context and obtains + device approval before native Square Pay. Keep the returned approval page open, + poll until ready_to_submit, then submit before preparation.expires_at. Unused + preparations expire automatically and cannot be reused. If spend-request + creation is rate limited, returns HTTP 429 with code `spend_request_rate_limited`; stop and back off before retrying. + Fill returns a value-free execution result. Validation failures before writing + return 400 (invalid request or targets), 403 (access or destination denied), 404 + (resource not found), or 409 (item or browser not ready). Once writing starts, + known partial failures and indeterminate field outcomes return 200 with status + `failed` or `unknown`, not an automatic-retry signal. A transport error may + leave the outcome unknown; do not automatically retry. + Args: + checkout: Required when preparing an unused AgentCard card for Square. Consent is bound to + this browser and declared merchant origin, not a tab. Wait for the item's + ready_to_submit status before native Pay and submit within its readiness + deadline. Unused preparations expire automatically; every preparation is + single-use, including after failure or expiry. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -734,21 +965,115 @@ async def perform_operation( timeout: Override the client-level default timeout for this request, in seconds """ + ... + + @overload + async def perform_operation( + self, + key: str, + *, + id_or_name: str, + browser_id: str, + fields: Iterable[VaultCardFillFieldParam], + page_url: str, + type: Literal["fill"], + timeout_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VaultItemOperationResponse: + """ + Retrieve the item first and invoke only an operation listed in + `available_operations`, following its natural-language description. Availability + is rechecked at execution time; unavailable operations return 409. Authorization + and preparation may call an external provider and return updated state. Link + cards advertise authorize without checkout context. Eligible unused AgentCard + cards advertise prepare_checkout, which requires checkout context and obtains + device approval before native Square Pay. Keep the returned approval page open, + poll until ready_to_submit, then submit before preparation.expires_at. Unused + preparations expire automatically and cannot be reused. If spend-request + creation is rate limited, returns HTTP 429 with code + `spend_request_rate_limited`; stop and back off before retrying. + + Fill returns a value-free execution result. Validation failures before writing + return 400 (invalid request or targets), 403 (access or destination denied), 404 + (resource not found), or 409 (item or browser not ready). Once writing starts, + known partial failures and indeterminate field outcomes return 200 with status + `failed` or `unknown`, not an automatic-retry signal. A transport error may + leave the outcome unknown; do not automatically retry. + + Args: + browser_id: Browser session ID, not a reusable browser name. + + fields: Field bindings for this step. No two bindings may resolve to the same element. + + page_url: Exact current top-level page URL, including path, query, and fragment. Must + match exactly one open page in the browser; zero or multiple matches fail. No + prefix or glob matching. Must use HTTPS without embedded credentials. + + timeout_ms: Total operation deadline in milliseconds, not a per-field timeout. + + 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 + """ + ... + + @required_args( + ["id_or_name", "type"], + ["id_or_name", "checkout", "type"], + ["id_or_name", "browser_id", "fields", "page_url", "type"], + ) + async def perform_operation( + self, + key: str, + *, + id_or_name: str, + type: Literal["authorize"] | Literal["prepare_checkout"] | Literal["fill"], + checkout: VaultCheckoutContextParam | Omit = omit, + browser_id: str | Omit = omit, + fields: Iterable[VaultCardFillFieldParam] | Omit = omit, + page_url: str | Omit = omit, + timeout_ms: int | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VaultItemOperationResponse: if not id_or_name: raise ValueError(f"Expected a non-empty value for `id_or_name` but received {id_or_name!r}") if not key: raise ValueError(f"Expected a non-empty value for `key` but received {key!r}") return cast( - VaultItem, + VaultItemOperationResponse, await self._post( path_template("/vaults/{id_or_name}/items/{key}/operations", id_or_name=id_or_name, key=key), body=await async_maybe_transform( - {"type": type}, item_perform_operation_params.ItemPerformOperationParams + { + "type": type, + "checkout": checkout, + "browser_id": browser_id, + "fields": fields, + "page_url": page_url, + "timeout_ms": timeout_ms, + }, + item_perform_operation_params.ItemPerformOperationParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=cast(Any, VaultItem), # Union types cannot be passed in as arguments in the type system + cast_to=cast( + Any, VaultItemOperationResponse + ), # Union types cannot be passed in as arguments in the type system ), ) diff --git a/src/kernel/types/auth/managed_auth_browser_config.py b/src/kernel/types/auth/managed_auth_browser_config.py index c7e565d1..3309904e 100644 --- a/src/kernel/types/auth/managed_auth_browser_config.py +++ b/src/kernel/types/auth/managed_auth_browser_config.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional +from typing_extensions import Literal from ..._models import BaseModel from ..browser_proxy_config import BrowserProxyConfig @@ -108,6 +109,14 @@ class ManagedAuthBrowserConfig(BaseModel): preserve or inherit the connection default. """ + region: Optional[Literal["us-east", "eu-west", "ap-southeast"]] = None + """Browser region. + + Omit on create to use us-east, on update to keep the current region, or on login + to inherit it. Login overrides apply only to that login. Non-default regions + require an eligible plan and organization access. + """ + stealth: Optional[bool] = None """Whether managed auth browser sessions use stealth mode. diff --git a/src/kernel/types/auth/managed_auth_browser_config_param.py b/src/kernel/types/auth/managed_auth_browser_config_param.py index bfd09f95..6f8eab6d 100644 --- a/src/kernel/types/auth/managed_auth_browser_config_param.py +++ b/src/kernel/types/auth/managed_auth_browser_config_param.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Optional -from typing_extensions import TypedDict +from typing_extensions import Literal, TypedDict from ..browser_proxy_config_param import BrowserProxyConfigParam from ..browsers.browser_telemetry_categories_config_param import BrowserTelemetryCategoriesConfigParam @@ -110,6 +110,14 @@ class ManagedAuthBrowserConfigParam(TypedDict, total=False): preserve or inherit the connection default. """ + region: Literal["us-east", "eu-west", "ap-southeast"] + """Browser region. + + Omit on create to use us-east, on update to keep the current region, or on login + to inherit it. Login overrides apply only to that login. Non-default regions + require an eligible plan and organization access. + """ + stealth: bool """Whether managed auth browser sessions use stealth mode. diff --git a/src/kernel/types/browsers/computer_batch_params.py b/src/kernel/types/browsers/computer_batch_params.py index 7fc6abb5..dbd91756 100644 --- a/src/kernel/types/browsers/computer_batch_params.py +++ b/src/kernel/types/browsers/computer_batch_params.py @@ -110,7 +110,10 @@ class ActionPressKey(TypedDict, total=False): Each item should be a key symbol supported by xdotool (see X11 keysym definitions). Examples include "Return", "Shift", "Ctrl", "Alt", "F5". Items in - this list could also be combinations, e.g. "Ctrl+t" or "Ctrl+Shift+Tab". + this list could also be combinations, e.g. "Ctrl+t" or "Ctrl+Shift+Tab". Use X11 + names for punctuation in combinations, such as "Ctrl+minus" or "Ctrl+plus". A + literal hyphen is also accepted as an alias, so "Ctrl+-" is normalized to + "Ctrl+minus". """ duration: int diff --git a/src/kernel/types/browsers/computer_press_key_params.py b/src/kernel/types/browsers/computer_press_key_params.py index ea2c9b45..8722e1b3 100644 --- a/src/kernel/types/browsers/computer_press_key_params.py +++ b/src/kernel/types/browsers/computer_press_key_params.py @@ -15,7 +15,10 @@ class ComputerPressKeyParams(TypedDict, total=False): Each item should be a key symbol supported by xdotool (see X11 keysym definitions). Examples include "Return", "Shift", "Ctrl", "Alt", "F5". Items in - this list could also be combinations, e.g. "Ctrl+t" or "Ctrl+Shift+Tab". + this list could also be combinations, e.g. "Ctrl+t" or "Ctrl+Shift+Tab". Use X11 + names for punctuation in combinations, such as "Ctrl+minus" or "Ctrl+plus". A + literal hyphen is also accepted as an alias, so "Ctrl+-" is normalized to + "Ctrl+minus". """ duration: int diff --git a/src/kernel/types/config_registry_resolve_params.py b/src/kernel/types/config_registry_resolve_params.py index 644c4b7a..2c0ecc00 100644 --- a/src/kernel/types/config_registry_resolve_params.py +++ b/src/kernel/types/config_registry_resolve_params.py @@ -19,3 +19,14 @@ class ConfigRegistryResolveParams(TypedDict, total=False): configuration. Kernel may test a subset of allowed countries. When omitted, Kernel uses its default country selection. """ + + intent: str + """ + Plain-language description of the workload you intend to run against this + target, in a sentence or two. Requires an https target, because the pass treats + any non-HTTPS destination as off-site and will not drive an http one. Kernel + uses it to drive the browser further into the site, where it can observe + protections that only appear once a session interacts. When this target already + has a verified configuration, the run confirms that one instead of re-deriving + the whole matrix, so supplying an intent narrows what can be recommended. + """ diff --git a/src/kernel/types/config_registry_response.py b/src/kernel/types/config_registry_response.py index 9995173f..dba10b9e 100644 --- a/src/kernel/types/config_registry_response.py +++ b/src/kernel/types/config_registry_response.py @@ -1,6 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional +from typing_extensions import Literal from .target import Target from .._models import BaseModel @@ -21,3 +22,20 @@ class ConfigRegistryResponse(BaseModel): """A recommendation or a structured no-recommendation result.""" target: Target + + guidance: Optional[str] = None + """Short advisory markdown to facilitate navigating this target. + + Returned even when no configuration reached the target, since knowing what + prevented success is useful without a configuration. Not verified against this + target. Null when nothing applicable was observed or no notes exist. + """ + + workload_outcome: Optional[ + Literal["completed", "turn_limit", "auth_required", "payment_required", "blocked", "error"] + ] = None + """How far the workload pass got, when an intent was supplied and a pass ran. + + A run outcome rather than advice, so it is reported whether or not any guidance + could be assembled. Null when no intent was supplied or no pass ran. + """ diff --git a/src/kernel/types/lookup_response.py b/src/kernel/types/lookup_response.py index cf0f5c16..d717b8cf 100644 --- a/src/kernel/types/lookup_response.py +++ b/src/kernel/types/lookup_response.py @@ -4,12 +4,21 @@ from .target import Target from .._models import BaseModel -from .recommendation import Recommendation +from .recommendation_result import RecommendationResult __all__ = ["LookupResponse"] class LookupResponse(BaseModel): - recommendation: Optional[Recommendation] = None + recommendation: Optional[RecommendationResult] = None + """A recommendation or a structured no-recommendation result.""" target: Target + + guidance: Optional[str] = None + """Short advisory markdown to facilitate navigating this target. + + Returned even when no configuration reached the target, since knowing what + prevented success is useful without a configuration. Not verified against this + target. Null when nothing applicable was observed or no notes exist. + """ diff --git a/src/kernel/types/vaults/__init__.py b/src/kernel/types/vaults/__init__.py index 623e45d4..b48fd00d 100644 --- a/src/kernel/types/vaults/__init__.py +++ b/src/kernel/types/vaults/__init__.py @@ -16,7 +16,22 @@ from .vault_payment_method import VaultPaymentMethod as VaultPaymentMethod from .card_vault_item_state import CardVaultItemState as CardVaultItemState from .wallet_vault_item_spec import WalletVaultItemSpec as WalletVaultItemSpec +from .vault_fill_field_result import VaultFillFieldResult as VaultFillFieldResult from .wallet_vault_item_state import WalletVaultItemState as WalletVaultItemState from .card_vault_item_spec_param import CardVaultItemSpecParam as CardVaultItemSpecParam +from .vault_card_fill_field_param import VaultCardFillFieldParam as VaultCardFillFieldParam +from .vault_checkout_context_param import VaultCheckoutContextParam as VaultCheckoutContextParam from .item_perform_operation_params import ItemPerformOperationParams as ItemPerformOperationParams +from .vault_item_operation_response import VaultItemOperationResponse as VaultItemOperationResponse +from .agentcard_checkout_preparation import AgentcardCheckoutPreparation as AgentcardCheckoutPreparation from .agentcard_checkout_authorization import AgentcardCheckoutAuthorization as AgentcardCheckoutAuthorization +from .fill_vault_item_operation_result import FillVaultItemOperationResult as FillVaultItemOperationResult +from .fill_vault_item_operation_request_param import ( + FillVaultItemOperationRequestParam as FillVaultItemOperationRequestParam, +) +from .authorize_vault_item_operation_request_param import ( + AuthorizeVaultItemOperationRequestParam as AuthorizeVaultItemOperationRequestParam, +) +from .prepare_checkout_vault_item_operation_request_param import ( + PrepareCheckoutVaultItemOperationRequestParam as PrepareCheckoutVaultItemOperationRequestParam, +) diff --git a/src/kernel/types/vaults/agentcard_checkout_preparation.py b/src/kernel/types/vaults/agentcard_checkout_preparation.py new file mode 100644 index 00000000..8296b2fd --- /dev/null +++ b/src/kernel/types/vaults/agentcard_checkout_preparation.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AgentcardCheckoutPreparation"] + + +class AgentcardCheckoutPreparation(BaseModel): + """One-use Square checkout preparation. + + Keep the approval page open through token handoff. The amount is display-only and does not constrain the merchant's eventual charge. + """ + + browser_id: str + + created_at: datetime + + environment: Literal["production", "sandbox"] + + merchant_origin: str + + status: Literal["creating", "awaiting_approval", "ready", "consumed", "cancelled", "expired", "unknown"] + """ + Preparation consumed means egress claimed the preparation and it cannot be + reused. It does not mean the attempt settled. Use the enclosing item's status as + the lifecycle indicator; item consumed means the attempt settled, not that an + order or charge succeeded. + """ + + id: Optional[str] = None + + approval_url: Optional[str] = None + + expires_at: Optional[datetime] = None + """ + When ready, the absolute deadline to submit the first native request; no later + than provider readiness expiry or 30 seconds after Kernel first observes + readiness. Polling never extends this deadline. + """ diff --git a/src/kernel/types/vaults/authorize_vault_item_operation_request_param.py b/src/kernel/types/vaults/authorize_vault_item_operation_request_param.py new file mode 100644 index 00000000..10ba11bc --- /dev/null +++ b/src/kernel/types/vaults/authorize_vault_item_operation_request_param.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["AuthorizeVaultItemOperationRequestParam"] + + +class AuthorizeVaultItemOperationRequestParam(TypedDict, total=False): + """Authorize a Link card using its existing purchase specification. + + Use only after explicit user approval and when the item advertises authorize. Do not automatically retry provider failures or indeterminate outcomes. Checkout context is not accepted. + """ + + type: Required[Literal["authorize"]] diff --git a/src/kernel/types/vaults/card_vault_item_state.py b/src/kernel/types/vaults/card_vault_item_state.py index 7bb7e274..98459762 100644 --- a/src/kernel/types/vaults/card_vault_item_state.py +++ b/src/kernel/types/vaults/card_vault_item_state.py @@ -8,6 +8,7 @@ from ..._utils import PropertyInfo from ..._models import BaseModel from .vault_card_aliases import VaultCardAliases +from .agentcard_checkout_preparation import AgentcardCheckoutPreparation from .agentcard_checkout_authorization import AgentcardCheckoutAuthorization __all__ = ["CardVaultItemState", "LinkCardState", "LinkCardStateMasks", "AgentCardCardState", "AgentCardCardStateMasks"] @@ -76,12 +77,28 @@ def __getattr__(self, attr: str) -> str: ... class AgentCardCardState(BaseModel): provider: Literal["agentcard"] - status: Literal["requested", "ready", "pending_approval", "degraded", "recovery_required"] - """recovery_required means the original checkout outcome is unresolved. - - Do not retry, delete, or replace it. Known authorization IDs may be reconciled - through provider observations; otherwise contact the provider or support for - manual reconciliation. It does not mean declined or expired. + status: Literal[ + "requested", + "ready", + "preparing", + "ready_to_submit", + "pending_approval", + "consumed", + "stopped", + "outcome_unknown", + "degraded", + "recovery_required", + ] + """ready_to_submit is device readiness for at most 30 seconds. + + consumed means the prepared attempt has settled, not that an order succeeded. + stopped cannot be reused. outcome_unknown requires merchant reconciliation and + blocks new requests. recovery_required means the original checkout outcome is + unresolved. Automatic reuse is blocked. Known authorization IDs must be + reconciled through provider observations or support. When no authorization ID + was returned, an explicitly confirmed item deletion may abandon the unresolved + attempt so the caller can create a replacement; deletion does not prove that the + original attempt failed. It does not mean declined or expired. """ aliases: Optional[VaultCardAliases] = None @@ -94,6 +111,13 @@ class AgentCardCardState(BaseModel): masks: Optional[AgentCardCardStateMasks] = None + preparation: Optional[AgentcardCheckoutPreparation] = None + """One-use Square checkout preparation. + + Keep the approval page open through token handoff. The amount is display-only + and does not constrain the merchant's eventual charge. + """ + status_reason: Optional[str] = None diff --git a/src/kernel/types/vaults/fill_vault_item_operation_request_param.py b/src/kernel/types/vaults/fill_vault_item_operation_request_param.py new file mode 100644 index 00000000..a4613cd6 --- /dev/null +++ b/src/kernel/types/vaults/fill_vault_item_operation_request_param.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +from .vault_card_fill_field_param import VaultCardFillFieldParam + +__all__ = ["FillVaultItemOperationRequestParam"] + + +class FillVaultItemOperationRequestParam(TypedDict, total=False): + """ + Fill selected fields from one ready, unexpired card into a browser linked + to its vault. Only supported for card items created from Link wallets. + Only invoke when the item advertises `fill`. Browser and vault must belong + to the same project. Kernel checks access and allowed destinations before + filling; providing a page URL does not authorize a destination. + + Find exactly one open page matching `page_url`. For each selector, search + the main frame and all descendant frames for editable inputs or selects + matched directly or contained within matching elements. Each selector must + resolve to one unique editable element across all frames; zero or multiple + candidates fail. Count each element once, even if multiple matching + containers contain it. Validate all bindings before filling. + Select elements match an option by its value, not its label. + If the page navigates or a target disappears during filling, stop rather + than selecting a different page or element. + + Fill in request order and stop on the first failure. This operation is + not atomic: previously filled fields are not rolled back. Never submit + the form or click buttons, though input/change events may trigger site + behavior. Fill is the preferred browser-checkout path. Aliases remain an + alternative for explicitly chosen egress-substitution integrations. Do not + automatically retry or fall back to aliases after a failed or indeterminate + operation. + + Secret values are never returned or included in operation logs, traces, + audit events, or error details. This does not prevent an agent with + unrestricted browser access from reading values from the page or other + browser observation surfaces. + """ + + browser_id: Required[str] + """Browser session ID, not a reusable browser name.""" + + fields: Required[Iterable[VaultCardFillFieldParam]] + """Field bindings for this step. No two bindings may resolve to the same element.""" + + page_url: Required[str] + """Exact current top-level page URL, including path, query, and fragment. + + Must match exactly one open page in the browser; zero or multiple matches fail. + No prefix or glob matching. Must use HTTPS without embedded credentials. + """ + + type: Required[Literal["fill"]] + + timeout_ms: int + """Total operation deadline in milliseconds, not a per-field timeout.""" diff --git a/src/kernel/types/vaults/fill_vault_item_operation_result.py b/src/kernel/types/vaults/fill_vault_item_operation_result.py new file mode 100644 index 00000000..0f3f1855 --- /dev/null +++ b/src/kernel/types/vaults/fill_vault_item_operation_result.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import Literal + +from ..._models import BaseModel +from .vault_fill_field_result import VaultFillFieldResult + +__all__ = ["FillVaultItemOperationResult"] + + +class FillVaultItemOperationResult(BaseModel): + fields: List[VaultFillFieldResult] + """Exactly one result per request binding, in request order. + + After the first failed or unknown field, all remaining fields are not_attempted. + """ + + status: Literal["completed", "failed", "unknown"] + """Completed only when all fields were filled. + + Failed when execution stopped with known outcomes. Unknown when any field's + outcome cannot be determined. None of these statuses confirms payment or + merchant acceptance. + """ + + type: Literal["fill"] diff --git a/src/kernel/types/vaults/item_perform_operation_params.py b/src/kernel/types/vaults/item_perform_operation_params.py index 9d62f380..c583f53b 100644 --- a/src/kernel/types/vaults/item_perform_operation_params.py +++ b/src/kernel/types/vaults/item_perform_operation_params.py @@ -2,12 +2,63 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing import Union, Iterable +from typing_extensions import Literal, Required, TypeAlias, TypedDict -__all__ = ["ItemPerformOperationParams"] +from .vault_card_fill_field_param import VaultCardFillFieldParam +from .vault_checkout_context_param import VaultCheckoutContextParam +__all__ = [ + "ItemPerformOperationParams", + "AuthorizeVaultItemOperationRequest", + "PrepareCheckoutVaultItemOperationRequest", + "FillVaultItemOperationRequest", +] -class ItemPerformOperationParams(TypedDict, total=False): + +class AuthorizeVaultItemOperationRequest(TypedDict, total=False): id_or_name: Required[str] type: Required[Literal["authorize"]] + + +class PrepareCheckoutVaultItemOperationRequest(TypedDict, total=False): + id_or_name: Required[str] + + checkout: Required[VaultCheckoutContextParam] + """Required when preparing an unused AgentCard card for Square. + + Consent is bound to this browser and declared merchant origin, not a tab. Wait + for the item's ready_to_submit status before native Pay and submit within its + readiness deadline. Unused preparations expire automatically; every preparation + is single-use, including after failure or expiry. + """ + + type: Required[Literal["prepare_checkout"]] + + +class FillVaultItemOperationRequest(TypedDict, total=False): + id_or_name: Required[str] + + browser_id: Required[str] + """Browser session ID, not a reusable browser name.""" + + fields: Required[Iterable[VaultCardFillFieldParam]] + """Field bindings for this step. No two bindings may resolve to the same element.""" + + page_url: Required[str] + """Exact current top-level page URL, including path, query, and fragment. + + Must match exactly one open page in the browser; zero or multiple matches fail. + No prefix or glob matching. Must use HTTPS without embedded credentials. + """ + + type: Required[Literal["fill"]] + + timeout_ms: int + """Total operation deadline in milliseconds, not a per-field timeout.""" + + +ItemPerformOperationParams: TypeAlias = Union[ + AuthorizeVaultItemOperationRequest, PrepareCheckoutVaultItemOperationRequest, FillVaultItemOperationRequest +] diff --git a/src/kernel/types/vaults/prepare_checkout_vault_item_operation_request_param.py b/src/kernel/types/vaults/prepare_checkout_vault_item_operation_request_param.py new file mode 100644 index 00000000..9900c513 --- /dev/null +++ b/src/kernel/types/vaults/prepare_checkout_vault_item_operation_request_param.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +from .vault_checkout_context_param import VaultCheckoutContextParam + +__all__ = ["PrepareCheckoutVaultItemOperationRequestParam"] + + +class PrepareCheckoutVaultItemOperationRequestParam(TypedDict, total=False): + """Prepare an unused AgentCard card for Square checkout. + + Deliver the returned approval URL and keep the approval page open. Poll the item until ready_to_submit, then submit native Pay before preparation.expires_at. Readiness lasts at most 30 seconds. Unused preparations expire automatically. Preparations are single-use even after failure or expiry; do not automatically retry and reconcile uncertain outcomes with the merchant. + """ + + checkout: Required[VaultCheckoutContextParam] + """Required when preparing an unused AgentCard card for Square. + + Consent is bound to this browser and declared merchant origin, not a tab. Wait + for the item's ready_to_submit status before native Pay and submit within its + readiness deadline. Unused preparations expire automatically; every preparation + is single-use, including after failure or expiry. + """ + + type: Required[Literal["prepare_checkout"]] diff --git a/src/kernel/types/vaults/vault_card_fill_field_param.py b/src/kernel/types/vaults/vault_card_fill_field_param.py new file mode 100644 index 00000000..e286291e --- /dev/null +++ b/src/kernel/types/vaults/vault_card_fill_field_param.py @@ -0,0 +1,60 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict + +__all__ = ["VaultCardFillFieldParam", "VaultCardStoredFillField", "VaultCardExpirationFillField"] + + +class VaultCardStoredFillField(TypedDict, total=False): + field: Required[ + Literal[ + "number", + "exp_month", + "exp_year", + "cvc", + "billing_name", + "billing_line1", + "billing_line2", + "billing_city", + "billing_state", + "billing_postal_code", + "billing_country", + ] + ] + """Field in the decrypted card, not an alias. + + Number and CVC preserve leading zeros; month uses two digits and year uses four + digits. Billing fields use the provider's stored billing address (name, line1, + line2, city, state, postal_code, country) without reformatting. Request only + needed billing fields. An absent or empty requested billing field returns 400 + field_unavailable before any browser writes; it does not make other card fields + unavailable. + """ + + selector: Required[str] + """CSS selector for an editable input or select, or a containing element. + + Must resolve to one unique editable element across all page frames. + """ + + +class VaultCardExpirationFillField(TypedDict, total=False): + """ + Combined expiration derived from the stored month and year; not a separate stored secret. + """ + + field: Required[Literal["expiration"]] + + format: Required[Literal["MM/YY", "MM/YYYY"]] + + selector: Required[str] + """CSS selector for an editable input or select, or a containing element. + + Must resolve to one unique editable element across all page frames. + """ + + +VaultCardFillFieldParam: TypeAlias = Union[VaultCardStoredFillField, VaultCardExpirationFillField] diff --git a/src/kernel/types/vaults/vault_checkout_context_param.py b/src/kernel/types/vaults/vault_checkout_context_param.py new file mode 100644 index 00000000..8b08c40c --- /dev/null +++ b/src/kernel/types/vaults/vault_checkout_context_param.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["VaultCheckoutContextParam"] + + +class VaultCheckoutContextParam(TypedDict, total=False): + """Required when preparing an unused AgentCard card for Square. + + Consent is bound to this browser and declared merchant origin, not a tab. Wait for the item's ready_to_submit status before native Pay and submit within its readiness deadline. Unused preparations expire automatically; every preparation is single-use, including after failure or expiry. + """ + + browser_id: Required[str] + """Active browser session with this vault bound to it.""" + + environment: Required[Literal["production", "sandbox"]] + """Square environment, independent of the AgentCard credential mode.""" + + merchant_origin: Required[str] + """Canonical HTTPS origin of the top-level merchant document, not the Square + iframe. + + HTTP localhost is accepted for tests. + """ diff --git a/src/kernel/types/vaults/vault_fill_field_result.py b/src/kernel/types/vaults/vault_fill_field_result.py new file mode 100644 index 00000000..757b4d43 --- /dev/null +++ b/src/kernel/types/vaults/vault_fill_field_result.py @@ -0,0 +1,35 @@ +# 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__ = ["VaultFillFieldResult"] + + +class VaultFillFieldResult(BaseModel): + index: int + """Zero-based index into the request fields array.""" + + status: Literal["filled", "failed", "not_attempted", "unknown"] + """ + Filled means the fill action completed, not that the website retained or + accepted the value. + """ + + error_code: Optional[ + Literal[ + "target_changed", + "element_not_found", + "ambiguous_selector", + "element_not_editable", + "option_not_found", + "timeout", + "execution_failed", + ] + ] = None + """Present only for failed or unknown fields. + + Never includes secret values, DOM content, or raw browser errors. + """ diff --git a/src/kernel/types/vaults/vault_item.py b/src/kernel/types/vaults/vault_item.py index 2cc7ab26..e33e12db 100644 --- a/src/kernel/types/vaults/vault_item.py +++ b/src/kernel/types/vaults/vault_item.py @@ -43,7 +43,7 @@ class WalletVaultItemAvailableOperation(BaseModel): description: str - type: Literal["authorize"] + type: Literal["authorize", "prepare_checkout", "fill"] class WalletVaultItemExpanded(BaseModel): @@ -107,7 +107,7 @@ class CardVaultItemAvailableOperation(BaseModel): description: str - type: Literal["authorize"] + type: Literal["authorize", "prepare_checkout", "fill"] class CardVaultItem(BaseModel): diff --git a/src/kernel/types/vaults/vault_item_operation_response.py b/src/kernel/types/vaults/vault_item_operation_response.py new file mode 100644 index 00000000..a464a6b8 --- /dev/null +++ b/src/kernel/types/vaults/vault_item_operation_response.py @@ -0,0 +1,139 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Union, Optional +from datetime import datetime +from typing_extensions import Literal, TypeAlias + +from ..._models import BaseModel +from .vault_item_action import VaultItemAction +from .card_vault_item_spec import CardVaultItemSpec +from .vault_payment_method import VaultPaymentMethod +from .card_vault_item_state import CardVaultItemState +from .wallet_vault_item_spec import WalletVaultItemSpec +from .wallet_vault_item_state import WalletVaultItemState +from .fill_vault_item_operation_result import FillVaultItemOperationResult + +__all__ = [ + "VaultItemOperationResponse", + "WalletVaultItem", + "WalletVaultItemAvailableExpansion", + "WalletVaultItemAvailableOperation", + "WalletVaultItemExpanded", + "CardVaultItem", + "CardVaultItemAvailableExpansion", + "CardVaultItemAvailableOperation", +] + + +class WalletVaultItemAvailableExpansion(BaseModel): + """ + Live data that can currently be requested by passing its type to the item GET expand parameter. + """ + + description: str + + type: Literal["payment_methods"] + + +class WalletVaultItemAvailableOperation(BaseModel): + """An operation that is currently valid for this item. + + Read the description before invoking it through the item operations endpoint. + """ + + description: str + + type: Literal["authorize", "prepare_checkout", "fill"] + + +class WalletVaultItemExpanded(BaseModel): + """Live, non-persisted data requested through the item GET expand parameter.""" + + payment_methods: Optional[List[VaultPaymentMethod]] = None + + +class WalletVaultItem(BaseModel): + id: str + + available_expansions: List[WalletVaultItemAvailableExpansion] + + available_operations: List[WalletVaultItemAvailableOperation] + + created_at: datetime + + key: str + """Immutable item key assigned when the item is created.""" + + spec: WalletVaultItemSpec + """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. + """ + + state: WalletVaultItemState + + type: Literal["wallet"] + + updated_at: datetime + + action: Optional[VaultItemAction] = None + + expanded: Optional[WalletVaultItemExpanded] = None + """Live, non-persisted data requested through the item GET expand parameter.""" + + expires_at: Optional[datetime] = None + + +class CardVaultItemAvailableExpansion(BaseModel): + """ + Live data that can currently be requested by passing its type to the item GET expand parameter. + """ + + description: str + + type: Literal["payment_methods"] + + +class CardVaultItemAvailableOperation(BaseModel): + """An operation that is currently valid for this item. + + Read the description before invoking it through the item operations endpoint. + """ + + description: str + + type: Literal["authorize", "prepare_checkout", "fill"] + + +class CardVaultItem(BaseModel): + id: str + + available_expansions: List[CardVaultItemAvailableExpansion] + + available_operations: List[CardVaultItemAvailableOperation] + + created_at: datetime + + key: str + """Immutable item key assigned when the item is created.""" + + spec: CardVaultItemSpec + """Live payment card. Test-mode card creation is not supported.""" + + state: CardVaultItemState + + type: Literal["card"] + + updated_at: datetime + + action: Optional[VaultItemAction] = None + + expires_at: Optional[datetime] = None + + +VaultItemOperationResponse: TypeAlias = Union[WalletVaultItem, CardVaultItem, FillVaultItemOperationResult] diff --git a/tests/api_resources/auth/test_connections.py b/tests/api_resources/auth/test_connections.py index 4dff13ee..53a55fa7 100644 --- a/tests/api_resources/auth/test_connections.py +++ b/tests/api_resources/auth/test_connections.py @@ -46,6 +46,7 @@ def test_method_create_with_all_params(self, client: Kernel) -> None: "mode": "direct", "name": "x", }, + "region": "us-east", "stealth": False, "telemetry": { "browser": { @@ -211,6 +212,7 @@ def test_method_update_with_all_params(self, client: Kernel) -> None: "mode": "direct", "name": "x", }, + "region": "us-east", "stealth": False, "telemetry": { "browser": { @@ -461,6 +463,7 @@ def test_method_login_with_all_params(self, client: Kernel) -> None: "mode": "direct", "name": "x", }, + "region": "us-east", "stealth": False, "telemetry": { "browser": { @@ -705,6 +708,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncKernel) -> "mode": "direct", "name": "x", }, + "region": "us-east", "stealth": False, "telemetry": { "browser": { @@ -870,6 +874,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncKernel) -> "mode": "direct", "name": "x", }, + "region": "us-east", "stealth": False, "telemetry": { "browser": { @@ -1120,6 +1125,7 @@ async def test_method_login_with_all_params(self, async_client: AsyncKernel) -> "mode": "direct", "name": "x", }, + "region": "us-east", "stealth": False, "telemetry": { "browser": { diff --git a/tests/api_resources/test_config_registry.py b/tests/api_resources/test_config_registry.py index 710d3088..18a3d598 100644 --- a/tests/api_resources/test_config_registry.py +++ b/tests/api_resources/test_config_registry.py @@ -119,6 +119,7 @@ def test_method_resolve_with_all_params(self, client: Kernel) -> None: config_registry = client.config_registry.resolve( url="https://example.com", allowed_proxy_countries=["US"], + intent="search for a black hoodie and add it to the cart", ) assert_matches_type(ConfigRegistryResponse, config_registry, path=["response"]) @@ -251,6 +252,7 @@ async def test_method_resolve_with_all_params(self, async_client: AsyncKernel) - config_registry = await async_client.config_registry.resolve( url="https://example.com", allowed_proxy_countries=["US"], + intent="search for a black hoodie and add it to the cart", ) assert_matches_type(ConfigRegistryResponse, config_registry, path=["response"]) diff --git a/tests/api_resources/vaults/test_items.py b/tests/api_resources/vaults/test_items.py index 9747f919..f9dde10b 100644 --- a/tests/api_resources/vaults/test_items.py +++ b/tests/api_resources/vaults/test_items.py @@ -13,6 +13,7 @@ VaultItem, ItemListResponse, ItemEventsResponse, + VaultItemOperationResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -392,17 +393,17 @@ def test_path_params_events(self, client: Kernel) -> None: @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_method_perform_operation(self, client: Kernel) -> None: + def test_method_perform_operation_overload_1(self, client: Kernel) -> None: item = client.vaults.items.perform_operation( key="key", id_or_name="id_or_name", type="authorize", ) - assert_matches_type(VaultItem, item, path=["response"]) + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_raw_response_perform_operation(self, client: Kernel) -> None: + def test_raw_response_perform_operation_overload_1(self, client: Kernel) -> None: response = client.vaults.items.with_raw_response.perform_operation( key="key", id_or_name="id_or_name", @@ -412,11 +413,11 @@ def test_raw_response_perform_operation(self, client: Kernel) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" item = response.parse() - assert_matches_type(VaultItem, item, path=["response"]) + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_streaming_response_perform_operation(self, client: Kernel) -> None: + def test_streaming_response_perform_operation_overload_1(self, client: Kernel) -> None: with client.vaults.items.with_streaming_response.perform_operation( key="key", id_or_name="id_or_name", @@ -426,13 +427,13 @@ def test_streaming_response_perform_operation(self, client: Kernel) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" item = response.parse() - assert_matches_type(VaultItem, item, path=["response"]) + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_path_params_perform_operation(self, client: Kernel) -> None: + def test_path_params_perform_operation_overload_1(self, client: Kernel) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"): client.vaults.items.with_raw_response.perform_operation( key="key", @@ -447,6 +448,276 @@ def test_path_params_perform_operation(self, client: Kernel) -> None: type="authorize", ) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_perform_operation_overload_2(self, client: Kernel) -> None: + item = client.vaults.items.perform_operation( + key="key", + id_or_name="id_or_name", + checkout={ + "browser_id": "browser_id", + "environment": "production", + "merchant_origin": "merchant_origin", + }, + type="prepare_checkout", + ) + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_perform_operation_overload_2(self, client: Kernel) -> None: + response = client.vaults.items.with_raw_response.perform_operation( + key="key", + id_or_name="id_or_name", + checkout={ + "browser_id": "browser_id", + "environment": "production", + "merchant_origin": "merchant_origin", + }, + type="prepare_checkout", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + item = response.parse() + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_perform_operation_overload_2(self, client: Kernel) -> None: + with client.vaults.items.with_streaming_response.perform_operation( + key="key", + id_or_name="id_or_name", + checkout={ + "browser_id": "browser_id", + "environment": "production", + "merchant_origin": "merchant_origin", + }, + type="prepare_checkout", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + item = response.parse() + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_perform_operation_overload_2(self, client: Kernel) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"): + client.vaults.items.with_raw_response.perform_operation( + key="key", + id_or_name="", + checkout={ + "browser_id": "browser_id", + "environment": "production", + "merchant_origin": "merchant_origin", + }, + type="prepare_checkout", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `key` but received ''"): + client.vaults.items.with_raw_response.perform_operation( + key="", + id_or_name="id_or_name", + checkout={ + "browser_id": "browser_id", + "environment": "production", + "merchant_origin": "merchant_origin", + }, + type="prepare_checkout", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_perform_operation_overload_3(self, client: Kernel) -> None: + item = client.vaults.items.perform_operation( + key="key", + id_or_name="id_or_name", + browser_id="browser-session-id", + fields=[ + { + "field": "number", + "selector": "#card-number", + }, + { + "field": "exp_month", + "selector": "#expiry-month", + }, + { + "field": "exp_year", + "selector": "#expiry-year", + }, + { + "field": "cvc", + "selector": "#security-code", + }, + ], + page_url="https://shop.example/checkout", + type="fill", + ) + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_perform_operation_with_all_params_overload_3(self, client: Kernel) -> None: + item = client.vaults.items.perform_operation( + key="key", + id_or_name="id_or_name", + browser_id="browser-session-id", + fields=[ + { + "field": "number", + "selector": "#card-number", + }, + { + "field": "exp_month", + "selector": "#expiry-month", + }, + { + "field": "exp_year", + "selector": "#expiry-year", + }, + { + "field": "cvc", + "selector": "#security-code", + }, + ], + page_url="https://shop.example/checkout", + type="fill", + timeout_ms=1, + ) + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_perform_operation_overload_3(self, client: Kernel) -> None: + response = client.vaults.items.with_raw_response.perform_operation( + key="key", + id_or_name="id_or_name", + browser_id="browser-session-id", + fields=[ + { + "field": "number", + "selector": "#card-number", + }, + { + "field": "exp_month", + "selector": "#expiry-month", + }, + { + "field": "exp_year", + "selector": "#expiry-year", + }, + { + "field": "cvc", + "selector": "#security-code", + }, + ], + page_url="https://shop.example/checkout", + type="fill", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + item = response.parse() + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_perform_operation_overload_3(self, client: Kernel) -> None: + with client.vaults.items.with_streaming_response.perform_operation( + key="key", + id_or_name="id_or_name", + browser_id="browser-session-id", + fields=[ + { + "field": "number", + "selector": "#card-number", + }, + { + "field": "exp_month", + "selector": "#expiry-month", + }, + { + "field": "exp_year", + "selector": "#expiry-year", + }, + { + "field": "cvc", + "selector": "#security-code", + }, + ], + page_url="https://shop.example/checkout", + type="fill", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + item = response.parse() + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_perform_operation_overload_3(self, client: Kernel) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"): + client.vaults.items.with_raw_response.perform_operation( + key="key", + id_or_name="", + browser_id="browser-session-id", + fields=[ + { + "field": "number", + "selector": "#card-number", + }, + { + "field": "exp_month", + "selector": "#expiry-month", + }, + { + "field": "exp_year", + "selector": "#expiry-year", + }, + { + "field": "cvc", + "selector": "#security-code", + }, + ], + page_url="https://shop.example/checkout", + type="fill", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `key` but received ''"): + client.vaults.items.with_raw_response.perform_operation( + key="", + id_or_name="id_or_name", + browser_id="browser-session-id", + fields=[ + { + "field": "number", + "selector": "#card-number", + }, + { + "field": "exp_month", + "selector": "#expiry-month", + }, + { + "field": "exp_year", + "selector": "#expiry-year", + }, + { + "field": "cvc", + "selector": "#security-code", + }, + ], + page_url="https://shop.example/checkout", + type="fill", + ) + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_upsert_overload_1(self, client: Kernel) -> None: @@ -1088,17 +1359,17 @@ async def test_path_params_events(self, async_client: AsyncKernel) -> None: @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_method_perform_operation(self, async_client: AsyncKernel) -> None: + async def test_method_perform_operation_overload_1(self, async_client: AsyncKernel) -> None: item = await async_client.vaults.items.perform_operation( key="key", id_or_name="id_or_name", type="authorize", ) - assert_matches_type(VaultItem, item, path=["response"]) + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_raw_response_perform_operation(self, async_client: AsyncKernel) -> None: + async def test_raw_response_perform_operation_overload_1(self, async_client: AsyncKernel) -> None: response = await async_client.vaults.items.with_raw_response.perform_operation( key="key", id_or_name="id_or_name", @@ -1108,11 +1379,11 @@ async def test_raw_response_perform_operation(self, async_client: AsyncKernel) - assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" item = await response.parse() - assert_matches_type(VaultItem, item, path=["response"]) + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_streaming_response_perform_operation(self, async_client: AsyncKernel) -> None: + async def test_streaming_response_perform_operation_overload_1(self, async_client: AsyncKernel) -> None: async with async_client.vaults.items.with_streaming_response.perform_operation( key="key", id_or_name="id_or_name", @@ -1122,13 +1393,13 @@ async def test_streaming_response_perform_operation(self, async_client: AsyncKer assert response.http_request.headers.get("X-Stainless-Lang") == "python" item = await response.parse() - assert_matches_type(VaultItem, item, path=["response"]) + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_path_params_perform_operation(self, async_client: AsyncKernel) -> None: + async def test_path_params_perform_operation_overload_1(self, async_client: AsyncKernel) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"): await async_client.vaults.items.with_raw_response.perform_operation( key="key", @@ -1143,6 +1414,276 @@ async def test_path_params_perform_operation(self, async_client: AsyncKernel) -> type="authorize", ) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_perform_operation_overload_2(self, async_client: AsyncKernel) -> None: + item = await async_client.vaults.items.perform_operation( + key="key", + id_or_name="id_or_name", + checkout={ + "browser_id": "browser_id", + "environment": "production", + "merchant_origin": "merchant_origin", + }, + type="prepare_checkout", + ) + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_perform_operation_overload_2(self, async_client: AsyncKernel) -> None: + response = await async_client.vaults.items.with_raw_response.perform_operation( + key="key", + id_or_name="id_or_name", + checkout={ + "browser_id": "browser_id", + "environment": "production", + "merchant_origin": "merchant_origin", + }, + type="prepare_checkout", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + item = await response.parse() + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_perform_operation_overload_2(self, async_client: AsyncKernel) -> None: + async with async_client.vaults.items.with_streaming_response.perform_operation( + key="key", + id_or_name="id_or_name", + checkout={ + "browser_id": "browser_id", + "environment": "production", + "merchant_origin": "merchant_origin", + }, + type="prepare_checkout", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + item = await response.parse() + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_perform_operation_overload_2(self, async_client: AsyncKernel) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"): + await async_client.vaults.items.with_raw_response.perform_operation( + key="key", + id_or_name="", + checkout={ + "browser_id": "browser_id", + "environment": "production", + "merchant_origin": "merchant_origin", + }, + type="prepare_checkout", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `key` but received ''"): + await async_client.vaults.items.with_raw_response.perform_operation( + key="", + id_or_name="id_or_name", + checkout={ + "browser_id": "browser_id", + "environment": "production", + "merchant_origin": "merchant_origin", + }, + type="prepare_checkout", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_perform_operation_overload_3(self, async_client: AsyncKernel) -> None: + item = await async_client.vaults.items.perform_operation( + key="key", + id_or_name="id_or_name", + browser_id="browser-session-id", + fields=[ + { + "field": "number", + "selector": "#card-number", + }, + { + "field": "exp_month", + "selector": "#expiry-month", + }, + { + "field": "exp_year", + "selector": "#expiry-year", + }, + { + "field": "cvc", + "selector": "#security-code", + }, + ], + page_url="https://shop.example/checkout", + type="fill", + ) + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_perform_operation_with_all_params_overload_3(self, async_client: AsyncKernel) -> None: + item = await async_client.vaults.items.perform_operation( + key="key", + id_or_name="id_or_name", + browser_id="browser-session-id", + fields=[ + { + "field": "number", + "selector": "#card-number", + }, + { + "field": "exp_month", + "selector": "#expiry-month", + }, + { + "field": "exp_year", + "selector": "#expiry-year", + }, + { + "field": "cvc", + "selector": "#security-code", + }, + ], + page_url="https://shop.example/checkout", + type="fill", + timeout_ms=1, + ) + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_perform_operation_overload_3(self, async_client: AsyncKernel) -> None: + response = await async_client.vaults.items.with_raw_response.perform_operation( + key="key", + id_or_name="id_or_name", + browser_id="browser-session-id", + fields=[ + { + "field": "number", + "selector": "#card-number", + }, + { + "field": "exp_month", + "selector": "#expiry-month", + }, + { + "field": "exp_year", + "selector": "#expiry-year", + }, + { + "field": "cvc", + "selector": "#security-code", + }, + ], + page_url="https://shop.example/checkout", + type="fill", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + item = await response.parse() + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_perform_operation_overload_3(self, async_client: AsyncKernel) -> None: + async with async_client.vaults.items.with_streaming_response.perform_operation( + key="key", + id_or_name="id_or_name", + browser_id="browser-session-id", + fields=[ + { + "field": "number", + "selector": "#card-number", + }, + { + "field": "exp_month", + "selector": "#expiry-month", + }, + { + "field": "exp_year", + "selector": "#expiry-year", + }, + { + "field": "cvc", + "selector": "#security-code", + }, + ], + page_url="https://shop.example/checkout", + type="fill", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + item = await response.parse() + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_perform_operation_overload_3(self, async_client: AsyncKernel) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id_or_name` but received ''"): + await async_client.vaults.items.with_raw_response.perform_operation( + key="key", + id_or_name="", + browser_id="browser-session-id", + fields=[ + { + "field": "number", + "selector": "#card-number", + }, + { + "field": "exp_month", + "selector": "#expiry-month", + }, + { + "field": "exp_year", + "selector": "#expiry-year", + }, + { + "field": "cvc", + "selector": "#security-code", + }, + ], + page_url="https://shop.example/checkout", + type="fill", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `key` but received ''"): + await async_client.vaults.items.with_raw_response.perform_operation( + key="", + id_or_name="id_or_name", + browser_id="browser-session-id", + fields=[ + { + "field": "number", + "selector": "#card-number", + }, + { + "field": "exp_month", + "selector": "#expiry-month", + }, + { + "field": "exp_year", + "selector": "#expiry-year", + }, + { + "field": "cvc", + "selector": "#security-code", + }, + ], + page_url="https://shop.example/checkout", + type="fill", + ) + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_method_upsert_overload_1(self, async_client: AsyncKernel) -> None: