From 4ce4ab9f533b4f5d9fe420f91f1a0054c0e13c1b Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 23:46:01 +0000 Subject: [PATCH 1/4] feat: chore(stlc): seal custom-code tracking files Stainless-Generated-From: c38e2308cd507f413c42d0d729d082ccd535debe --- api.md | 15 + src/kernel/resources/vaults/items.py | 482 ++++++++++-- src/kernel/types/vaults/__init__.py | 24 +- ...lect_vault_item_operation_request_param.py | 27 + .../vaults/credential_collection_action.py | 50 ++ .../credential_vault_field_definition.py | 31 + .../credential_vault_field_input_param.py | 41 ++ .../vaults/credential_vault_field_state.py | 18 + .../vaults/credential_vault_field_type.py | 7 + .../credential_vault_field_update_param.py | 20 + .../types/vaults/credential_vault_item.py | 93 +++ .../credential_vault_item_request_param.py | 36 + .../vaults/credential_vault_item_spec.py | 19 + .../credential_vault_item_spec_input_param.py | 27 + ...credential_vault_item_spec_update_param.py | 21 + .../vaults/credential_vault_item_state.py | 20 + ...dential_vault_item_update_request_param.py | 38 + ...fill_vault_item_operation_request_param.py | 23 +- .../vaults/item_perform_operation_params.py | 26 +- .../types/vaults/item_retrieve_params.py | 6 +- src/kernel/types/vaults/item_update_params.py | 32 +- src/kernel/types/vaults/item_upsert_params.py | 17 +- .../vaults/vault_card_fill_field_param.py | 60 -- .../types/vaults/vault_fill_field_param.py | 23 + src/kernel/types/vaults/vault_item.py | 9 +- .../vaults/vault_item_operation_response.py | 9 +- tests/api_resources/vaults/test_items.py | 690 ++++++++++++++---- 27 files changed, 1598 insertions(+), 266 deletions(-) create mode 100644 src/kernel/types/vaults/collect_vault_item_operation_request_param.py create mode 100644 src/kernel/types/vaults/credential_collection_action.py create mode 100644 src/kernel/types/vaults/credential_vault_field_definition.py create mode 100644 src/kernel/types/vaults/credential_vault_field_input_param.py create mode 100644 src/kernel/types/vaults/credential_vault_field_state.py create mode 100644 src/kernel/types/vaults/credential_vault_field_type.py create mode 100644 src/kernel/types/vaults/credential_vault_field_update_param.py create mode 100644 src/kernel/types/vaults/credential_vault_item.py create mode 100644 src/kernel/types/vaults/credential_vault_item_request_param.py create mode 100644 src/kernel/types/vaults/credential_vault_item_spec.py create mode 100644 src/kernel/types/vaults/credential_vault_item_spec_input_param.py create mode 100644 src/kernel/types/vaults/credential_vault_item_spec_update_param.py create mode 100644 src/kernel/types/vaults/credential_vault_item_state.py create mode 100644 src/kernel/types/vaults/credential_vault_item_update_request_param.py delete mode 100644 src/kernel/types/vaults/vault_card_fill_field_param.py create mode 100644 src/kernel/types/vaults/vault_fill_field_param.py diff --git a/api.md b/api.md index 1c9b02a3..fdb9696b 100644 --- a/api.md +++ b/api.md @@ -529,12 +529,27 @@ from kernel.types.vaults import ( AuthorizeVaultItemOperationRequest, CardVaultItemSpec, CardVaultItemState, + CollectVaultItemOperationRequest, + CredentialCollectionAction, + CredentialVaultFieldDefinition, + CredentialVaultFieldInput, + CredentialVaultFieldState, + CredentialVaultFieldType, + CredentialVaultFieldUpdate, + CredentialVaultItem, + CredentialVaultItemRequest, + CredentialVaultItemSpec, + CredentialVaultItemSpecInput, + CredentialVaultItemSpecUpdate, + CredentialVaultItemState, + CredentialVaultItemUpdateRequest, FillVaultItemOperationRequest, FillVaultItemOperationResult, PrepareCheckoutVaultItemOperationRequest, VaultCardAliases, VaultCardFillField, VaultCheckoutContext, + VaultFillField, VaultFillFieldResult, VaultItem, VaultItemAction, diff --git a/src/kernel/resources/vaults/items.py b/src/kernel/resources/vaults/items.py index 02315e2b..507cf048 100644 --- a/src/kernel/resources/vaults/items.py +++ b/src/kernel/resources/vaults/items.py @@ -28,10 +28,12 @@ from ...types.vaults.vault_item import VaultItem from ...types.vaults.item_list_response import ItemListResponse from ...types.vaults.item_events_response import ItemEventsResponse +from ...types.vaults.vault_fill_field_param import VaultFillFieldParam 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 +from ...types.vaults.credential_vault_item_spec_input_param import CredentialVaultItemSpecInputParam +from ...types.vaults.credential_vault_item_spec_update_param import CredentialVaultItemSpecUpdateParam __all__ = ["ItemsResource", "AsyncItemsResource"] @@ -75,13 +77,19 @@ def retrieve( and live data that can be requested through `expand`. Read each operation's description before using it. Expanded data is fetched from the provider and is not persisted in the vault item. Requesting an unavailable expansion returns 409 - instead of a partial item. + instead of a partial item. Pending credential items return a collection action. + Kernel-hosted active collection links are renewed atomically on expiry for ready + or pending items without changing the item version. Invoke collect to open a + form for a ready item without clearing values. Sensitive credential values are + never returned. Args: expand: Live fields advertised by `available_expansions` to include in `expanded`. - wait: Hold for up to this many seconds while the item is pending authorization or - approval. + wait: Hold for up to this many seconds while the item is pending authorization, + approval, or credential collection. Return the current item when ready or when + the wait elapses. This does not wait for edits to an already-ready credential; + poll GET without wait and compare version to observe changes after collect. extra_headers: Send extra headers @@ -116,12 +124,14 @@ def retrieve( ), ) + @overload def update( self, key: str, *, id_or_name: str, spec: CardVaultItemSpecParam, + type: Literal["card"] | 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, @@ -129,15 +139,19 @@ def update( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VaultItem: - """Requested cards accept a replacement specification. - - Pending issuance requests - may update provider-supported fields on their existing request, subject to - atomic provider approval checks; omitted optional fields remain unchanged and - explicit empty lists clear them. Wallet/provider binding and unsupported fields - cannot change after authorization starts. An uncertain update enters - recovery_required and must not be retried. Checkout cards may be edited between - authorizations. + """ + Credential updates require type credential and the current version, and change + only values or description; omitted values are preserved, nonempty strings + replace, and null or empty strings clear supported fields. Clearing required + text/email/password values returns pending_collection; browser forms still + require nonempty required inputs. Card updates may omit type for compatibility + with legacy requests. Requested cards accept a replacement specification. + Pending issuance requests may update provider-supported fields on their existing + request, subject to atomic provider approval checks; omitted optional fields + remain unchanged and explicit empty lists clear them. Wallet/provider binding + and unsupported fields cannot change after authorization starts. An uncertain + update enters recovery_required and must not be retried. Checkout cards may be + edited between authorizations. Args: spec: Live payment card. Test-mode card creation is not supported. @@ -150,6 +164,74 @@ def update( timeout: Override the client-level default timeout for this request, in seconds """ + ... + + @overload + def update( + self, + key: str, + *, + id_or_name: str, + spec: CredentialVaultItemSpecUpdateParam, + type: Literal["credential"], + version: int, + expected_item_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VaultItem: + """ + Credential updates require type credential and the current version, and change + only values or description; omitted values are preserved, nonempty strings + replace, and null or empty strings clear supported fields. Clearing required + text/email/password values returns pending_collection; browser forms still + require nonempty required inputs. Card updates may omit type for compatibility + with legacy requests. Requested cards accept a replacement specification. + Pending issuance requests may update provider-supported fields on their existing + request, subject to atomic provider approval checks; omitted optional fields + remain unchanged and explicit empty lists clear them. Wallet/provider binding + and unsupported fields cannot change after authorization starts. An uncertain + update enters recovery_required and must not be retried. Checkout cards may be + edited between authorizations. + + Args: + version: Expected current item version from the latest read. + + expected_item_id: Optional immutable item ID precondition. Returns 409 if the key now identifies a + different item. Accepted writes target this immutable ID, preventing + replacement-key races. Supply this when submitting a form bound to a previously + read item. + + 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", "spec"], ["id_or_name", "spec", "type", "version"]) + def update( + self, + key: str, + *, + id_or_name: str, + spec: CardVaultItemSpecParam | CredentialVaultItemSpecUpdateParam, + type: Literal["card"] | Literal["credential"] | Omit = omit, + version: int | Omit = omit, + expected_item_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VaultItem: 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: @@ -158,7 +240,15 @@ def update( VaultItem, self._patch( path_template("/vaults/{id_or_name}/items/{key}", id_or_name=id_or_name, key=key), - body=maybe_transform({"spec": spec}, item_update_params.ItemUpdateParams), + body=maybe_transform( + { + "spec": spec, + "type": type, + "version": version, + "expected_item_id": expected_item_id, + }, + item_update_params.ItemUpdateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -177,8 +267,11 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ItemListResponse: - """ - List vault items without secret values + """Credential entries include safe field metadata and non-sensitive values. + + Listing + never creates or renews collection sessions; only an existing unexpired active + session is included. Use single-item GET or collect to obtain a fresh link. Args: extra_headers: Send extra headers @@ -337,6 +430,51 @@ def perform_operation( """ ... + @overload + def perform_operation( + self, + key: str, + *, + id_or_name: str, + type: Literal["collect"], + # 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: + 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, @@ -396,9 +534,9 @@ def perform_operation( *, id_or_name: str, browser_id: str, - fields: Iterable[VaultCardFillFieldParam], - page_url: str, + fields: Iterable[VaultFillFieldParam], type: Literal["fill"], + 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. @@ -434,7 +572,9 @@ def perform_operation( 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. + prefix or glob matching. Required for cards, which must use HTTPS without + embedded credentials. Optional for credentials, where omission requires exactly + one open page. timeout_ms: Total operation deadline in milliseconds, not a per-field timeout. @@ -449,19 +589,17 @@ def perform_operation( ... @required_args( - ["id_or_name", "type"], - ["id_or_name", "checkout", "type"], - ["id_or_name", "browser_id", "fields", "page_url", "type"], + ["id_or_name", "type"], ["id_or_name", "checkout", "type"], ["id_or_name", "browser_id", "fields", "type"] ) def perform_operation( self, key: str, *, id_or_name: str, - type: Literal["authorize"] | Literal["prepare_checkout"] | Literal["fill"], + type: Literal["authorize"] | Literal["collect"] | Literal["prepare_checkout"] | Literal["fill"], checkout: VaultCheckoutContextParam | Omit = omit, browser_id: str | Omit = omit, - fields: Iterable[VaultCardFillFieldParam] | Omit = omit, + fields: Iterable[VaultFillFieldParam] | 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. @@ -520,7 +658,10 @@ def upsert( card in any lifecycle state without polling the provider, reauthorizing, replacing aliases, or resetting recovery. Conflicting specifications return 409. Provider-specific authorization requirements and retry behavior are described in - the item's request schema. + the item's request schema. Do not use credential items to store, collect, or + fill credit card data, including card numbers (PANs), security codes (CVV/CVC), + or expiration dates. Use wallet and card item types for credit cards and payment + checkout instead. Args: spec: AgentCard wallet. Omit provider_config to use Kernel-managed credentials, or @@ -561,7 +702,10 @@ def upsert( card in any lifecycle state without polling the provider, reauthorizing, replacing aliases, or resetting recovery. Conflicting specifications return 409. Provider-specific authorization requirements and retry behavior are described in - the item's request schema. + the item's request schema. Do not use credential items to store, collect, or + fill credit card data, including card numbers (PANs), security codes (CVV/CVC), + or expiration dates. Use wallet and card item types for credit cards and payment + checkout instead. Args: spec: Live payment card. Test-mode card creation is not supported. @@ -576,14 +720,57 @@ def upsert( """ ... + @overload + def upsert( + self, + key: str, + *, + id_or_name: str, + spec: CredentialVaultItemSpecInputParam, + type: Literal["credential"], + # 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, + ) -> VaultItem: + """ + Create an item under a key unique within its vault, or retrieve the existing + item when its specification matches. An identical card PUT returns the existing + card in any lifecycle state without polling the provider, reauthorizing, + replacing aliases, or resetting recovery. Conflicting specifications return 409. + Provider-specific authorization requirements and retry behavior are described in + the item's request schema. Do not use credential items to store, collect, or + fill credit card data, including card numbers (PANs), security codes (CVV/CVC), + or expiration dates. Use wallet and card item types for credit cards and payment + checkout instead. + + Args: + spec: Credential fields are for login and other non-payment credentials. Do not store, + collect, or fill credit card data in credential items. Use wallet and card item + types for credit cards and payment checkout instead. + + 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", "spec", "type"]) def upsert( self, key: str, *, id_or_name: str, - spec: item_upsert_params.WalletVaultItemRequestSpec | CardVaultItemSpecParam, - type: Literal["wallet"] | Literal["card"], + spec: item_upsert_params.WalletVaultItemRequestSpec + | CardVaultItemSpecParam + | CredentialVaultItemSpecInputParam, + type: Literal["wallet"] | Literal["card"] | Literal["credential"], # 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, @@ -653,13 +840,19 @@ async def retrieve( and live data that can be requested through `expand`. Read each operation's description before using it. Expanded data is fetched from the provider and is not persisted in the vault item. Requesting an unavailable expansion returns 409 - instead of a partial item. + instead of a partial item. Pending credential items return a collection action. + Kernel-hosted active collection links are renewed atomically on expiry for ready + or pending items without changing the item version. Invoke collect to open a + form for a ready item without clearing values. Sensitive credential values are + never returned. Args: expand: Live fields advertised by `available_expansions` to include in `expanded`. - wait: Hold for up to this many seconds while the item is pending authorization or - approval. + wait: Hold for up to this many seconds while the item is pending authorization, + approval, or credential collection. Return the current item when ready or when + the wait elapses. This does not wait for edits to an already-ready credential; + poll GET without wait and compare version to observe changes after collect. extra_headers: Send extra headers @@ -694,12 +887,14 @@ async def retrieve( ), ) + @overload async def update( self, key: str, *, id_or_name: str, spec: CardVaultItemSpecParam, + type: Literal["card"] | 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, @@ -707,15 +902,19 @@ async def update( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VaultItem: - """Requested cards accept a replacement specification. - - Pending issuance requests - may update provider-supported fields on their existing request, subject to - atomic provider approval checks; omitted optional fields remain unchanged and - explicit empty lists clear them. Wallet/provider binding and unsupported fields - cannot change after authorization starts. An uncertain update enters - recovery_required and must not be retried. Checkout cards may be edited between - authorizations. + """ + Credential updates require type credential and the current version, and change + only values or description; omitted values are preserved, nonempty strings + replace, and null or empty strings clear supported fields. Clearing required + text/email/password values returns pending_collection; browser forms still + require nonempty required inputs. Card updates may omit type for compatibility + with legacy requests. Requested cards accept a replacement specification. + Pending issuance requests may update provider-supported fields on their existing + request, subject to atomic provider approval checks; omitted optional fields + remain unchanged and explicit empty lists clear them. Wallet/provider binding + and unsupported fields cannot change after authorization starts. An uncertain + update enters recovery_required and must not be retried. Checkout cards may be + edited between authorizations. Args: spec: Live payment card. Test-mode card creation is not supported. @@ -728,6 +927,74 @@ async def update( timeout: Override the client-level default timeout for this request, in seconds """ + ... + + @overload + async def update( + self, + key: str, + *, + id_or_name: str, + spec: CredentialVaultItemSpecUpdateParam, + type: Literal["credential"], + version: int, + expected_item_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VaultItem: + """ + Credential updates require type credential and the current version, and change + only values or description; omitted values are preserved, nonempty strings + replace, and null or empty strings clear supported fields. Clearing required + text/email/password values returns pending_collection; browser forms still + require nonempty required inputs. Card updates may omit type for compatibility + with legacy requests. Requested cards accept a replacement specification. + Pending issuance requests may update provider-supported fields on their existing + request, subject to atomic provider approval checks; omitted optional fields + remain unchanged and explicit empty lists clear them. Wallet/provider binding + and unsupported fields cannot change after authorization starts. An uncertain + update enters recovery_required and must not be retried. Checkout cards may be + edited between authorizations. + + Args: + version: Expected current item version from the latest read. + + expected_item_id: Optional immutable item ID precondition. Returns 409 if the key now identifies a + different item. Accepted writes target this immutable ID, preventing + replacement-key races. Supply this when submitting a form bound to a previously + read item. + + 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", "spec"], ["id_or_name", "spec", "type", "version"]) + async def update( + self, + key: str, + *, + id_or_name: str, + spec: CardVaultItemSpecParam | CredentialVaultItemSpecUpdateParam, + type: Literal["card"] | Literal["credential"] | Omit = omit, + version: int | Omit = omit, + expected_item_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> VaultItem: 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: @@ -736,7 +1003,15 @@ async def update( VaultItem, await self._patch( path_template("/vaults/{id_or_name}/items/{key}", id_or_name=id_or_name, key=key), - body=await async_maybe_transform({"spec": spec}, item_update_params.ItemUpdateParams), + body=await async_maybe_transform( + { + "spec": spec, + "type": type, + "version": version, + "expected_item_id": expected_item_id, + }, + item_update_params.ItemUpdateParams, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -755,8 +1030,11 @@ async def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ItemListResponse: - """ - List vault items without secret values + """Credential entries include safe field metadata and non-sensitive values. + + Listing + never creates or renews collection sessions; only an existing unexpired active + session is included. Use single-item GET or collect to obtain a fresh link. Args: extra_headers: Send extra headers @@ -915,6 +1193,51 @@ async def perform_operation( """ ... + @overload + async def perform_operation( + self, + key: str, + *, + id_or_name: str, + type: Literal["collect"], + # 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: + 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, @@ -974,9 +1297,9 @@ async def perform_operation( *, id_or_name: str, browser_id: str, - fields: Iterable[VaultCardFillFieldParam], - page_url: str, + fields: Iterable[VaultFillFieldParam], type: Literal["fill"], + 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. @@ -1012,7 +1335,9 @@ async def perform_operation( 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. + prefix or glob matching. Required for cards, which must use HTTPS without + embedded credentials. Optional for credentials, where omission requires exactly + one open page. timeout_ms: Total operation deadline in milliseconds, not a per-field timeout. @@ -1027,19 +1352,17 @@ async def perform_operation( ... @required_args( - ["id_or_name", "type"], - ["id_or_name", "checkout", "type"], - ["id_or_name", "browser_id", "fields", "page_url", "type"], + ["id_or_name", "type"], ["id_or_name", "checkout", "type"], ["id_or_name", "browser_id", "fields", "type"] ) async def perform_operation( self, key: str, *, id_or_name: str, - type: Literal["authorize"] | Literal["prepare_checkout"] | Literal["fill"], + type: Literal["authorize"] | Literal["collect"] | Literal["prepare_checkout"] | Literal["fill"], checkout: VaultCheckoutContextParam | Omit = omit, browser_id: str | Omit = omit, - fields: Iterable[VaultCardFillFieldParam] | Omit = omit, + fields: Iterable[VaultFillFieldParam] | 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. @@ -1098,7 +1421,10 @@ async def upsert( card in any lifecycle state without polling the provider, reauthorizing, replacing aliases, or resetting recovery. Conflicting specifications return 409. Provider-specific authorization requirements and retry behavior are described in - the item's request schema. + the item's request schema. Do not use credential items to store, collect, or + fill credit card data, including card numbers (PANs), security codes (CVV/CVC), + or expiration dates. Use wallet and card item types for credit cards and payment + checkout instead. Args: spec: AgentCard wallet. Omit provider_config to use Kernel-managed credentials, or @@ -1139,7 +1465,10 @@ async def upsert( card in any lifecycle state without polling the provider, reauthorizing, replacing aliases, or resetting recovery. Conflicting specifications return 409. Provider-specific authorization requirements and retry behavior are described in - the item's request schema. + the item's request schema. Do not use credential items to store, collect, or + fill credit card data, including card numbers (PANs), security codes (CVV/CVC), + or expiration dates. Use wallet and card item types for credit cards and payment + checkout instead. Args: spec: Live payment card. Test-mode card creation is not supported. @@ -1154,14 +1483,57 @@ async def upsert( """ ... + @overload + async def upsert( + self, + key: str, + *, + id_or_name: str, + spec: CredentialVaultItemSpecInputParam, + type: Literal["credential"], + # 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, + ) -> VaultItem: + """ + Create an item under a key unique within its vault, or retrieve the existing + item when its specification matches. An identical card PUT returns the existing + card in any lifecycle state without polling the provider, reauthorizing, + replacing aliases, or resetting recovery. Conflicting specifications return 409. + Provider-specific authorization requirements and retry behavior are described in + the item's request schema. Do not use credential items to store, collect, or + fill credit card data, including card numbers (PANs), security codes (CVV/CVC), + or expiration dates. Use wallet and card item types for credit cards and payment + checkout instead. + + Args: + spec: Credential fields are for login and other non-payment credentials. Do not store, + collect, or fill credit card data in credential items. Use wallet and card item + types for credit cards and payment checkout instead. + + 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", "spec", "type"]) async def upsert( self, key: str, *, id_or_name: str, - spec: item_upsert_params.WalletVaultItemRequestSpec | CardVaultItemSpecParam, - type: Literal["wallet"] | Literal["card"], + spec: item_upsert_params.WalletVaultItemRequestSpec + | CardVaultItemSpecParam + | CredentialVaultItemSpecInputParam, + type: Literal["wallet"] | Literal["card"] | Literal["credential"], # 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, diff --git a/src/kernel/types/vaults/__init__.py b/src/kernel/types/vaults/__init__.py index b48fd00d..9188791e 100644 --- a/src/kernel/types/vaults/__init__.py +++ b/src/kernel/types/vaults/__init__.py @@ -15,20 +15,42 @@ from .item_retrieve_params import ItemRetrieveParams as ItemRetrieveParams from .vault_payment_method import VaultPaymentMethod as VaultPaymentMethod from .card_vault_item_state import CardVaultItemState as CardVaultItemState +from .credential_vault_item import CredentialVaultItem as CredentialVaultItem +from .vault_fill_field_param import VaultFillFieldParam as VaultFillFieldParam 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 .credential_vault_item_spec import CredentialVaultItemSpec as CredentialVaultItemSpec +from .credential_vault_field_type import CredentialVaultFieldType as CredentialVaultFieldType +from .credential_vault_item_state import CredentialVaultItemState as CredentialVaultItemState +from .credential_collection_action import CredentialCollectionAction as CredentialCollectionAction +from .credential_vault_field_state import CredentialVaultFieldState as CredentialVaultFieldState 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 .credential_vault_field_definition import CredentialVaultFieldDefinition as CredentialVaultFieldDefinition +from .credential_vault_field_input_param import CredentialVaultFieldInputParam as CredentialVaultFieldInputParam +from .credential_vault_field_update_param import CredentialVaultFieldUpdateParam as CredentialVaultFieldUpdateParam +from .credential_vault_item_request_param import CredentialVaultItemRequestParam as CredentialVaultItemRequestParam +from .credential_vault_item_spec_input_param import ( + CredentialVaultItemSpecInputParam as CredentialVaultItemSpecInputParam, +) +from .credential_vault_item_spec_update_param import ( + CredentialVaultItemSpecUpdateParam as CredentialVaultItemSpecUpdateParam, +) from .fill_vault_item_operation_request_param import ( FillVaultItemOperationRequestParam as FillVaultItemOperationRequestParam, ) +from .collect_vault_item_operation_request_param import ( + CollectVaultItemOperationRequestParam as CollectVaultItemOperationRequestParam, +) +from .credential_vault_item_update_request_param import ( + CredentialVaultItemUpdateRequestParam as CredentialVaultItemUpdateRequestParam, +) from .authorize_vault_item_operation_request_param import ( AuthorizeVaultItemOperationRequestParam as AuthorizeVaultItemOperationRequestParam, ) diff --git a/src/kernel/types/vaults/collect_vault_item_operation_request_param.py b/src/kernel/types/vaults/collect_vault_item_operation_request_param.py new file mode 100644 index 00000000..2ce3031c --- /dev/null +++ b/src/kernel/types/vaults/collect_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 + +__all__ = ["CollectVaultItemOperationRequestParam"] + + +class CollectVaultItemOperationRequestParam(TypedDict, total=False): + """Return the credential item with its collection action. + + Supported for ready + and pending_collection credential items. Always render the same form from + every form-supported field; totp fields have no form input and are omitted. + No caller-selected field subsets or form overrides are accepted. + Reuse an active Kernel-hosted session or renew an expired session atomically. + Customer-hosted forms use their own backend and ordinary item GET/PATCH. Opening + the form does not clear values or change readiness or item version. + To observe edits on a ready item, record its version and poll GET without + wait until the version changes, then reconcile the returned state. Version + changes may also come from PATCH; they do not identify a particular form + submission. Customer-hosted apps use their own submission callback, including + for unchanged forms. The wait parameter waits for readiness, not edits. + """ + + type: Required[Literal["collect"]] diff --git a/src/kernel/types/vaults/credential_collection_action.py b/src/kernel/types/vaults/credential_collection_action.py new file mode 100644 index 00000000..331e255c --- /dev/null +++ b/src/kernel/types/vaults/credential_collection_action.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["CredentialCollectionAction"] + + +class CredentialCollectionAction(BaseModel): + """ + One schema-derived form for the item, available in ready or pending_collection + state. Render every form-supported field as editable; omit totp fields and + preserve their stored seeds. Prefill non-sensitive values, + and allow existing sensitive values to be preserved or replaced without ever + revealing them. No field subsets or per-request form configuration exist. + Validate required fields against the resulting values, including preserved + secrets. Submit changed values only, using the version used to render the form. + Scoped hosted submission rejects totp edits; seed writes require the ordinary + authenticated item API. Customer forms likewise omit totp from their payloads. + Save edits atomically. A successful hosted submission increments the version, + marks ready, and consumes the session; an empty edit may complete collection + while preserving values. A customer form uses PATCH for changed values and + does not send an empty PATCH when nothing changed. + Kernel-hosted bearer sessions require no Kernel account and are bound to the + item version. Expired, superseded, consumed, or deleted-item sessions cannot + submit. Authenticated item GET renews expired active sessions for ready or pending items; pending + items always receive an action. A ready item with no active session omits the + action until collect is invoked. Concurrent renewals return the same link. + Renewal changes neither values nor item version. An expired link cannot renew + itself. The hosted form handles its collection protocol; callers only open + the returned URL and do not extract or submit its token through the public API. + For customer-hosted forms, use @onkernel/vault-react and an authenticated + customer backend calling the ordinary item GET/PATCH API. Kernel does not + store customer collection URLs or authenticate the customer's end users. + Treat URLs and submitted values as secrets and exclude them from logs, + traces, and errors. + """ + + expires_at: datetime + """Expiry of the Kernel-hosted collection link (30 minutes after issuance).""" + + name: Literal["collect"] + + url: str + """Time-scoped hosted form URL (vault.kernel.sh in production). + + Open this URL as returned; treat it as a secret. + """ diff --git a/src/kernel/types/vaults/credential_vault_field_definition.py b/src/kernel/types/vaults/credential_vault_field_definition.py new file mode 100644 index 00000000..9350813b --- /dev/null +++ b/src/kernel/types/vaults/credential_vault_field_definition.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from ..._models import BaseModel +from .credential_vault_field_type import CredentialVaultFieldType + +__all__ = ["CredentialVaultFieldDefinition"] + + +class CredentialVaultFieldDefinition(BaseModel): + required: bool + """Whether a nonempty value is required for readiness and form submission.""" + + sensitive: bool + """Whether the value is omitted from every item response. + + Reserve true for secrets such as passwords, API tokens, and TOTP seeds. Ordinary + usernames and email addresses should be false so the form can display and + prefill them. + """ + + type: CredentialVaultFieldType + """ + Text, email, and password have form inputs; totp does not and is omitted from + both Kernel-hosted and customer React forms. Password and totp must be + sensitive. A totp value is an RFC 4648 Base32 generator seed (case-insensitive, + optional trailing padding), not an otpauth URI or current code. Reject invalid + or empty decoded seeds. Browser fill generates an RFC 6238 code at execution + time using HMAC-SHA1, 6 digits, and a 30-second period. Preserve leading zeros; + never fill the seed. Custom algorithms, digits, periods, and form enrollment are + unsupported. + """ diff --git a/src/kernel/types/vaults/credential_vault_field_input_param.py b/src/kernel/types/vaults/credential_vault_field_input_param.py new file mode 100644 index 00000000..2cf68978 --- /dev/null +++ b/src/kernel/types/vaults/credential_vault_field_input_param.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .credential_vault_field_type import CredentialVaultFieldType + +__all__ = ["CredentialVaultFieldInputParam"] + + +class CredentialVaultFieldInputParam(TypedDict, total=False): + type: Required[CredentialVaultFieldType] + """ + Text, email, and password have form inputs; totp does not and is omitted from + both Kernel-hosted and customer React forms. Password and totp must be + sensitive. A totp value is an RFC 4648 Base32 generator seed (case-insensitive, + optional trailing padding), not an otpauth URI or current code. Reject invalid + or empty decoded seeds. Browser fill generates an RFC 6238 code at execution + time using HMAC-SHA1, 6 digits, and a 30-second period. Preserve leading zeros; + never fill the seed. Custom algorithms, digits, periods, and form enrollment are + unsupported. + """ + + required: bool + + sensitive: bool + """ + Set false explicitly for ordinary usernames, email addresses, and other + non-secret identifiers. Reserve true for secrets such as passwords, API tokens, + and TOTP seeds. Password and totp fields must be true. Omission defaults to true + for safety; do not rely on that default for every field. False permits API reads + and form prefilling. + """ + + value: str + """ + Optional initial value satisfying the declared type, at most 16 KiB in UTF-8 + bytes. Omit to leave unset; null and empty strings are rejected on creation. + Sensitive values are encrypted and never copied into the returned spec. + """ diff --git a/src/kernel/types/vaults/credential_vault_field_state.py b/src/kernel/types/vaults/credential_vault_field_state.py new file mode 100644 index 00000000..1c2935cd --- /dev/null +++ b/src/kernel/types/vaults/credential_vault_field_state.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["CredentialVaultFieldState"] + + +class CredentialVaultFieldState(BaseModel): + has_value: bool + + value: Optional[str] = None + """Present exactly when has_value is true and the field is not sensitive. + + Reflects the latest developer or human edit. For totp, has_value indicates a + stored seed; neither the seed nor a generated code is returned. + """ diff --git a/src/kernel/types/vaults/credential_vault_field_type.py b/src/kernel/types/vaults/credential_vault_field_type.py new file mode 100644 index 00000000..cb400e9c --- /dev/null +++ b/src/kernel/types/vaults/credential_vault_field_type.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__ = ["CredentialVaultFieldType"] + +CredentialVaultFieldType: TypeAlias = Literal["text", "email", "password", "totp"] diff --git a/src/kernel/types/vaults/credential_vault_field_update_param.py b/src/kernel/types/vaults/credential_vault_field_update_param.py new file mode 100644 index 00000000..9d4a532b --- /dev/null +++ b/src/kernel/types/vaults/credential_vault_field_update_param.py @@ -0,0 +1,20 @@ +# 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 Required, TypedDict + +__all__ = ["CredentialVaultFieldUpdateParam"] + + +class CredentialVaultFieldUpdateParam(TypedDict, total=False): + value: Required[Optional[str]] + """ + Replacement value (at most 16 KiB in UTF-8 bytes), or null or an empty string to + immediately clear the stored value. Clearing a required form-supported field + reopens collection; clearing an optional field does not prevent readiness. + Values must satisfy the declared field type. For totp, value is the generator + seed, never a current code. Clearing a required totp field returns 400 because + it cannot be collected in a form. + """ diff --git a/src/kernel/types/vaults/credential_vault_item.py b/src/kernel/types/vaults/credential_vault_item.py new file mode 100644 index 00000000..79c12abc --- /dev/null +++ b/src/kernel/types/vaults/credential_vault_item.py @@ -0,0 +1,93 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from ..._models import BaseModel +from .credential_vault_item_spec import CredentialVaultItemSpec +from .credential_vault_item_state import CredentialVaultItemState +from .credential_collection_action import CredentialCollectionAction + +__all__ = ["CredentialVaultItem", "AvailableExpansion", "AvailableOperation"] + + +class AvailableExpansion(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 AvailableOperation(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", "collect", "prepare_checkout", "fill"] + + +class CredentialVaultItem(BaseModel): + id: str + + available_expansions: List[AvailableExpansion] + + available_operations: List[AvailableOperation] + """Advertises collect for ready and pending_collection items. + + Browser fill is advertised only when separately implemented and eligible. + """ + + created_at: datetime + + key: str + """Immutable item key assigned when the item is created.""" + + spec: CredentialVaultItemSpec + + state: CredentialVaultItemState + + type: Literal["credential"] + + updated_at: datetime + + version: int + """ + Starts at 1 and increments on PATCH and successful hosted submission, but not + collection-link renewal. + """ + + action: Optional[CredentialCollectionAction] = None + """ + One schema-derived form for the item, available in ready or pending_collection + state. Render every form-supported field as editable; omit totp fields and + preserve their stored seeds. Prefill non-sensitive values, and allow existing + sensitive values to be preserved or replaced without ever revealing them. No + field subsets or per-request form configuration exist. Validate required fields + against the resulting values, including preserved secrets. Submit changed values + only, using the version used to render the form. Scoped hosted submission + rejects totp edits; seed writes require the ordinary authenticated item API. + Customer forms likewise omit totp from their payloads. Save edits atomically. A + successful hosted submission increments the version, marks ready, and consumes + the session; an empty edit may complete collection while preserving values. A + customer form uses PATCH for changed values and does not send an empty PATCH + when nothing changed. Kernel-hosted bearer sessions require no Kernel account + and are bound to the item version. Expired, superseded, consumed, or + deleted-item sessions cannot submit. Authenticated item GET renews expired + active sessions for ready or pending items; pending items always receive an + action. A ready item with no active session omits the action until collect is + invoked. Concurrent renewals return the same link. Renewal changes neither + values nor item version. An expired link cannot renew itself. The hosted form + handles its collection protocol; callers only open the returned URL and do not + extract or submit its token through the public API. For customer-hosted forms, + use @onkernel/vault-react and an authenticated customer backend calling the + ordinary item GET/PATCH API. Kernel does not store customer collection URLs or + authenticate the customer's end users. Treat URLs and submitted values as + secrets and exclude them from logs, traces, and errors. + """ diff --git a/src/kernel/types/vaults/credential_vault_item_request_param.py b/src/kernel/types/vaults/credential_vault_item_request_param.py new file mode 100644 index 00000000..579d7a0b --- /dev/null +++ b/src/kernel/types/vaults/credential_vault_item_request_param.py @@ -0,0 +1,36 @@ +# 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 .credential_vault_item_spec_input_param import CredentialVaultItemSpecInputParam + +__all__ = ["CredentialVaultItemRequestParam"] + + +class CredentialVaultItemRequestParam(TypedDict, total=False): + """ + Create a credential item without a wallet or external provider. + Do not use credential items to store, collect, or fill credit card data, + including card numbers (PANs), security codes (CVV/CVC), or expiration dates. + Use wallet and card item types for credit cards and payment checkout instead. + If all required fields have values, return ready without a collection action; + collect can still open its form. Otherwise return pending_collection with + a time-scoped Kernel-hosted collection action. Missing + optional fields alone do not trigger collection. Repeating the original + creation request returns the current item without overwriting later edits; + a different request at the same key returns 409. Use PATCH for updates. + Required totp fields must include a valid seed on creation; otherwise return + 400 rather than opening a form that cannot collect it. Optional totp fields + may be unset and populated later through PATCH. + """ + + spec: Required[CredentialVaultItemSpecInputParam] + """Credential fields are for login and other non-payment credentials. + + Do not store, collect, or fill credit card data in credential items. Use wallet + and card item types for credit cards and payment checkout instead. + """ + + type: Required[Literal["credential"]] diff --git a/src/kernel/types/vaults/credential_vault_item_spec.py b/src/kernel/types/vaults/credential_vault_item_spec.py new file mode 100644 index 00000000..e953f416 --- /dev/null +++ b/src/kernel/types/vaults/credential_vault_item_spec.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional + +from ..._models import BaseModel +from .credential_vault_field_definition import CredentialVaultFieldDefinition + +__all__ = ["CredentialVaultItemSpec"] + + +class CredentialVaultItemSpec(BaseModel): + fields: Dict[str, CredentialVaultFieldDefinition] + + description: Optional[str] = None + """ + Recognizable site or service name displayed verbatim as the form title, without + suffixes such as sign-in credentials. Display text only, not an enforced + destination policy. + """ diff --git a/src/kernel/types/vaults/credential_vault_item_spec_input_param.py b/src/kernel/types/vaults/credential_vault_item_spec_input_param.py new file mode 100644 index 00000000..a0e8850a --- /dev/null +++ b/src/kernel/types/vaults/credential_vault_item_spec_input_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 import Dict +from typing_extensions import Required, TypedDict + +from .credential_vault_field_input_param import CredentialVaultFieldInputParam + +__all__ = ["CredentialVaultItemSpecInputParam"] + + +class CredentialVaultItemSpecInputParam(TypedDict, total=False): + """Credential fields are for login and other non-payment credentials. + + Do not store, collect, or fill credit card data in credential items. Use wallet and card item types for credit cards and payment checkout instead. + """ + + fields: Required[Dict[str, CredentialVaultFieldInputParam]] + + description: str + """ + The site's recognizable display name, used verbatim as the user-facing form + title (for example, Hacker News). Use only the site or service name; do not + append sign-in, login, credentials, or task instructions. This is display text, + not an enforced destination policy. At most 16 KiB in UTF-8 bytes. + """ diff --git a/src/kernel/types/vaults/credential_vault_item_spec_update_param.py b/src/kernel/types/vaults/credential_vault_item_spec_update_param.py new file mode 100644 index 00000000..d7933b9b --- /dev/null +++ b/src/kernel/types/vaults/credential_vault_item_spec_update_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 import Dict +from typing_extensions import TypedDict + +from .credential_vault_field_update_param import CredentialVaultFieldUpdateParam + +__all__ = ["CredentialVaultItemSpecUpdateParam"] + + +class CredentialVaultItemSpecUpdateParam(TypedDict, total=False): + description: str + """ + Recognizable site or service name used as the form title, without suffixes such + as sign-in credentials. An empty string clears it. Display text only, not an + enforced destination policy. The server also enforces a 16 KiB UTF-8 byte limit. + """ + + fields: Dict[str, CredentialVaultFieldUpdateParam] diff --git a/src/kernel/types/vaults/credential_vault_item_state.py b/src/kernel/types/vaults/credential_vault_item_state.py new file mode 100644 index 00000000..7d64ad95 --- /dev/null +++ b/src/kernel/types/vaults/credential_vault_item_state.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict +from typing_extensions import Literal + +from ..._models import BaseModel +from .credential_vault_field_state import CredentialVaultFieldState + +__all__ = ["CredentialVaultItemState"] + + +class CredentialVaultItemState(BaseModel): + fields: Dict[str, CredentialVaultFieldState] + """Exactly one entry for each declared field.""" + + status: Literal["pending_collection", "ready"] + """Ready means all required fields have values, not that a login succeeded. + + Optional fields may remain unset. + """ diff --git a/src/kernel/types/vaults/credential_vault_item_update_request_param.py b/src/kernel/types/vaults/credential_vault_item_update_request_param.py new file mode 100644 index 00000000..b76c4b8f --- /dev/null +++ b/src/kernel/types/vaults/credential_vault_item_update_request_param.py @@ -0,0 +1,38 @@ +# 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 .credential_vault_item_spec_update_param import CredentialVaultItemSpecUpdateParam + +__all__ = ["CredentialVaultItemUpdateRequestParam"] + + +class CredentialVaultItemUpdateRequestParam(TypedDict, total=False): + """Atomically update description and selected values. + + Omitted properties are + preserved. Field names, types, required flags, and sensitivity cannot change. + Unknown field names return 400; stale versions or mismatched item types + return 409 without changing the item. A successful update increments version + and invalidates outstanding Kernel-hosted collection sessions. If required + values remain missing, return pending_collection and a fresh collection + action. Otherwise return ready without an action; collect can open the form + again without clearing values. Customer URLs have no Kernel-managed expiry. + """ + + spec: Required[CredentialVaultItemSpecUpdateParam] + + type: Required[Literal["credential"]] + + version: Required[int] + """Expected current item version from the latest read.""" + + expected_item_id: str + """Optional immutable item ID precondition. + + Returns 409 if the key now identifies a different item. Accepted writes target + this immutable ID, preventing replacement-key races. Supply this when submitting + a form bound to a previously read item. + """ 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 index a4613cd6..1d8fc8be 100644 --- a/src/kernel/types/vaults/fill_vault_item_operation_request_param.py +++ b/src/kernel/types/vaults/fill_vault_item_operation_request_param.py @@ -5,20 +5,23 @@ from typing import Iterable from typing_extensions import Literal, Required, TypedDict -from .vault_card_fill_field_param import VaultCardFillFieldParam +from .vault_fill_field_param import VaultFillFieldParam __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. + Fill selected fields from one ready credential or ready, unexpired Link card + into a browser linked to its vault. 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 + Find exactly one open page matching `page_url`. Credential items may omit + `page_url` to require exactly one open page; cards require an HTTPS page URL. + Credentials have no destination allowlist. TOTP fields generate a current + code immediately before writing; their seeds never enter the browser. 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 @@ -45,17 +48,19 @@ class FillVaultItemOperationRequestParam(TypedDict, total=False): browser_id: Required[str] """Browser session ID, not a reusable browser name.""" - fields: Required[Iterable[VaultCardFillFieldParam]] + fields: Required[Iterable[VaultFillFieldParam]] """Field bindings for this step. No two bindings may resolve to the same element.""" - page_url: Required[str] + type: Required[Literal["fill"]] + + page_url: 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. + No prefix or glob matching. Required for cards, which must use HTTPS without + embedded credentials. Optional for credentials, where omission requires exactly + one open page. """ - type: Required[Literal["fill"]] - timeout_ms: int """Total operation deadline in milliseconds, not a per-field timeout.""" diff --git a/src/kernel/types/vaults/item_perform_operation_params.py b/src/kernel/types/vaults/item_perform_operation_params.py index c583f53b..33c38043 100644 --- a/src/kernel/types/vaults/item_perform_operation_params.py +++ b/src/kernel/types/vaults/item_perform_operation_params.py @@ -5,12 +5,13 @@ from typing import Union, Iterable from typing_extensions import Literal, Required, TypeAlias, TypedDict -from .vault_card_fill_field_param import VaultCardFillFieldParam +from .vault_fill_field_param import VaultFillFieldParam from .vault_checkout_context_param import VaultCheckoutContextParam __all__ = [ "ItemPerformOperationParams", "AuthorizeVaultItemOperationRequest", + "CollectVaultItemOperationRequest", "PrepareCheckoutVaultItemOperationRequest", "FillVaultItemOperationRequest", ] @@ -22,6 +23,12 @@ class AuthorizeVaultItemOperationRequest(TypedDict, total=False): type: Required[Literal["authorize"]] +class CollectVaultItemOperationRequest(TypedDict, total=False): + id_or_name: Required[str] + + type: Required[Literal["collect"]] + + class PrepareCheckoutVaultItemOperationRequest(TypedDict, total=False): id_or_name: Required[str] @@ -43,22 +50,27 @@ class FillVaultItemOperationRequest(TypedDict, total=False): browser_id: Required[str] """Browser session ID, not a reusable browser name.""" - fields: Required[Iterable[VaultCardFillFieldParam]] + fields: Required[Iterable[VaultFillFieldParam]] """Field bindings for this step. No two bindings may resolve to the same element.""" - page_url: Required[str] + type: Required[Literal["fill"]] + + page_url: 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. + No prefix or glob matching. Required for cards, which must use HTTPS without + embedded credentials. Optional for credentials, where omission requires exactly + one open page. """ - type: Required[Literal["fill"]] - timeout_ms: int """Total operation deadline in milliseconds, not a per-field timeout.""" ItemPerformOperationParams: TypeAlias = Union[ - AuthorizeVaultItemOperationRequest, PrepareCheckoutVaultItemOperationRequest, FillVaultItemOperationRequest + AuthorizeVaultItemOperationRequest, + CollectVaultItemOperationRequest, + PrepareCheckoutVaultItemOperationRequest, + FillVaultItemOperationRequest, ] diff --git a/src/kernel/types/vaults/item_retrieve_params.py b/src/kernel/types/vaults/item_retrieve_params.py index 976881b2..d07f8167 100644 --- a/src/kernel/types/vaults/item_retrieve_params.py +++ b/src/kernel/types/vaults/item_retrieve_params.py @@ -16,6 +16,8 @@ class ItemRetrieveParams(TypedDict, total=False): wait: int """ - Hold for up to this many seconds while the item is pending authorization or - approval. + Hold for up to this many seconds while the item is pending authorization, + approval, or credential collection. Return the current item when ready or when + the wait elapses. This does not wait for edits to an already-ready credential; + poll GET without wait and compare version to observe changes after collect. """ diff --git a/src/kernel/types/vaults/item_update_params.py b/src/kernel/types/vaults/item_update_params.py index 1ea45428..136a04d0 100644 --- a/src/kernel/types/vaults/item_update_params.py +++ b/src/kernel/types/vaults/item_update_params.py @@ -2,15 +2,41 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing import Union +from typing_extensions import Literal, Required, TypeAlias, TypedDict from .card_vault_item_spec_param import CardVaultItemSpecParam +from .credential_vault_item_spec_update_param import CredentialVaultItemSpecUpdateParam -__all__ = ["ItemUpdateParams"] +__all__ = ["ItemUpdateParams", "CardVaultItemUpdateRequest", "CredentialVaultItemUpdateRequest"] -class ItemUpdateParams(TypedDict, total=False): +class CardVaultItemUpdateRequest(TypedDict, total=False): id_or_name: Required[str] spec: Required[CardVaultItemSpecParam] """Live payment card. Test-mode card creation is not supported.""" + + type: Literal["card"] + + +class CredentialVaultItemUpdateRequest(TypedDict, total=False): + id_or_name: Required[str] + + spec: Required[CredentialVaultItemSpecUpdateParam] + + type: Required[Literal["credential"]] + + version: Required[int] + """Expected current item version from the latest read.""" + + expected_item_id: str + """Optional immutable item ID precondition. + + Returns 409 if the key now identifies a different item. Accepted writes target + this immutable ID, preventing replacement-key races. Supply this when submitting + a form bound to a previously read item. + """ + + +ItemUpdateParams: TypeAlias = Union[CardVaultItemUpdateRequest, CredentialVaultItemUpdateRequest] diff --git a/src/kernel/types/vaults/item_upsert_params.py b/src/kernel/types/vaults/item_upsert_params.py index 2a62d726..5890280a 100644 --- a/src/kernel/types/vaults/item_upsert_params.py +++ b/src/kernel/types/vaults/item_upsert_params.py @@ -6,6 +6,7 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from .card_vault_item_spec_param import CardVaultItemSpecParam +from .credential_vault_item_spec_input_param import CredentialVaultItemSpecInputParam __all__ = [ "ItemUpsertParams", @@ -22,6 +23,7 @@ "WalletVaultItemRequestSpecAgentCardWalletVaultItemSpec", "WalletVaultItemRequestSpecAgentCardWalletVaultItemSpecProviderConfig", "CardVaultItemRequest", + "CredentialVaultItemRequest", ] @@ -185,4 +187,17 @@ class CardVaultItemRequest(TypedDict, total=False): type: Required[Literal["card"]] -ItemUpsertParams: TypeAlias = Union[WalletVaultItemRequest, CardVaultItemRequest] +class CredentialVaultItemRequest(TypedDict, total=False): + id_or_name: Required[str] + + spec: Required[CredentialVaultItemSpecInputParam] + """Credential fields are for login and other non-payment credentials. + + Do not store, collect, or fill credit card data in credential items. Use wallet + and card item types for credit cards and payment checkout instead. + """ + + type: Required[Literal["credential"]] + + +ItemUpsertParams: TypeAlias = Union[WalletVaultItemRequest, CardVaultItemRequest, CredentialVaultItemRequest] diff --git a/src/kernel/types/vaults/vault_card_fill_field_param.py b/src/kernel/types/vaults/vault_card_fill_field_param.py deleted file mode 100644 index e286291e..00000000 --- a/src/kernel/types/vaults/vault_card_fill_field_param.py +++ /dev/null @@ -1,60 +0,0 @@ -# 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_fill_field_param.py b/src/kernel/types/vaults/vault_fill_field_param.py new file mode 100644 index 00000000..a7fc5255 --- /dev/null +++ b/src/kernel/types/vaults/vault_fill_field_param.py @@ -0,0 +1,23 @@ +# 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__ = ["VaultFillFieldParam"] + + +class VaultFillFieldParam(TypedDict, total=False): + field: Required[str] + """A declared credential field name or a supported card field. + + Unset credential fields cannot be filled. + """ + + selector: Required[str] + + format: Literal["MM/YY", "MM/YYYY"] + """Required only for a card's combined expiration field. + + Forbidden for other card fields and all credential fields. + """ diff --git a/src/kernel/types/vaults/vault_item.py b/src/kernel/types/vaults/vault_item.py index e33e12db..07253ea4 100644 --- a/src/kernel/types/vaults/vault_item.py +++ b/src/kernel/types/vaults/vault_item.py @@ -10,6 +10,7 @@ from .card_vault_item_spec import CardVaultItemSpec from .vault_payment_method import VaultPaymentMethod from .card_vault_item_state import CardVaultItemState +from .credential_vault_item import CredentialVaultItem from .wallet_vault_item_spec import WalletVaultItemSpec from .wallet_vault_item_state import WalletVaultItemState @@ -43,7 +44,7 @@ class WalletVaultItemAvailableOperation(BaseModel): description: str - type: Literal["authorize", "prepare_checkout", "fill"] + type: Literal["authorize", "collect", "prepare_checkout", "fill"] class WalletVaultItemExpanded(BaseModel): @@ -107,7 +108,7 @@ class CardVaultItemAvailableOperation(BaseModel): description: str - type: Literal["authorize", "prepare_checkout", "fill"] + type: Literal["authorize", "collect", "prepare_checkout", "fill"] class CardVaultItem(BaseModel): @@ -136,4 +137,6 @@ class CardVaultItem(BaseModel): expires_at: Optional[datetime] = None -VaultItem: TypeAlias = Annotated[Union[WalletVaultItem, CardVaultItem], PropertyInfo(discriminator="type")] +VaultItem: TypeAlias = Annotated[ + Union[WalletVaultItem, CardVaultItem, CredentialVaultItem], PropertyInfo(discriminator="type") +] diff --git a/src/kernel/types/vaults/vault_item_operation_response.py b/src/kernel/types/vaults/vault_item_operation_response.py index a464a6b8..163f73a7 100644 --- a/src/kernel/types/vaults/vault_item_operation_response.py +++ b/src/kernel/types/vaults/vault_item_operation_response.py @@ -9,6 +9,7 @@ from .card_vault_item_spec import CardVaultItemSpec from .vault_payment_method import VaultPaymentMethod from .card_vault_item_state import CardVaultItemState +from .credential_vault_item import CredentialVaultItem from .wallet_vault_item_spec import WalletVaultItemSpec from .wallet_vault_item_state import WalletVaultItemState from .fill_vault_item_operation_result import FillVaultItemOperationResult @@ -43,7 +44,7 @@ class WalletVaultItemAvailableOperation(BaseModel): description: str - type: Literal["authorize", "prepare_checkout", "fill"] + type: Literal["authorize", "collect", "prepare_checkout", "fill"] class WalletVaultItemExpanded(BaseModel): @@ -107,7 +108,7 @@ class CardVaultItemAvailableOperation(BaseModel): description: str - type: Literal["authorize", "prepare_checkout", "fill"] + type: Literal["authorize", "collect", "prepare_checkout", "fill"] class CardVaultItem(BaseModel): @@ -136,4 +137,6 @@ class CardVaultItem(BaseModel): expires_at: Optional[datetime] = None -VaultItemOperationResponse: TypeAlias = Union[WalletVaultItem, CardVaultItem, FillVaultItemOperationResult] +VaultItemOperationResponse: TypeAlias = Union[ + WalletVaultItem, CardVaultItem, CredentialVaultItem, FillVaultItemOperationResult +] diff --git a/tests/api_resources/vaults/test_items.py b/tests/api_resources/vaults/test_items.py index f9dde10b..1eeb98e3 100644 --- a/tests/api_resources/vaults/test_items.py +++ b/tests/api_resources/vaults/test_items.py @@ -87,38 +87,38 @@ def test_path_params_retrieve(self, client: Kernel) -> None: @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_method_update(self, client: Kernel) -> None: + def test_method_update_overload_1(self, client: Kernel) -> None: item = client.vaults.items.update( key="x", id_or_name="id_or_name", spec={ - "amount": 3000, - "context": "The order total changed to USD 30.00 including shipping and taxes for one notebook. Update this unapproved request rather than creating a second payment.", - "currency": "usd", - "merchant_name": "Example Store", - "merchant_url": "https://store.example.com", - "payment_method_id": "pm_example", + "amount": 1, + "context": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "currency": "bFx", + "merchant_name": "x", + "merchant_url": "https://example.com", + "payment_method_id": "x", "provider": "link", - "wallet": "link-wallet", + "wallet": "wallet", }, ) assert_matches_type(VaultItem, item, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_method_update_with_all_params(self, client: Kernel) -> None: + def test_method_update_with_all_params_overload_1(self, client: Kernel) -> None: item = client.vaults.items.update( key="x", id_or_name="id_or_name", spec={ - "amount": 3000, - "context": "The order total changed to USD 30.00 including shipping and taxes for one notebook. Update this unapproved request rather than creating a second payment.", - "currency": "usd", - "merchant_name": "Example Store", - "merchant_url": "https://store.example.com", - "payment_method_id": "pm_example", + "amount": 1, + "context": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "currency": "bFx", + "merchant_name": "x", + "merchant_url": "https://example.com", + "payment_method_id": "x", "provider": "link", - "wallet": "link-wallet", + "wallet": "wallet", "expires_at": 0, "line_items": [ { @@ -148,24 +148,25 @@ def test_method_update_with_all_params(self, client: Kernel) -> None: } ], }, + type="card", ) assert_matches_type(VaultItem, item, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_raw_response_update(self, client: Kernel) -> None: + def test_raw_response_update_overload_1(self, client: Kernel) -> None: response = client.vaults.items.with_raw_response.update( key="x", id_or_name="id_or_name", spec={ - "amount": 3000, - "context": "The order total changed to USD 30.00 including shipping and taxes for one notebook. Update this unapproved request rather than creating a second payment.", - "currency": "usd", - "merchant_name": "Example Store", - "merchant_url": "https://store.example.com", - "payment_method_id": "pm_example", + "amount": 1, + "context": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "currency": "bFx", + "merchant_name": "x", + "merchant_url": "https://example.com", + "payment_method_id": "x", "provider": "link", - "wallet": "link-wallet", + "wallet": "wallet", }, ) @@ -176,19 +177,19 @@ def test_raw_response_update(self, client: Kernel) -> None: @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_streaming_response_update(self, client: Kernel) -> None: + def test_streaming_response_update_overload_1(self, client: Kernel) -> None: with client.vaults.items.with_streaming_response.update( key="x", id_or_name="id_or_name", spec={ - "amount": 3000, - "context": "The order total changed to USD 30.00 including shipping and taxes for one notebook. Update this unapproved request rather than creating a second payment.", - "currency": "usd", - "merchant_name": "Example Store", - "merchant_url": "https://store.example.com", - "payment_method_id": "pm_example", + "amount": 1, + "context": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "currency": "bFx", + "merchant_name": "x", + "merchant_url": "https://example.com", + "payment_method_id": "x", "provider": "link", - "wallet": "link-wallet", + "wallet": "wallet", }, ) as response: assert not response.is_closed @@ -201,20 +202,20 @@ 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: + def test_path_params_update_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.update( key="x", id_or_name="", spec={ - "amount": 3000, - "context": "The order total changed to USD 30.00 including shipping and taxes for one notebook. Update this unapproved request rather than creating a second payment.", - "currency": "usd", - "merchant_name": "Example Store", - "merchant_url": "https://store.example.com", - "payment_method_id": "pm_example", + "amount": 1, + "context": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "currency": "bFx", + "merchant_name": "x", + "merchant_url": "https://example.com", + "payment_method_id": "x", "provider": "link", - "wallet": "link-wallet", + "wallet": "wallet", }, ) @@ -223,17 +224,100 @@ def test_path_params_update(self, client: Kernel) -> None: key="", id_or_name="id_or_name", spec={ - "amount": 3000, - "context": "The order total changed to USD 30.00 including shipping and taxes for one notebook. Update this unapproved request rather than creating a second payment.", - "currency": "usd", - "merchant_name": "Example Store", - "merchant_url": "https://store.example.com", - "payment_method_id": "pm_example", + "amount": 1, + "context": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "currency": "bFx", + "merchant_name": "x", + "merchant_url": "https://example.com", + "payment_method_id": "x", "provider": "link", - "wallet": "link-wallet", + "wallet": "wallet", }, ) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_update_overload_2(self, client: Kernel) -> None: + item = client.vaults.items.update( + key="x", + id_or_name="id_or_name", + spec={}, + type="credential", + version=1, + ) + assert_matches_type(VaultItem, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_update_with_all_params_overload_2(self, client: Kernel) -> None: + item = client.vaults.items.update( + key="x", + id_or_name="id_or_name", + spec={ + "description": "description", + "fields": {"foo": {"value": "value"}}, + }, + type="credential", + version=1, + expected_item_id="x", + ) + assert_matches_type(VaultItem, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_update_overload_2(self, client: Kernel) -> None: + response = client.vaults.items.with_raw_response.update( + key="x", + id_or_name="id_or_name", + spec={}, + type="credential", + version=1, + ) + + 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"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_update_overload_2(self, client: Kernel) -> None: + with client.vaults.items.with_streaming_response.update( + key="x", + id_or_name="id_or_name", + spec={}, + type="credential", + version=1, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + item = response.parse() + assert_matches_type(VaultItem, 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_update_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.update( + key="x", + id_or_name="", + spec={}, + type="credential", + version=1, + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `key` but received ''"): + client.vaults.items.with_raw_response.update( + key="", + id_or_name="id_or_name", + spec={}, + type="credential", + version=1, + ) + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_list(self, client: Kernel) -> None: @@ -451,6 +535,63 @@ def test_path_params_perform_operation_overload_1(self, client: Kernel) -> None: @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", + type="collect", + ) + 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", + type="collect", + ) + + 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", + type="collect", + ) 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="", + type="collect", + ) + + 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", + type="collect", + ) + + @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", @@ -465,7 +606,7 @@ def test_method_perform_operation_overload_2(self, client: Kernel) -> None: @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_raw_response_perform_operation_overload_2(self, client: Kernel) -> None: + 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", @@ -484,7 +625,7 @@ def test_raw_response_perform_operation_overload_2(self, client: Kernel) -> None @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_streaming_response_perform_operation_overload_2(self, client: Kernel) -> None: + 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", @@ -505,7 +646,7 @@ def test_streaming_response_perform_operation_overload_2(self, client: Kernel) - @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_path_params_perform_operation_overload_2(self, client: Kernel) -> None: + 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", @@ -532,7 +673,7 @@ def test_path_params_perform_operation_overload_2(self, client: Kernel) -> None: @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_method_perform_operation_overload_3(self, client: Kernel) -> None: + def test_method_perform_operation_overload_4(self, client: Kernel) -> None: item = client.vaults.items.perform_operation( key="key", id_or_name="id_or_name", @@ -555,14 +696,13 @@ def test_method_perform_operation_overload_3(self, client: Kernel) -> None: "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: + def test_method_perform_operation_with_all_params_overload_4(self, client: Kernel) -> None: item = client.vaults.items.perform_operation( key="key", id_or_name="id_or_name", @@ -571,29 +711,33 @@ def test_method_perform_operation_with_all_params_overload_3(self, client: Kerne { "field": "number", "selector": "#card-number", + "format": "MM/YY", }, { "field": "exp_month", "selector": "#expiry-month", + "format": "MM/YY", }, { "field": "exp_year", "selector": "#expiry-year", + "format": "MM/YY", }, { "field": "cvc", "selector": "#security-code", + "format": "MM/YY", }, ], - page_url="https://shop.example/checkout", type="fill", + page_url="https://shop.example/checkout", 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: + def test_raw_response_perform_operation_overload_4(self, client: Kernel) -> None: response = client.vaults.items.with_raw_response.perform_operation( key="key", id_or_name="id_or_name", @@ -616,7 +760,6 @@ def test_raw_response_perform_operation_overload_3(self, client: Kernel) -> None "selector": "#security-code", }, ], - page_url="https://shop.example/checkout", type="fill", ) @@ -627,7 +770,7 @@ def test_raw_response_perform_operation_overload_3(self, client: Kernel) -> None @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_streaming_response_perform_operation_overload_3(self, client: Kernel) -> None: + def test_streaming_response_perform_operation_overload_4(self, client: Kernel) -> None: with client.vaults.items.with_streaming_response.perform_operation( key="key", id_or_name="id_or_name", @@ -650,7 +793,6 @@ def test_streaming_response_perform_operation_overload_3(self, client: Kernel) - "selector": "#security-code", }, ], - page_url="https://shop.example/checkout", type="fill", ) as response: assert not response.is_closed @@ -663,7 +805,7 @@ def test_streaming_response_perform_operation_overload_3(self, client: Kernel) - @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_path_params_perform_operation_overload_3(self, client: Kernel) -> None: + def test_path_params_perform_operation_overload_4(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", @@ -687,7 +829,6 @@ def test_path_params_perform_operation_overload_3(self, client: Kernel) -> None: "selector": "#security-code", }, ], - page_url="https://shop.example/checkout", type="fill", ) @@ -714,7 +855,6 @@ def test_path_params_perform_operation_overload_3(self, client: Kernel) -> None: "selector": "#security-code", }, ], - page_url="https://shop.example/checkout", type="fill", ) @@ -982,6 +1122,89 @@ def test_path_params_upsert_overload_2(self, client: Kernel) -> None: type="card", ) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_upsert_overload_3(self, client: Kernel) -> None: + item = client.vaults.items.upsert( + key="x", + id_or_name="id_or_name", + spec={"fields": {"foo": {"type": "text"}}}, + type="credential", + ) + assert_matches_type(VaultItem, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_upsert_with_all_params_overload_3(self, client: Kernel) -> None: + item = client.vaults.items.upsert( + key="x", + id_or_name="id_or_name", + spec={ + "fields": { + "foo": { + "type": "text", + "required": True, + "sensitive": True, + "value": "x", + } + }, + "description": "description", + }, + type="credential", + ) + assert_matches_type(VaultItem, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_upsert_overload_3(self, client: Kernel) -> None: + response = client.vaults.items.with_raw_response.upsert( + key="x", + id_or_name="id_or_name", + spec={"fields": {"foo": {"type": "text"}}}, + type="credential", + ) + + 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"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_upsert_overload_3(self, client: Kernel) -> None: + with client.vaults.items.with_streaming_response.upsert( + key="x", + id_or_name="id_or_name", + spec={"fields": {"foo": {"type": "text"}}}, + type="credential", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + item = response.parse() + assert_matches_type(VaultItem, 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_upsert_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.upsert( + key="x", + id_or_name="", + spec={"fields": {"foo": {"type": "text"}}}, + type="credential", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `key` but received ''"): + client.vaults.items.with_raw_response.upsert( + key="", + id_or_name="id_or_name", + spec={"fields": {"foo": {"type": "text"}}}, + type="credential", + ) + class TestAsyncItems: parametrize = pytest.mark.parametrize( @@ -1053,38 +1276,38 @@ async def test_path_params_retrieve(self, async_client: AsyncKernel) -> None: @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_method_update(self, async_client: AsyncKernel) -> None: + async def test_method_update_overload_1(self, async_client: AsyncKernel) -> None: item = await async_client.vaults.items.update( key="x", id_or_name="id_or_name", spec={ - "amount": 3000, - "context": "The order total changed to USD 30.00 including shipping and taxes for one notebook. Update this unapproved request rather than creating a second payment.", - "currency": "usd", - "merchant_name": "Example Store", - "merchant_url": "https://store.example.com", - "payment_method_id": "pm_example", + "amount": 1, + "context": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "currency": "bFx", + "merchant_name": "x", + "merchant_url": "https://example.com", + "payment_method_id": "x", "provider": "link", - "wallet": "link-wallet", + "wallet": "wallet", }, ) assert_matches_type(VaultItem, item, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_method_update_with_all_params(self, async_client: AsyncKernel) -> None: + async def test_method_update_with_all_params_overload_1(self, async_client: AsyncKernel) -> None: item = await async_client.vaults.items.update( key="x", id_or_name="id_or_name", spec={ - "amount": 3000, - "context": "The order total changed to USD 30.00 including shipping and taxes for one notebook. Update this unapproved request rather than creating a second payment.", - "currency": "usd", - "merchant_name": "Example Store", - "merchant_url": "https://store.example.com", - "payment_method_id": "pm_example", + "amount": 1, + "context": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "currency": "bFx", + "merchant_name": "x", + "merchant_url": "https://example.com", + "payment_method_id": "x", "provider": "link", - "wallet": "link-wallet", + "wallet": "wallet", "expires_at": 0, "line_items": [ { @@ -1114,24 +1337,25 @@ async def test_method_update_with_all_params(self, async_client: AsyncKernel) -> } ], }, + type="card", ) assert_matches_type(VaultItem, item, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_raw_response_update(self, async_client: AsyncKernel) -> None: + async def test_raw_response_update_overload_1(self, async_client: AsyncKernel) -> None: response = await async_client.vaults.items.with_raw_response.update( key="x", id_or_name="id_or_name", spec={ - "amount": 3000, - "context": "The order total changed to USD 30.00 including shipping and taxes for one notebook. Update this unapproved request rather than creating a second payment.", - "currency": "usd", - "merchant_name": "Example Store", - "merchant_url": "https://store.example.com", - "payment_method_id": "pm_example", + "amount": 1, + "context": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "currency": "bFx", + "merchant_name": "x", + "merchant_url": "https://example.com", + "payment_method_id": "x", "provider": "link", - "wallet": "link-wallet", + "wallet": "wallet", }, ) @@ -1142,19 +1366,19 @@ async def test_raw_response_update(self, async_client: AsyncKernel) -> None: @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_streaming_response_update(self, async_client: AsyncKernel) -> None: + async def test_streaming_response_update_overload_1(self, async_client: AsyncKernel) -> None: async with async_client.vaults.items.with_streaming_response.update( key="x", id_or_name="id_or_name", spec={ - "amount": 3000, - "context": "The order total changed to USD 30.00 including shipping and taxes for one notebook. Update this unapproved request rather than creating a second payment.", - "currency": "usd", - "merchant_name": "Example Store", - "merchant_url": "https://store.example.com", - "payment_method_id": "pm_example", + "amount": 1, + "context": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "currency": "bFx", + "merchant_name": "x", + "merchant_url": "https://example.com", + "payment_method_id": "x", "provider": "link", - "wallet": "link-wallet", + "wallet": "wallet", }, ) as response: assert not response.is_closed @@ -1167,20 +1391,20 @@ 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: + async def test_path_params_update_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.update( key="x", id_or_name="", spec={ - "amount": 3000, - "context": "The order total changed to USD 30.00 including shipping and taxes for one notebook. Update this unapproved request rather than creating a second payment.", - "currency": "usd", - "merchant_name": "Example Store", - "merchant_url": "https://store.example.com", - "payment_method_id": "pm_example", + "amount": 1, + "context": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "currency": "bFx", + "merchant_name": "x", + "merchant_url": "https://example.com", + "payment_method_id": "x", "provider": "link", - "wallet": "link-wallet", + "wallet": "wallet", }, ) @@ -1189,17 +1413,100 @@ async def test_path_params_update(self, async_client: AsyncKernel) -> None: key="", id_or_name="id_or_name", spec={ - "amount": 3000, - "context": "The order total changed to USD 30.00 including shipping and taxes for one notebook. Update this unapproved request rather than creating a second payment.", - "currency": "usd", - "merchant_name": "Example Store", - "merchant_url": "https://store.example.com", - "payment_method_id": "pm_example", + "amount": 1, + "context": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "currency": "bFx", + "merchant_name": "x", + "merchant_url": "https://example.com", + "payment_method_id": "x", "provider": "link", - "wallet": "link-wallet", + "wallet": "wallet", }, ) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_update_overload_2(self, async_client: AsyncKernel) -> None: + item = await async_client.vaults.items.update( + key="x", + id_or_name="id_or_name", + spec={}, + type="credential", + version=1, + ) + assert_matches_type(VaultItem, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_update_with_all_params_overload_2(self, async_client: AsyncKernel) -> None: + item = await async_client.vaults.items.update( + key="x", + id_or_name="id_or_name", + spec={ + "description": "description", + "fields": {"foo": {"value": "value"}}, + }, + type="credential", + version=1, + expected_item_id="x", + ) + assert_matches_type(VaultItem, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_update_overload_2(self, async_client: AsyncKernel) -> None: + response = await async_client.vaults.items.with_raw_response.update( + key="x", + id_or_name="id_or_name", + spec={}, + type="credential", + version=1, + ) + + 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"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_update_overload_2(self, async_client: AsyncKernel) -> None: + async with async_client.vaults.items.with_streaming_response.update( + key="x", + id_or_name="id_or_name", + spec={}, + type="credential", + version=1, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + item = await response.parse() + assert_matches_type(VaultItem, 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_update_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.update( + key="x", + id_or_name="", + spec={}, + type="credential", + version=1, + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `key` but received ''"): + await async_client.vaults.items.with_raw_response.update( + key="", + id_or_name="id_or_name", + spec={}, + type="credential", + version=1, + ) + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncKernel) -> None: @@ -1417,6 +1724,63 @@ async def test_path_params_perform_operation_overload_1(self, async_client: Asyn @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", + type="collect", + ) + 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", + type="collect", + ) + + 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", + type="collect", + ) 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="", + type="collect", + ) + + 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", + type="collect", + ) + + @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", @@ -1431,7 +1795,7 @@ async def test_method_perform_operation_overload_2(self, async_client: AsyncKern @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_raw_response_perform_operation_overload_2(self, async_client: AsyncKernel) -> None: + 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", @@ -1450,7 +1814,7 @@ async def test_raw_response_perform_operation_overload_2(self, async_client: Asy @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 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", @@ -1471,7 +1835,7 @@ async def test_streaming_response_perform_operation_overload_2(self, async_clien @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_path_params_perform_operation_overload_2(self, async_client: AsyncKernel) -> None: + 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", @@ -1498,7 +1862,7 @@ async def test_path_params_perform_operation_overload_2(self, async_client: Asyn @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_method_perform_operation_overload_3(self, async_client: AsyncKernel) -> None: + async def test_method_perform_operation_overload_4(self, async_client: AsyncKernel) -> None: item = await async_client.vaults.items.perform_operation( key="key", id_or_name="id_or_name", @@ -1521,14 +1885,13 @@ async def test_method_perform_operation_overload_3(self, async_client: AsyncKern "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: + async def test_method_perform_operation_with_all_params_overload_4(self, async_client: AsyncKernel) -> None: item = await async_client.vaults.items.perform_operation( key="key", id_or_name="id_or_name", @@ -1537,29 +1900,33 @@ async def test_method_perform_operation_with_all_params_overload_3(self, async_c { "field": "number", "selector": "#card-number", + "format": "MM/YY", }, { "field": "exp_month", "selector": "#expiry-month", + "format": "MM/YY", }, { "field": "exp_year", "selector": "#expiry-year", + "format": "MM/YY", }, { "field": "cvc", "selector": "#security-code", + "format": "MM/YY", }, ], - page_url="https://shop.example/checkout", type="fill", + page_url="https://shop.example/checkout", 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: + async def test_raw_response_perform_operation_overload_4(self, async_client: AsyncKernel) -> None: response = await async_client.vaults.items.with_raw_response.perform_operation( key="key", id_or_name="id_or_name", @@ -1582,7 +1949,6 @@ async def test_raw_response_perform_operation_overload_3(self, async_client: Asy "selector": "#security-code", }, ], - page_url="https://shop.example/checkout", type="fill", ) @@ -1593,7 +1959,7 @@ async def test_raw_response_perform_operation_overload_3(self, async_client: Asy @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 def test_streaming_response_perform_operation_overload_4(self, async_client: AsyncKernel) -> None: async with async_client.vaults.items.with_streaming_response.perform_operation( key="key", id_or_name="id_or_name", @@ -1616,7 +1982,6 @@ async def test_streaming_response_perform_operation_overload_3(self, async_clien "selector": "#security-code", }, ], - page_url="https://shop.example/checkout", type="fill", ) as response: assert not response.is_closed @@ -1629,7 +1994,7 @@ async def test_streaming_response_perform_operation_overload_3(self, async_clien @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_path_params_perform_operation_overload_3(self, async_client: AsyncKernel) -> None: + async def test_path_params_perform_operation_overload_4(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", @@ -1653,7 +2018,6 @@ async def test_path_params_perform_operation_overload_3(self, async_client: Asyn "selector": "#security-code", }, ], - page_url="https://shop.example/checkout", type="fill", ) @@ -1680,7 +2044,6 @@ async def test_path_params_perform_operation_overload_3(self, async_client: Asyn "selector": "#security-code", }, ], - page_url="https://shop.example/checkout", type="fill", ) @@ -1947,3 +2310,86 @@ async def test_path_params_upsert_overload_2(self, async_client: AsyncKernel) -> }, type="card", ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_upsert_overload_3(self, async_client: AsyncKernel) -> None: + item = await async_client.vaults.items.upsert( + key="x", + id_or_name="id_or_name", + spec={"fields": {"foo": {"type": "text"}}}, + type="credential", + ) + assert_matches_type(VaultItem, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_upsert_with_all_params_overload_3(self, async_client: AsyncKernel) -> None: + item = await async_client.vaults.items.upsert( + key="x", + id_or_name="id_or_name", + spec={ + "fields": { + "foo": { + "type": "text", + "required": True, + "sensitive": True, + "value": "x", + } + }, + "description": "description", + }, + type="credential", + ) + assert_matches_type(VaultItem, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_upsert_overload_3(self, async_client: AsyncKernel) -> None: + response = await async_client.vaults.items.with_raw_response.upsert( + key="x", + id_or_name="id_or_name", + spec={"fields": {"foo": {"type": "text"}}}, + type="credential", + ) + + 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"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_upsert_overload_3(self, async_client: AsyncKernel) -> None: + async with async_client.vaults.items.with_streaming_response.upsert( + key="x", + id_or_name="id_or_name", + spec={"fields": {"foo": {"type": "text"}}}, + type="credential", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + item = await response.parse() + assert_matches_type(VaultItem, 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_upsert_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.upsert( + key="x", + id_or_name="", + spec={"fields": {"foo": {"type": "text"}}}, + type="credential", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `key` but received ''"): + await async_client.vaults.items.with_raw_response.upsert( + key="", + id_or_name="id_or_name", + spec={"fields": {"foo": {"type": "text"}}}, + type="credential", + ) From c11035847c9f6dd23b7cdd5efa0a8b188d5a850a Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 23:56:19 +0000 Subject: [PATCH 2/4] feat: Expose managed auth field input modes Stainless-Generated-From: 641e17ff11ca542895bb45f2e6c06d75f136aa5d --- src/kernel/types/auth/connection_follow_response.py | 3 +++ src/kernel/types/auth/managed_auth.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/kernel/types/auth/connection_follow_response.py b/src/kernel/types/auth/connection_follow_response.py index ff6dcf0b..c7690794 100644 --- a/src/kernel/types/auth/connection_follow_response.py +++ b/src/kernel/types/auth/connection_follow_response.py @@ -109,6 +109,9 @@ class ManagedAuthStateEventField(BaseModel): hint: Optional[str] = None """Context shown near the field, including a masked code destination.""" + input_mode: Optional[Literal["text", "email", "tel", "numeric"]] = None + """Virtual keyboard hint, independent of field type and browser validation.""" + label: Optional[str] = None """Human-readable label shown to the user.""" diff --git a/src/kernel/types/auth/managed_auth.py b/src/kernel/types/auth/managed_auth.py index a358197e..62fa7852 100644 --- a/src/kernel/types/auth/managed_auth.py +++ b/src/kernel/types/auth/managed_auth.py @@ -218,6 +218,9 @@ class Field(BaseModel): hint: Optional[str] = None """Context shown near the field, including a masked code destination.""" + input_mode: Optional[Literal["text", "email", "tel", "numeric"]] = None + """Virtual keyboard hint, independent of field type and browser validation.""" + label: Optional[str] = None """Human-readable label shown to the user.""" From 0737fe974b15c6e422c8722d1d76c97c811533d7 Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 00:17:29 +0000 Subject: [PATCH 3/4] feat: Return working config registry configurations Stainless-Generated-From: fafe851cda2bec958cc113405ced9ecd6d1c35e3 --- src/kernel/types/config_registry_response.py | 9 ++++++++- src/kernel/types/lookup_response.py | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/kernel/types/config_registry_response.py b/src/kernel/types/config_registry_response.py index dba10b9e..cad09105 100644 --- a/src/kernel/types/config_registry_response.py +++ b/src/kernel/types/config_registry_response.py @@ -1,11 +1,12 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import List, Optional from typing_extensions import Literal from .target import Target from .._models import BaseModel from .analysis import Analysis +from .recommendation import Recommendation from .recommendation_result import RecommendationResult __all__ = ["ConfigRegistryResponse"] @@ -23,6 +24,12 @@ class ConfigRegistryResponse(BaseModel): target: Target + working_configurations: List[Recommendation] + """ + Working configurations for the target, ordered with the recommended + configuration first. + """ + guidance: Optional[str] = None """Short advisory markdown to facilitate navigating this target. diff --git a/src/kernel/types/lookup_response.py b/src/kernel/types/lookup_response.py index d717b8cf..c92e163e 100644 --- a/src/kernel/types/lookup_response.py +++ b/src/kernel/types/lookup_response.py @@ -1,9 +1,10 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import List, Optional from .target import Target from .._models import BaseModel +from .recommendation import Recommendation from .recommendation_result import RecommendationResult __all__ = ["LookupResponse"] @@ -15,6 +16,12 @@ class LookupResponse(BaseModel): target: Target + working_configurations: List[Recommendation] + """ + Working configurations for the target, ordered with the recommended + configuration first. + """ + guidance: Optional[str] = None """Short advisory markdown to facilitate navigating this target. From 15dd33c71b5897fc0ffe60150cb4af9b31c97805 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 00:47:02 +0000 Subject: [PATCH 4/4] release: 0.103.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 f70d9978..508a7081 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.102.0" + ".": "0.103.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 80c45f21..54221756 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.103.0 (2026-09-15) + +Full Changelog: [v0.102.0...v0.103.0](https://github.com/kernel/kernel-python-sdk/compare/v0.102.0...v0.103.0) + +### Features + +* chore(stlc): seal custom-code tracking files ([4ce4ab9](https://github.com/kernel/kernel-python-sdk/commit/4ce4ab9f533b4f5d9fe420f91f1a0054c0e13c1b)) +* Expose managed auth field input modes ([c110358](https://github.com/kernel/kernel-python-sdk/commit/c11035847c9f6dd23b7cdd5efa0a8b188d5a850a)) +* Return working config registry configurations ([0737fe9](https://github.com/kernel/kernel-python-sdk/commit/0737fe974b15c6e422c8722d1d76c97c811533d7)) + ## [0.102.0](https://github.com/kernel/kernel-python-sdk/compare/v0.101.0...v0.102.0) (2026-09-14) diff --git a/pyproject.toml b/pyproject.toml index 473c0ec0..e8a7f193 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "kernel" -version = "0.102.0" +version = "0.103.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 b5daff26..033ca8e7 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.102.0" # x-release-please-version +__version__ = "0.103.0" # x-release-please-version