From 56c998338b3f885d4d8276e5d038d9612bf0d1fd Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Mon, 2 Nov 2026 23:27:13 +0000 Subject: [PATCH 1/5] feat: Pass through tool_page_unloaded and disable SDK retries for WebMCP invoke failures Stainless-Generated-From: a670dcf1aed08939b4b2790f0506c5d9c9c0fb7f --- .../resources/browsers/webmcp/webmcp.py | 24 ++++++++++++------- .../browsers/webmcp_invoke_tool_params.py | 2 +- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/kernel/resources/browsers/webmcp/webmcp.py b/src/kernel/resources/browsers/webmcp/webmcp.py index 5cab5b8f..cb63d56f 100644 --- a/src/kernel/resources/browsers/webmcp/webmcp.py +++ b/src/kernel/resources/browsers/webmcp/webmcp.py @@ -82,12 +82,16 @@ def invoke_tool( across navigation. Inspect a populated form, obtain any required confirmation, then submit through Playwright or computer interaction without invoking the tool again. If the tab or embedded frame disappears, or the request times out after - invocation begins, the response reports outcome_unknown and the tool is not - retried. CDP-backed custom tool outputs above 240 KiB return an error rather - than a truncated result. + invocation begins, the response reports outcome_unknown. If the tool's page is + reloaded or replaced by a navigation the tool did not start, and the tool never + responds, the response reports tool_page_unloaded once the replacement page + loads, or right away if the tool's tab crashes. Neither failure is retried, and + both set X-Should-Retry to false because the tool may have had side effects. + CDP-backed custom tool outputs above 240 KiB return an error rather than a + truncated result. Args: - input: Tool input, limited to 1 MiB after JSON serialization. + input: Tool input, limited to 4 MiB after JSON serialization. extra_headers: Send extra headers @@ -219,12 +223,16 @@ async def invoke_tool( across navigation. Inspect a populated form, obtain any required confirmation, then submit through Playwright or computer interaction without invoking the tool again. If the tab or embedded frame disappears, or the request times out after - invocation begins, the response reports outcome_unknown and the tool is not - retried. CDP-backed custom tool outputs above 240 KiB return an error rather - than a truncated result. + invocation begins, the response reports outcome_unknown. If the tool's page is + reloaded or replaced by a navigation the tool did not start, and the tool never + responds, the response reports tool_page_unloaded once the replacement page + loads, or right away if the tool's tab crashes. Neither failure is retried, and + both set X-Should-Retry to false because the tool may have had side effects. + CDP-backed custom tool outputs above 240 KiB return an error rather than a + truncated result. Args: - input: Tool input, limited to 1 MiB after JSON serialization. + input: Tool input, limited to 4 MiB after JSON serialization. extra_headers: Send extra headers diff --git a/src/kernel/types/browsers/webmcp_invoke_tool_params.py b/src/kernel/types/browsers/webmcp_invoke_tool_params.py index 99f0f25f..a7ef8816 100644 --- a/src/kernel/types/browsers/webmcp_invoke_tool_params.py +++ b/src/kernel/types/browsers/webmcp_invoke_tool_params.py @@ -10,7 +10,7 @@ class WebmcpInvokeToolParams(TypedDict, total=False): input: Required[Dict[str, object]] - """Tool input, limited to 1 MiB after JSON serialization.""" + """Tool input, limited to 4 MiB after JSON serialization.""" tool_ref: Required[str] From e5454cb02a33599e81ec5ca3c764e2522ad2cd76 Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Tue, 3 Nov 2026 17:04:37 +0000 Subject: [PATCH 2/5] feat: Drop Kernel's origin gate from 1Password fill Stainless-Generated-From: 1f6da93b3427376e6526a1c28012a96279a47615 --- src/kernel/resources/vaults/items.py | 8 ++++---- src/kernel/types/vaults/item_perform_operation_params.py | 3 ++- ...ne_password_fill_vault_item_operation_request_param.py | 5 +++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/kernel/resources/vaults/items.py b/src/kernel/resources/vaults/items.py index c89f26b0..775dd63c 100644 --- a/src/kernel/resources/vaults/items.py +++ b/src/kernel/resources/vaults/items.py @@ -1280,8 +1280,8 @@ def perform_operation( page_url: Exact current top-level page URL. Must match exactly one open page in the browser unless target_id is supplied. - entry_id: ID of an approved request entry. Required when several approved entries have the - page's origin. + entry_id: ID of an approved request entry. Required when several entries are approved and + the page's origin does not match exactly one of them. target_id: CDP target ID of the page to fill, such as tab.target_id from playwright.execute. Use it when several open pages have page_url. The page's @@ -3285,8 +3285,8 @@ async def perform_operation( page_url: Exact current top-level page URL. Must match exactly one open page in the browser unless target_id is supplied. - entry_id: ID of an approved request entry. Required when several approved entries have the - page's origin. + entry_id: ID of an approved request entry. Required when several entries are approved and + the page's origin does not match exactly one of them. target_id: CDP target ID of the page to fill, such as tab.target_id from playwright.execute. Use it when several open pages have page_url. The page's diff --git a/src/kernel/types/vaults/item_perform_operation_params.py b/src/kernel/types/vaults/item_perform_operation_params.py index 722c5daa..776d78b9 100644 --- a/src/kernel/types/vaults/item_perform_operation_params.py +++ b/src/kernel/types/vaults/item_perform_operation_params.py @@ -235,7 +235,8 @@ class OnePasswordFillVaultItemOperationRequest(TypedDict, total=False): entry_id: str """ID of an approved request entry. - Required when several approved entries have the page's origin. + Required when several entries are approved and the page's origin does not match + exactly one of them. """ target_id: str diff --git a/src/kernel/types/vaults/one_password_fill_vault_item_operation_request_param.py b/src/kernel/types/vaults/one_password_fill_vault_item_operation_request_param.py index a66cce42..1e8e1212 100644 --- a/src/kernel/types/vaults/one_password_fill_vault_item_operation_request_param.py +++ b/src/kernel/types/vaults/one_password_fill_vault_item_operation_request_param.py @@ -10,7 +10,7 @@ class OnePasswordFillVaultItemOperationRequestParam(TypedDict, total=False): """Fill and submit an approved 1Password login in the selected browser page. - The page must share the selected entry's login origin. Supply entry_id when more than one approved entry matches the page origin, and target_id when more than one open page has page_url. The extension selects fields; callers cannot supply selectors or secret values. Submission does not confirm website authentication. + The 1Password extension decides whether the approved login matches the page and refuses pages that don't. Supply entry_id when several entries are approved and the page origin does not identify exactly one of them, and target_id when more than one open page has page_url. The extension selects fields; callers cannot supply selectors or secret values. Submission does not confirm website authentication. """ browser_id: Required[str] @@ -27,7 +27,8 @@ class OnePasswordFillVaultItemOperationRequestParam(TypedDict, total=False): entry_id: str """ID of an approved request entry. - Required when several approved entries have the page's origin. + Required when several entries are approved and the page's origin does not match + exactly one of them. """ target_id: str From c11d2644383af967ddcb8b892b325ea4421eb956 Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Wed, 4 Nov 2026 04:07:52 +0000 Subject: [PATCH 3/5] feat: Add single-use Kernel cards to vaults Stainless-Generated-From: 943252a6920fb068bc2f593bc714757d75bd78fc --- api.md | 2 ++ src/kernel/types/vaults/__init__.py | 4 +++ src/kernel/types/vaults/kernel_card_state.py | 14 +++++--- .../vaults/kernel_card_vault_item_spec.py | 28 +++++++++++++-- .../kernel_card_vault_item_spec_param.py | 33 +++++++++++++++-- .../types/vaults/kernel_single_use_card.py | 19 ++++++++++ .../vaults/kernel_single_use_card_param.py | 36 +++++++++++++++++++ .../vaults/single_use_card_billing_address.py | 24 +++++++++++++ .../single_use_card_billing_address_param.py | 24 +++++++++++++ 9 files changed, 175 insertions(+), 9 deletions(-) create mode 100644 src/kernel/types/vaults/kernel_single_use_card.py create mode 100644 src/kernel/types/vaults/kernel_single_use_card_param.py create mode 100644 src/kernel/types/vaults/single_use_card_billing_address.py create mode 100644 src/kernel/types/vaults/single_use_card_billing_address_param.py diff --git a/api.md b/api.md index 06153f73..75792aba 100644 --- a/api.md +++ b/api.md @@ -608,6 +608,7 @@ from kernel.types.vaults import ( KernelCredentialVaultItemSpecInput, KernelCredentialVaultItemState, KernelPaymentRequestVaultItemSpec, + KernelSingleUseCard, KernelWalletState, KernelWalletVaultItemSpec, LinkShippingAddress, @@ -639,6 +640,7 @@ from kernel.types.vaults import ( ShopPayCardVaultItemSpec, ShopPayWalletState, ShopPayWalletVaultItemSpec, + SingleUseCardBillingAddress, VaultCardAliases, VaultCardFillField, VaultCheckoutContext, diff --git a/src/kernel/types/vaults/__init__.py b/src/kernel/types/vaults/__init__.py index fa731977..acf77e0f 100644 --- a/src/kernel/types/vaults/__init__.py +++ b/src/kernel/types/vaults/__init__.py @@ -22,6 +22,7 @@ from .credential_vault_item import CredentialVaultItem as CredentialVaultItem from .link_shipping_address import LinkShippingAddress as LinkShippingAddress from .shop_pay_wallet_state import ShopPayWalletState as ShopPayWalletState +from .kernel_single_use_card import KernelSingleUseCard as KernelSingleUseCard 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 @@ -37,6 +38,7 @@ from .agentcard_prepared_processor import AgentcardPreparedProcessor as AgentcardPreparedProcessor from .credential_collection_action import CredentialCollectionAction as CredentialCollectionAction from .credential_vault_field_state import CredentialVaultFieldState as CredentialVaultFieldState +from .kernel_single_use_card_param import KernelSingleUseCardParam as KernelSingleUseCardParam from .vault_checkout_context_param import VaultCheckoutContextParam as VaultCheckoutContextParam from .credential_account_vault_item import CredentialAccountVaultItem as CredentialAccountVaultItem from .item_perform_operation_params import ItemPerformOperationParams as ItemPerformOperationParams @@ -46,6 +48,7 @@ from .agentcard_checkout_preparation import AgentcardCheckoutPreparation as AgentcardCheckoutPreparation from .payment_request_vault_item_spec import PaymentRequestVaultItemSpec as PaymentRequestVaultItemSpec from .shop_pay_wallet_vault_item_spec import ShopPayWalletVaultItemSpec as ShopPayWalletVaultItemSpec +from .single_use_card_billing_address import SingleUseCardBillingAddress as SingleUseCardBillingAddress from .agentcard_checkout_authorization import AgentcardCheckoutAuthorization as AgentcardCheckoutAuthorization from .fill_vault_item_operation_result import FillVaultItemOperationResult as FillVaultItemOperationResult from .payment_request_vault_item_state import PaymentRequestVaultItemState as PaymentRequestVaultItemState @@ -66,6 +69,7 @@ ) from .payment_request_vault_item_spec_param import PaymentRequestVaultItemSpecParam as PaymentRequestVaultItemSpecParam from .shop_pay_wallet_vault_item_spec_param import ShopPayWalletVaultItemSpecParam as ShopPayWalletVaultItemSpecParam +from .single_use_card_billing_address_param import SingleUseCardBillingAddressParam as SingleUseCardBillingAddressParam from .credential_vault_item_spec_input_param import ( CredentialVaultItemSpecInputParam as CredentialVaultItemSpecInputParam, ) diff --git a/src/kernel/types/vaults/kernel_card_state.py b/src/kernel/types/vaults/kernel_card_state.py index 0838182c..ca789220 100644 --- a/src/kernel/types/vaults/kernel_card_state.py +++ b/src/kernel/types/vaults/kernel_card_state.py @@ -32,13 +32,18 @@ def __getattr__(self, attr: str) -> str: ... class KernelCardState(BaseModel): - """A ready Kernel card supports authorize, fill, then submit checkout. - - Card details are delivered for a few minutes after fill; submit promptly. Repeated fill is safe and never submits checkout. Visa purchases require a spend_approval action before details are issued; Mastercard purchases need no hosted approval. masks.last4 is the enrolled card's last four digits; masks.token_last4, when present, is the token's last four digits shown to the merchant. Kernel does not observe whether the merchant charged the card. + """ + A ready Kernel card funded by a payment method supports authorize, fill, then submit checkout. A single-use card (source single_use_card) is ready on creation, supports only fill, and is expired from expires_at; Kernel deletes its stored details shortly after expires_at. It has no payment_method_id or funding_status. Card details are delivered for a few minutes after fill; submit promptly. Repeated fill is safe and never submits checkout. Visa purchases require a spend_approval action before details are issued; Mastercard purchases need no hosted approval. masks.last4 is the enrolled card's last four digits; masks.token_last4, when present, is the token's last four digits shown to the merchant. Kernel does not observe whether the merchant charged the card. """ provider: Literal["kernel"] + source: Literal["payment_method", "single_use_card"] + """ + payment_method for a purchase funded by a wallet payment method; single_use_card + for a card whose details were supplied with single_use_card. + """ + status: Literal[ "requested", "pending_authorization", "ready", "consumed", "expired", "declined", "recovery_required" ] @@ -65,7 +70,8 @@ class KernelCardState(BaseModel): domains: Optional[List[str]] = None """Informational registrable domain. - Fill is locked to merchant_url's exact origin. + Fill is locked to merchant_url's exact origin, except that a single-use card + fills on any https page of this registrable domain. """ funding_status: Optional[Literal["available", "unavailable", "removed"]] = None diff --git a/src/kernel/types/vaults/kernel_card_vault_item_spec.py b/src/kernel/types/vaults/kernel_card_vault_item_spec.py index 525321d6..17a9d625 100644 --- a/src/kernel/types/vaults/kernel_card_vault_item_spec.py +++ b/src/kernel/types/vaults/kernel_card_vault_item_spec.py @@ -1,9 +1,11 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional +from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel +from .kernel_single_use_card import KernelSingleUseCard __all__ = ["KernelCardVaultItemSpec"] @@ -11,7 +13,7 @@ class KernelCardVaultItemSpec(BaseModel): """One live purchase with a Kernel-enrolled card. - Authorization obtains an agentic network token number, expiry and one-time 3-digit code. They are stored encrypted for the fill operation, which types them only on merchant_url's origin; the merchant's own checkout submits the payment. The one-time code is valid until the item's expires_at; fill and submit checkout before then. Mastercard purchases need no cardholder approval. A Visa purchase returns a spend_approval action: the cardholder approves it with a Visa passkey, and Kernel registers a Visa intent for one transaction up to the amount before issuing the code. For supported cards whose network cannot be enrolled for agentic tokens, authorize returns pending_authorization with a spend_approval action to collect a fresh security code in a hosted field. Once collected, the card is ready and can be filled before its expires_at, by default five minutes after collection. An expired card requires a new purchase. Fill types those details on merchant_url's origin. The amount, merchant_url and merchant_country are recorded but do not restrict payment requests sent by the browser. Card updates are not supported; delete and create a new item instead. + Authorization obtains an agentic network token number, expiry and one-time 3-digit code. They are stored encrypted for the fill operation, which types them only on merchant_url's origin; the merchant's own checkout submits the payment. The one-time code is valid until the item's expires_at; fill and submit checkout before then. Mastercard purchases need no cardholder approval. A Visa purchase returns a spend_approval action: the cardholder approves it with a Visa passkey, and Kernel registers a Visa intent for one transaction up to the amount before issuing the code. For supported cards whose network cannot be enrolled for agentic tokens, authorize returns pending_authorization with a spend_approval action to collect a fresh security code in a hosted field. Once collected, the card is ready and can be filled before its expires_at, by default five minutes after collection. An expired card requires a new purchase. Fill types those details on merchant_url's origin. The amount, merchant_url and merchant_country are recorded but do not restrict payment requests sent by the browser. Card updates are not supported; delete and create a new item instead. With single_use_card the item is instead a single-use card whose details the caller supplies, such as a virtual card from its issuer. It is not funded by a payment method, is ready on creation, and supports only fill: Kernel stores the details encrypted, never returns them, and fills them on any https page whose registrable domain (state.domains) is merchant_url's, including its subdomains. Fill does not consume it. From expires_at it is expired and can no longer be filled; Kernel deletes its stored details shortly after expires_at. Deleting the card, its wallet or its vault deletes them immediately. amount, currency and the merchant fields are recorded but not enforced. Repeating the identical request returns the existing card; any other request with the same key returns 409, as does repeating it once the expired card's details have been deleted. """ amount: int @@ -37,6 +39,17 @@ class KernelCardVaultItemSpec(BaseModel): wallet: str """Key of the Kernel wallet item whose enrolled card pays.""" + expires_at: Optional[datetime] = None + """Single-use cards only. + + From this time the card is expired and can no longer be filled; Kernel deletes + its stored details shortly after. Deleting the card, its wallet or its vault + deletes them immediately. Defaults to 2 hours after creation, capped at the end + of the card's expiry month; a requested time must be in the future, at most 24 + hours after creation, and not after the end of the card's expiry month. Returned + in UTC. + """ + merchant_category: Optional[str] = None """Actual merchant category when known. Omit rather than invent a category.""" @@ -59,5 +72,16 @@ class KernelCardVaultItemSpec(BaseModel): Omission selects the wallet's default card, ID card, for compatibility with existing requests; it never falls back to another method, and a wallet without a default card requires the ID. The purchase is bound to the method's enrolled - card and network token, so later operations cannot switch funding. + card and network token, so later operations cannot switch funding. Not allowed + with single_use_card. + """ + + single_use_card: Optional[KernelSingleUseCard] = None + """ + Details of a card the caller supplies, such as a single-use virtual card from + its issuer. Mutually exclusive with payment_method_id. The card details are + stored encrypted for fill and never returned; reads return only reference. + Kernel deletes them shortly after the card's expires_at; deleting the card, its + wallet or its vault deletes them immediately. A card whose expiry month has + ended is rejected. """ diff --git a/src/kernel/types/vaults/kernel_card_vault_item_spec_param.py b/src/kernel/types/vaults/kernel_card_vault_item_spec_param.py index a540a547..58aed4aa 100644 --- a/src/kernel/types/vaults/kernel_card_vault_item_spec_param.py +++ b/src/kernel/types/vaults/kernel_card_vault_item_spec_param.py @@ -2,7 +2,12 @@ from __future__ import annotations -from typing_extensions import Literal, Required, TypedDict +from typing import Union +from datetime import datetime +from typing_extensions import Literal, Required, Annotated, TypedDict + +from ..._utils import PropertyInfo +from .kernel_single_use_card_param import KernelSingleUseCardParam __all__ = ["KernelCardVaultItemSpecParam"] @@ -10,7 +15,7 @@ class KernelCardVaultItemSpecParam(TypedDict, total=False): """One live purchase with a Kernel-enrolled card. - Authorization obtains an agentic network token number, expiry and one-time 3-digit code. They are stored encrypted for the fill operation, which types them only on merchant_url's origin; the merchant's own checkout submits the payment. The one-time code is valid until the item's expires_at; fill and submit checkout before then. Mastercard purchases need no cardholder approval. A Visa purchase returns a spend_approval action: the cardholder approves it with a Visa passkey, and Kernel registers a Visa intent for one transaction up to the amount before issuing the code. For supported cards whose network cannot be enrolled for agentic tokens, authorize returns pending_authorization with a spend_approval action to collect a fresh security code in a hosted field. Once collected, the card is ready and can be filled before its expires_at, by default five minutes after collection. An expired card requires a new purchase. Fill types those details on merchant_url's origin. The amount, merchant_url and merchant_country are recorded but do not restrict payment requests sent by the browser. Card updates are not supported; delete and create a new item instead. + Authorization obtains an agentic network token number, expiry and one-time 3-digit code. They are stored encrypted for the fill operation, which types them only on merchant_url's origin; the merchant's own checkout submits the payment. The one-time code is valid until the item's expires_at; fill and submit checkout before then. Mastercard purchases need no cardholder approval. A Visa purchase returns a spend_approval action: the cardholder approves it with a Visa passkey, and Kernel registers a Visa intent for one transaction up to the amount before issuing the code. For supported cards whose network cannot be enrolled for agentic tokens, authorize returns pending_authorization with a spend_approval action to collect a fresh security code in a hosted field. Once collected, the card is ready and can be filled before its expires_at, by default five minutes after collection. An expired card requires a new purchase. Fill types those details on merchant_url's origin. The amount, merchant_url and merchant_country are recorded but do not restrict payment requests sent by the browser. Card updates are not supported; delete and create a new item instead. With single_use_card the item is instead a single-use card whose details the caller supplies, such as a virtual card from its issuer. It is not funded by a payment method, is ready on creation, and supports only fill: Kernel stores the details encrypted, never returns them, and fills them on any https page whose registrable domain (state.domains) is merchant_url's, including its subdomains. Fill does not consume it. From expires_at it is expired and can no longer be filled; Kernel deletes its stored details shortly after expires_at. Deleting the card, its wallet or its vault deletes them immediately. amount, currency and the merchant fields are recorded but not enforced. Repeating the identical request returns the existing card; any other request with the same key returns 409, as does repeating it once the expired card's details have been deleted. """ amount: Required[int] @@ -36,6 +41,17 @@ class KernelCardVaultItemSpecParam(TypedDict, total=False): wallet: Required[str] """Key of the Kernel wallet item whose enrolled card pays.""" + expires_at: Annotated[Union[str, datetime], PropertyInfo(format="iso8601")] + """Single-use cards only. + + From this time the card is expired and can no longer be filled; Kernel deletes + its stored details shortly after. Deleting the card, its wallet or its vault + deletes them immediately. Defaults to 2 hours after creation, capped at the end + of the card's expiry month; a requested time must be in the future, at most 24 + hours after creation, and not after the end of the card's expiry month. Returned + in UTC. + """ + merchant_category: str """Actual merchant category when known. Omit rather than invent a category.""" @@ -58,5 +74,16 @@ class KernelCardVaultItemSpecParam(TypedDict, total=False): Omission selects the wallet's default card, ID card, for compatibility with existing requests; it never falls back to another method, and a wallet without a default card requires the ID. The purchase is bound to the method's enrolled - card and network token, so later operations cannot switch funding. + card and network token, so later operations cannot switch funding. Not allowed + with single_use_card. + """ + + single_use_card: KernelSingleUseCardParam + """ + Details of a card the caller supplies, such as a single-use virtual card from + its issuer. Mutually exclusive with payment_method_id. The card details are + stored encrypted for fill and never returned; reads return only reference. + Kernel deletes them shortly after the card's expires_at; deleting the card, its + wallet or its vault deletes them immediately. A card whose expiry month has + ended is rejected. """ diff --git a/src/kernel/types/vaults/kernel_single_use_card.py b/src/kernel/types/vaults/kernel_single_use_card.py new file mode 100644 index 00000000..6dcd8212 --- /dev/null +++ b/src/kernel/types/vaults/kernel_single_use_card.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["KernelSingleUseCard"] + + +class KernelSingleUseCard(BaseModel): + """ + Details of a card the caller supplies, such as a single-use virtual card from its issuer. Mutually exclusive with payment_method_id. The card details are stored encrypted for fill and never returned; reads return only reference. Kernel deletes them shortly after the card's expires_at; deleting the card, its wallet or its vault deletes them immediately. A card whose expiry month has ended is rejected. + """ + + reference: Optional[str] = None + """The caller's identifier for the card, such as the issuer's card ID. + + Returned unchanged on reads. + """ diff --git a/src/kernel/types/vaults/kernel_single_use_card_param.py b/src/kernel/types/vaults/kernel_single_use_card_param.py new file mode 100644 index 00000000..5ca4ea73 --- /dev/null +++ b/src/kernel/types/vaults/kernel_single_use_card_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 Required, TypedDict + +from .single_use_card_billing_address_param import SingleUseCardBillingAddressParam + +__all__ = ["KernelSingleUseCardParam"] + + +class KernelSingleUseCardParam(TypedDict, total=False): + """ + Details of a card the caller supplies, such as a single-use virtual card from its issuer. Mutually exclusive with payment_method_id. The card details are stored encrypted for fill and never returned; reads return only reference. Kernel deletes them shortly after the card's expires_at; deleting the card, its wallet or its vault deletes them immediately. A card whose expiry month has ended is rejected. + """ + + cvc: Required[str] + + exp_month: Required[str] + + exp_year: Required[str] + + number: Required[str] + """Card number; must pass the Luhn check.""" + + billing_address: SingleUseCardBillingAddressParam + """Billing address filled with the billing\\__\\** fill fields. Never returned.""" + + name: str + """Cardholder name, filled as billing_name.""" + + reference: str + """The caller's identifier for the card, such as the issuer's card ID. + + Returned unchanged on reads. + """ diff --git a/src/kernel/types/vaults/single_use_card_billing_address.py b/src/kernel/types/vaults/single_use_card_billing_address.py new file mode 100644 index 00000000..a26f9343 --- /dev/null +++ b/src/kernel/types/vaults/single_use_card_billing_address.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from ..._models import BaseModel + +__all__ = ["SingleUseCardBillingAddress"] + + +class SingleUseCardBillingAddress(BaseModel): + """Billing address filled with the billing_* fill fields. Never returned.""" + + city: Optional[str] = None + + country: Optional[str] = None + """ISO 3166-1 alpha-2 code.""" + + line1: Optional[str] = None + + line2: Optional[str] = None + + postal_code: Optional[str] = None + + state: Optional[str] = None diff --git a/src/kernel/types/vaults/single_use_card_billing_address_param.py b/src/kernel/types/vaults/single_use_card_billing_address_param.py new file mode 100644 index 00000000..dedc559b --- /dev/null +++ b/src/kernel/types/vaults/single_use_card_billing_address_param.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SingleUseCardBillingAddressParam"] + + +class SingleUseCardBillingAddressParam(TypedDict, total=False): + """Billing address filled with the billing_* fill fields. Never returned.""" + + city: str + + country: str + """ISO 3166-1 alpha-2 code.""" + + line1: str + + line2: str + + postal_code: str + + state: str From f67de9c930a43bc3f1b8db04a93bf13831b7d8cb Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Wed, 4 Nov 2026 16:40:55 +0000 Subject: [PATCH 4/5] feat: Let customers keep refreshing imported Link wallet grants Stainless-Generated-From: 97ae6043e5c5ede83741602c06c8d6931c09fefd --- src/kernel/resources/vaults/items.py | 124 +++++++++++++ .../vaults/credential_account_vault_item.py | 1 + .../types/vaults/credential_vault_item.py | 1 + .../vaults/item_perform_operation_params.py | 10 + src/kernel/types/vaults/item_upsert_params.py | 30 ++- .../vaults/payment_request_vault_item.py | 1 + src/kernel/types/vaults/vault_item.py | 2 + .../vaults/vault_item_operation_response.py | 2 + .../types/vaults/wallet_vault_item_spec.py | 9 + tests/api_resources/vaults/test_items.py | 172 +++++++++++++++--- 10 files changed, 319 insertions(+), 33 deletions(-) diff --git a/src/kernel/resources/vaults/items.py b/src/kernel/resources/vaults/items.py index 775dd63c..35c1669e 100644 --- a/src/kernel/resources/vaults/items.py +++ b/src/kernel/resources/vaults/items.py @@ -1424,6 +1424,67 @@ def perform_operation( """ ... + @overload + def perform_operation( + self, + key: str, + *, + id_or_name: str, + access_token: str, + type: Literal["update_access_token"], + # 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, + idempotency_key: str | None = None, + ) -> 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; pass browser_id and page_url from the final checkout + page so Kernel can inspect the checkout. 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 rejected with a non-retryable provider error, the card item is + deleted and the provider's error code and message are returned. Rate limits + return HTTP 429 and retain the card item; stop, back off, and retry the same + authorize operation. + + Ready Kernel cards advertise fill. Fill delivers card details for a few minutes; + submit checkout promptly after filling. A repeated fill is safe. + + 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`. Fill never submits the page, so it is safe to retry after + a failure, an `unknown` outcome, or a transport error. + + Connected Kernel wallets may advertise add_payment_method, which returns a + single-use hosted card_enrollment link for a card, and remove_payment_method, + which removes one card without affecting the others. Both operations require + vaults:write in addition to vaults:use on the vault for scoped API keys, and + return 403 without both permissions. + + 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 + + idempotency_key: Specify a custom idempotency key for this request + """ + ... + @overload def perform_operation( self, @@ -1656,6 +1717,7 @@ def perform_operation( | Literal["1pw_fill"] | Literal["1pw_recover"] | Literal["1pw_update_access_token"] + | Literal["update_access_token"] | Literal["webmcp_invoke"] | Literal["add_payment_method"] | Literal["remove_payment_method"], @@ -3429,6 +3491,67 @@ async def perform_operation( """ ... + @overload + async def perform_operation( + self, + key: str, + *, + id_or_name: str, + access_token: str, + type: Literal["update_access_token"], + # 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, + idempotency_key: str | None = None, + ) -> 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; pass browser_id and page_url from the final checkout + page so Kernel can inspect the checkout. 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 rejected with a non-retryable provider error, the card item is + deleted and the provider's error code and message are returned. Rate limits + return HTTP 429 and retain the card item; stop, back off, and retry the same + authorize operation. + + Ready Kernel cards advertise fill. Fill delivers card details for a few minutes; + submit checkout promptly after filling. A repeated fill is safe. + + 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`. Fill never submits the page, so it is safe to retry after + a failure, an `unknown` outcome, or a transport error. + + Connected Kernel wallets may advertise add_payment_method, which returns a + single-use hosted card_enrollment link for a card, and remove_payment_method, + which removes one card without affecting the others. Both operations require + vaults:write in addition to vaults:use on the vault for scoped API keys, and + return 403 without both permissions. + + 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 + + idempotency_key: Specify a custom idempotency key for this request + """ + ... + @overload async def perform_operation( self, @@ -3661,6 +3784,7 @@ async def perform_operation( | Literal["1pw_fill"] | Literal["1pw_recover"] | Literal["1pw_update_access_token"] + | Literal["update_access_token"] | Literal["webmcp_invoke"] | Literal["add_payment_method"] | Literal["remove_payment_method"], diff --git a/src/kernel/types/vaults/credential_account_vault_item.py b/src/kernel/types/vaults/credential_account_vault_item.py index f33ee788..441dab27 100644 --- a/src/kernel/types/vaults/credential_account_vault_item.py +++ b/src/kernel/types/vaults/credential_account_vault_item.py @@ -45,6 +45,7 @@ class AvailableOperation(BaseModel): "1pw_fill", "1pw_recover", "1pw_update_access_token", + "update_access_token", "webmcp_invoke", "add_payment_method", "remove_payment_method", diff --git a/src/kernel/types/vaults/credential_vault_item.py b/src/kernel/types/vaults/credential_vault_item.py index 6b6a1f69..f2e40546 100644 --- a/src/kernel/types/vaults/credential_vault_item.py +++ b/src/kernel/types/vaults/credential_vault_item.py @@ -52,6 +52,7 @@ class AvailableOperation(BaseModel): "1pw_fill", "1pw_recover", "1pw_update_access_token", + "update_access_token", "webmcp_invoke", "add_payment_method", "remove_payment_method", diff --git a/src/kernel/types/vaults/item_perform_operation_params.py b/src/kernel/types/vaults/item_perform_operation_params.py index 776d78b9..96977cc4 100644 --- a/src/kernel/types/vaults/item_perform_operation_params.py +++ b/src/kernel/types/vaults/item_perform_operation_params.py @@ -28,6 +28,7 @@ "OnePasswordFillVaultItemOperationRequest", "OnePasswordRecoverVaultItemOperationRequest", "OnePasswordUpdateAccessTokenVaultItemOperationRequest", + "UpdateAccessTokenVaultItemOperationRequest", "WebmcpInvokeVaultItemOperationRequest", "AddPaymentMethodVaultItemOperationRequest", "RemovePaymentMethodVaultItemOperationRequest", @@ -267,6 +268,14 @@ class OnePasswordUpdateAccessTokenVaultItemOperationRequest(TypedDict, total=Fal """Optional supplied expiry. Omit to clear the old expiry.""" +class UpdateAccessTokenVaultItemOperationRequest(TypedDict, total=False): + id_or_name: Required[str] + + access_token: Required[str] + + type: Required[Literal["update_access_token"]] + + class WebmcpInvokeVaultItemOperationRequest(TypedDict, total=False): id_or_name: Required[str] @@ -330,6 +339,7 @@ class RemovePaymentMethodVaultItemOperationRequest(TypedDict, total=False): OnePasswordFillVaultItemOperationRequest, OnePasswordRecoverVaultItemOperationRequest, OnePasswordUpdateAccessTokenVaultItemOperationRequest, + UpdateAccessTokenVaultItemOperationRequest, WebmcpInvokeVaultItemOperationRequest, AddPaymentMethodVaultItemOperationRequest, RemovePaymentMethodVaultItemOperationRequest, diff --git a/src/kernel/types/vaults/item_upsert_params.py b/src/kernel/types/vaults/item_upsert_params.py index 8732049f..e627f2a6 100644 --- a/src/kernel/types/vaults/item_upsert_params.py +++ b/src/kernel/types/vaults/item_upsert_params.py @@ -107,14 +107,15 @@ class WalletVaultItemRequestSpecLinkWalletVaultItemRequestSpecAuthorizationImpor class WalletVaultItemRequestSpecLinkWalletVaultItemRequestSpecAuthorizationImportedLinkAuthorizationInputTokens( TypedDict, total=False ): - """Send the token pair from your backend. + """Send the tokens from your backend. - Both tokens must be from the same Link grant under the referenced client. Supply a currently valid access token. Kernel refreshes when needed after import and uses the expiry returned by Link for subsequent tokens. Tokens are never returned in wallet responses, events, or logs. + Supply a currently valid access token. With token_refresh=kernel, also send the refresh token from the same Link grant under the referenced client; Kernel refreshes when needed after import and uses the expiry returned by Link for subsequent tokens. With token_refresh=customer, omit the refresh token. Tokens are never returned in wallet responses, events, or logs. """ access_token: Required[str] - refresh_token: Required[str] + refresh_token: str + """Required with token_refresh=kernel; rejected with token_refresh=customer.""" class WalletVaultItemRequestSpecLinkWalletVaultItemRequestSpecAuthorizationImportedLinkAuthorizationInput( @@ -123,7 +124,8 @@ class WalletVaultItemRequestSpecLinkWalletVaultItemRequestSpecAuthorizationImpor """The customer's backend completes Link OAuth and supplies the resulting tokens. For a new wallet, Kernel verifies the access token can access Link payment methods without consuming or rotating the refresh token. Valid access creates a wallet with state.status=connected. An expired, invalid, revoked, or insufficiently scoped access token returns 400 and no wallet is created. Refresh expired tokens in your backend before importing them. A failed import does not modify existing wallets. - After successful import, Kernel owns subsequent refresh-token rotation; the customer must stop refreshing this grant. Import does not verify the refresh token: if it or the configured client credentials are rejected during a later refresh, the imported wallet becomes degraded. An unknown refresh outcome also leaves it degraded; Kernel does not retry a refresh token that may already have been consumed. There is no in-place reauthorization operation for an imported wallet. + By default (token_refresh=kernel), Kernel owns subsequent refresh-token rotation after a successful import; the customer must stop refreshing this grant. With token_refresh=customer, send only the access token: your backend keeps the refresh token and rotates the grant, and Kernel never refreshes or revokes it. Replace the stored access token with the wallet's update_access_token operation before each step that reaches Link, such as creating, authorizing or filling a card. When Link rejects the stored access token, operations return 409 without contacting Link again; update the token and retry. The remainder of this description applies to token_refresh=kernel. + With token_refresh=kernel, Kernel owns subsequent refresh-token rotation; the customer must stop refreshing this grant. Import does not verify the refresh token: if it or the configured client credentials are rejected during a later refresh, the imported wallet becomes degraded. An unknown refresh outcome also leaves it degraded; Kernel does not retry a refresh token that may already have been consumed. There is no in-place reauthorization operation for an imported wallet. If this imported wallet's credentials become unusable, obtain a fresh Link OAuth grant in your backend and create a wallet under a NEW wallet key. Use the new wallet for NEW cards and payments, not to retry an old payment whose outcome is uncertain. This does not replace the old grant, rebind existing cards, or resolve their payment outcomes. Retain the old wallet and its cards while reconciling any uncertain payments with the provider or support. Do not repeat an uncertain payment on the new wallet, and do not treat deletion as evidence that it did not execute. Deletion of the old wallet can remain blocked by unresolved child cards. Repeating a create for the same item key and non-secret spec returns the existing wallet without replacing tokens, even if they have rotated or the wallet needs reconnection. ID and name references resolving to the same config are equivalent. A different config or non-secret spec returns 409. This create operation does not replace an existing grant. """ @@ -137,12 +139,22 @@ class WalletVaultItemRequestSpecLinkWalletVaultItemRequestSpecAuthorizationImpor tokens: Required[ WalletVaultItemRequestSpecLinkWalletVaultItemRequestSpecAuthorizationImportedLinkAuthorizationInputTokens ] - """Send the token pair from your backend. + """Send the tokens from your backend. - Both tokens must be from the same Link grant under the referenced client. Supply - a currently valid access token. Kernel refreshes when needed after import and - uses the expiry returned by Link for subsequent tokens. Tokens are never - returned in wallet responses, events, or logs. + Supply a currently valid access token. With token_refresh=kernel, also send the + refresh token from the same Link grant under the referenced client; Kernel + refreshes when needed after import and uses the expiry returned by Link for + subsequent tokens. With token_refresh=customer, omit the refresh token. Tokens + are never returned in wallet responses, events, or logs. + """ + + token_refresh: Literal["kernel", "customer"] + """Who refreshes an imported Link grant. + + kernel (default) means Kernel holds and rotates the refresh token. customer + means your backend keeps the refresh token and replaces the access token with + update_access_token; Kernel never refreshes or revokes the grant. Fixed at + wallet creation. """ diff --git a/src/kernel/types/vaults/payment_request_vault_item.py b/src/kernel/types/vaults/payment_request_vault_item.py index 48ef3809..65106c8d 100644 --- a/src/kernel/types/vaults/payment_request_vault_item.py +++ b/src/kernel/types/vaults/payment_request_vault_item.py @@ -45,6 +45,7 @@ class AvailableOperation(BaseModel): "1pw_fill", "1pw_recover", "1pw_update_access_token", + "update_access_token", "webmcp_invoke", "add_payment_method", "remove_payment_method", diff --git a/src/kernel/types/vaults/vault_item.py b/src/kernel/types/vaults/vault_item.py index f91ece5e..43f81ab3 100644 --- a/src/kernel/types/vaults/vault_item.py +++ b/src/kernel/types/vaults/vault_item.py @@ -63,6 +63,7 @@ class WalletVaultItemAvailableOperation(BaseModel): "1pw_fill", "1pw_recover", "1pw_update_access_token", + "update_access_token", "webmcp_invoke", "add_payment_method", "remove_payment_method", @@ -170,6 +171,7 @@ class CardVaultItemAvailableOperation(BaseModel): "1pw_fill", "1pw_recover", "1pw_update_access_token", + "update_access_token", "webmcp_invoke", "add_payment_method", "remove_payment_method", diff --git a/src/kernel/types/vaults/vault_item_operation_response.py b/src/kernel/types/vaults/vault_item_operation_response.py index 023cc83c..31a923e4 100644 --- a/src/kernel/types/vaults/vault_item_operation_response.py +++ b/src/kernel/types/vaults/vault_item_operation_response.py @@ -68,6 +68,7 @@ class WalletVaultItemAvailableOperation(BaseModel): "1pw_fill", "1pw_recover", "1pw_update_access_token", + "update_access_token", "webmcp_invoke", "add_payment_method", "remove_payment_method", @@ -175,6 +176,7 @@ class CardVaultItemAvailableOperation(BaseModel): "1pw_fill", "1pw_recover", "1pw_update_access_token", + "update_access_token", "webmcp_invoke", "add_payment_method", "remove_payment_method", diff --git a/src/kernel/types/vaults/wallet_vault_item_spec.py b/src/kernel/types/vaults/wallet_vault_item_spec.py index 6082f625..8eb9cc2b 100644 --- a/src/kernel/types/vaults/wallet_vault_item_spec.py +++ b/src/kernel/types/vaults/wallet_vault_item_spec.py @@ -61,6 +61,15 @@ class LinkWalletVaultItemSpecAuthorization(BaseModel): method: Literal["oauth"] + token_refresh: Optional[Literal["kernel", "customer"]] = None + """Who refreshes an imported Link grant. + + kernel (default) means Kernel holds and rotates the refresh token. customer + means your backend keeps the refresh token and replaces the access token with + update_access_token; Kernel never refreshes or revokes the grant. Fixed at + wallet creation. + """ + class LinkWalletVaultItemSpec(BaseModel): authorization: LinkWalletVaultItemSpecAuthorization diff --git a/tests/api_resources/vaults/test_items.py b/tests/api_resources/vaults/test_items.py index cf14bbe1..36bd1d18 100644 --- a/tests/api_resources/vaults/test_items.py +++ b/tests/api_resources/vaults/test_items.py @@ -1706,6 +1706,68 @@ def test_path_params_perform_operation_overload_14(self, client: Kernel) -> None @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_perform_operation_overload_15(self, client: Kernel) -> None: + item = client.vaults.items.perform_operation( + key="key", + id_or_name="id_or_name", + access_token="x", + type="update_access_token", + ) + assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_perform_operation_overload_15(self, client: Kernel) -> None: + response = client.vaults.items.with_raw_response.perform_operation( + key="key", + id_or_name="id_or_name", + access_token="x", + type="update_access_token", + ) + + 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_15(self, client: Kernel) -> None: + with client.vaults.items.with_streaming_response.perform_operation( + key="key", + id_or_name="id_or_name", + access_token="x", + type="update_access_token", + ) 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_15(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="", + access_token="x", + type="update_access_token", + ) + + 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", + access_token="x", + type="update_access_token", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_perform_operation_overload_16(self, client: Kernel) -> None: item = client.vaults.items.perform_operation( key="key", id_or_name="id_or_name", @@ -1732,7 +1794,7 @@ def test_method_perform_operation_overload_15(self, client: Kernel) -> None: @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_method_perform_operation_with_all_params_overload_15(self, client: Kernel) -> None: + def test_method_perform_operation_with_all_params_overload_16(self, client: Kernel) -> None: item = client.vaults.items.perform_operation( key="key", id_or_name="id_or_name", @@ -1762,7 +1824,7 @@ def test_method_perform_operation_with_all_params_overload_15(self, client: Kern @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_raw_response_perform_operation_overload_15(self, client: Kernel) -> None: + def test_raw_response_perform_operation_overload_16(self, client: Kernel) -> None: response = client.vaults.items.with_raw_response.perform_operation( key="key", id_or_name="id_or_name", @@ -1793,7 +1855,7 @@ def test_raw_response_perform_operation_overload_15(self, client: Kernel) -> Non @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_streaming_response_perform_operation_overload_15(self, client: Kernel) -> None: + def test_streaming_response_perform_operation_overload_16(self, client: Kernel) -> None: with client.vaults.items.with_streaming_response.perform_operation( key="key", id_or_name="id_or_name", @@ -1826,7 +1888,7 @@ def test_streaming_response_perform_operation_overload_15(self, client: Kernel) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_path_params_perform_operation_overload_15(self, client: Kernel) -> None: + def test_path_params_perform_operation_overload_16(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", @@ -1877,7 +1939,7 @@ def test_path_params_perform_operation_overload_15(self, client: Kernel) -> None @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_method_perform_operation_overload_16(self, client: Kernel) -> None: + def test_method_perform_operation_overload_17(self, client: Kernel) -> None: item = client.vaults.items.perform_operation( key="key", id_or_name="id_or_name", @@ -1887,7 +1949,7 @@ def test_method_perform_operation_overload_16(self, client: Kernel) -> None: @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_raw_response_perform_operation_overload_16(self, client: Kernel) -> None: + def test_raw_response_perform_operation_overload_17(self, client: Kernel) -> None: response = client.vaults.items.with_raw_response.perform_operation( key="key", id_or_name="id_or_name", @@ -1901,7 +1963,7 @@ def test_raw_response_perform_operation_overload_16(self, client: Kernel) -> Non @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_streaming_response_perform_operation_overload_16(self, client: Kernel) -> None: + def test_streaming_response_perform_operation_overload_17(self, client: Kernel) -> None: with client.vaults.items.with_streaming_response.perform_operation( key="key", id_or_name="id_or_name", @@ -1917,7 +1979,7 @@ def test_streaming_response_perform_operation_overload_16(self, client: Kernel) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_path_params_perform_operation_overload_16(self, client: Kernel) -> None: + def test_path_params_perform_operation_overload_17(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", @@ -1934,7 +1996,7 @@ def test_path_params_perform_operation_overload_16(self, client: Kernel) -> None @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_method_perform_operation_overload_17(self, client: Kernel) -> None: + def test_method_perform_operation_overload_18(self, client: Kernel) -> None: item = client.vaults.items.perform_operation( key="key", id_or_name="id_or_name", @@ -1945,7 +2007,7 @@ def test_method_perform_operation_overload_17(self, client: Kernel) -> None: @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_raw_response_perform_operation_overload_17(self, client: Kernel) -> None: + def test_raw_response_perform_operation_overload_18(self, client: Kernel) -> None: response = client.vaults.items.with_raw_response.perform_operation( key="key", id_or_name="id_or_name", @@ -1960,7 +2022,7 @@ def test_raw_response_perform_operation_overload_17(self, client: Kernel) -> Non @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_streaming_response_perform_operation_overload_17(self, client: Kernel) -> None: + def test_streaming_response_perform_operation_overload_18(self, client: Kernel) -> None: with client.vaults.items.with_streaming_response.perform_operation( key="key", id_or_name="id_or_name", @@ -1977,7 +2039,7 @@ def test_streaming_response_perform_operation_overload_17(self, client: Kernel) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - def test_path_params_perform_operation_overload_17(self, client: Kernel) -> None: + def test_path_params_perform_operation_overload_18(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", @@ -4327,6 +4389,68 @@ async def test_path_params_perform_operation_overload_14(self, async_client: Asy @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_method_perform_operation_overload_15(self, async_client: AsyncKernel) -> None: + item = await async_client.vaults.items.perform_operation( + key="key", + id_or_name="id_or_name", + access_token="x", + type="update_access_token", + ) + 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_15(self, async_client: AsyncKernel) -> None: + response = await async_client.vaults.items.with_raw_response.perform_operation( + key="key", + id_or_name="id_or_name", + access_token="x", + type="update_access_token", + ) + + 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_15(self, async_client: AsyncKernel) -> None: + async with async_client.vaults.items.with_streaming_response.perform_operation( + key="key", + id_or_name="id_or_name", + access_token="x", + type="update_access_token", + ) 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_15(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="", + access_token="x", + type="update_access_token", + ) + + 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", + access_token="x", + type="update_access_token", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_perform_operation_overload_16(self, async_client: AsyncKernel) -> None: item = await async_client.vaults.items.perform_operation( key="key", id_or_name="id_or_name", @@ -4353,7 +4477,7 @@ async def test_method_perform_operation_overload_15(self, async_client: AsyncKer @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_method_perform_operation_with_all_params_overload_15(self, async_client: AsyncKernel) -> None: + async def test_method_perform_operation_with_all_params_overload_16(self, async_client: AsyncKernel) -> None: item = await async_client.vaults.items.perform_operation( key="key", id_or_name="id_or_name", @@ -4383,7 +4507,7 @@ async def test_method_perform_operation_with_all_params_overload_15(self, async_ @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_raw_response_perform_operation_overload_15(self, async_client: AsyncKernel) -> None: + async def test_raw_response_perform_operation_overload_16(self, async_client: AsyncKernel) -> None: response = await async_client.vaults.items.with_raw_response.perform_operation( key="key", id_or_name="id_or_name", @@ -4414,7 +4538,7 @@ async def test_raw_response_perform_operation_overload_15(self, async_client: As @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_streaming_response_perform_operation_overload_15(self, async_client: AsyncKernel) -> None: + async def test_streaming_response_perform_operation_overload_16(self, async_client: AsyncKernel) -> None: async with async_client.vaults.items.with_streaming_response.perform_operation( key="key", id_or_name="id_or_name", @@ -4447,7 +4571,7 @@ async def test_streaming_response_perform_operation_overload_15(self, async_clie @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_path_params_perform_operation_overload_15(self, async_client: AsyncKernel) -> None: + async def test_path_params_perform_operation_overload_16(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", @@ -4498,7 +4622,7 @@ async def test_path_params_perform_operation_overload_15(self, async_client: Asy @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_method_perform_operation_overload_16(self, async_client: AsyncKernel) -> None: + async def test_method_perform_operation_overload_17(self, async_client: AsyncKernel) -> None: item = await async_client.vaults.items.perform_operation( key="key", id_or_name="id_or_name", @@ -4508,7 +4632,7 @@ async def test_method_perform_operation_overload_16(self, async_client: AsyncKer @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_raw_response_perform_operation_overload_16(self, async_client: AsyncKernel) -> None: + async def test_raw_response_perform_operation_overload_17(self, async_client: AsyncKernel) -> None: response = await async_client.vaults.items.with_raw_response.perform_operation( key="key", id_or_name="id_or_name", @@ -4522,7 +4646,7 @@ async def test_raw_response_perform_operation_overload_16(self, async_client: As @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_streaming_response_perform_operation_overload_16(self, async_client: AsyncKernel) -> None: + async def test_streaming_response_perform_operation_overload_17(self, async_client: AsyncKernel) -> None: async with async_client.vaults.items.with_streaming_response.perform_operation( key="key", id_or_name="id_or_name", @@ -4538,7 +4662,7 @@ async def test_streaming_response_perform_operation_overload_16(self, async_clie @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_path_params_perform_operation_overload_16(self, async_client: AsyncKernel) -> None: + async def test_path_params_perform_operation_overload_17(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", @@ -4555,7 +4679,7 @@ async def test_path_params_perform_operation_overload_16(self, async_client: Asy @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_method_perform_operation_overload_17(self, async_client: AsyncKernel) -> None: + async def test_method_perform_operation_overload_18(self, async_client: AsyncKernel) -> None: item = await async_client.vaults.items.perform_operation( key="key", id_or_name="id_or_name", @@ -4566,7 +4690,7 @@ async def test_method_perform_operation_overload_17(self, async_client: AsyncKer @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_raw_response_perform_operation_overload_17(self, async_client: AsyncKernel) -> None: + async def test_raw_response_perform_operation_overload_18(self, async_client: AsyncKernel) -> None: response = await async_client.vaults.items.with_raw_response.perform_operation( key="key", id_or_name="id_or_name", @@ -4581,7 +4705,7 @@ async def test_raw_response_perform_operation_overload_17(self, async_client: As @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_streaming_response_perform_operation_overload_17(self, async_client: AsyncKernel) -> None: + async def test_streaming_response_perform_operation_overload_18(self, async_client: AsyncKernel) -> None: async with async_client.vaults.items.with_streaming_response.perform_operation( key="key", id_or_name="id_or_name", @@ -4598,7 +4722,7 @@ async def test_streaming_response_perform_operation_overload_17(self, async_clie @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize - async def test_path_params_perform_operation_overload_17(self, async_client: AsyncKernel) -> None: + async def test_path_params_perform_operation_overload_18(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", From 06f1a77c6850f9169229bdcc9e9c8c639621038b Mon Sep 17 00:00:00 2001 From: "kernel-internal[bot]" <260533166+kernel-internal[bot]@users.noreply.github.com> Date: Wed, 4 Nov 2026 16:47:03 +0000 Subject: [PATCH 5/5] release: 0.140.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 b84fc160..7da24d1a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.139.0" + ".": "0.140.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ccdac82..f98c4455 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [0.140.0](https://github.com/kernel/kernel-python-sdk/compare/v0.139.0...v0.140.0) (2026-11-04) + + +### Features + +* Add single-use Kernel cards to vaults ([c11d264](https://github.com/kernel/kernel-python-sdk/commit/c11d2644383af967ddcb8b892b325ea4421eb956)) +* Drop Kernel's origin gate from 1Password fill ([e5454cb](https://github.com/kernel/kernel-python-sdk/commit/e5454cb02a33599e81ec5ca3c764e2522ad2cd76)) +* Let customers keep refreshing imported Link wallet grants ([f67de9c](https://github.com/kernel/kernel-python-sdk/commit/f67de9c930a43bc3f1b8db04a93bf13831b7d8cb)) +* Pass through tool_page_unloaded and disable SDK retries for WebMCP invoke failures ([56c9983](https://github.com/kernel/kernel-python-sdk/commit/56c998338b3f885d4d8276e5d038d9612bf0d1fd)) + ## [0.139.0](https://github.com/kernel/kernel-python-sdk/compare/v0.138.0...v0.139.0) (2026-10-30) diff --git a/pyproject.toml b/pyproject.toml index e196f12d..9cc1d8e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "kernel" -version = "0.139.0" +version = "0.140.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 7a10bcae..f93f5c0f 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.139.0" # x-release-please-version +__version__ = "0.140.0" # x-release-please-version