diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f56be53d..345168d4 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.136.0" + ".": "0.137.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 62965155..3de0febb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [0.137.0](https://github.com/kernel/kernel-python-sdk/compare/v0.136.0...v0.137.0) (2026-10-29) + + +### Features + +* Add request-only fingerprint beta contract ([bca5a05](https://github.com/kernel/kernel-python-sdk/commit/bca5a056f486104f3a9f145c65b78df29a104898)) +* Read Shop Pay card status one read at a time and report outcome details ([888e8a2](https://github.com/kernel/kernel-python-sdk/commit/888e8a2d2a64e672941c2c420a0726115727ef42)) +* Remove the projects_enabled flag from the API ([bfb22ed](https://github.com/kernel/kernel-python-sdk/commit/bfb22ed009fa5cbe81781ad6fb3d81023571b643)) +* route browser REPL and WebMCP calls directly to browser VMs ([1974522](https://github.com/kernel/kernel-python-sdk/commit/1974522e499d2dd7a3cf39713229ef9dd9f70d34)) +* Route Evervault alias payment windows through per-window upstreams ([845e6c6](https://github.com/kernel/kernel-python-sdk/commit/845e6c6219be1ec937179236359909aff4137eaf)) +* Wire Shop Pay into the vault API ([e5e6b25](https://github.com/kernel/kernel-python-sdk/commit/e5e6b2541b1360a42fbad61acdfd58d5767fa409)) + ## [0.136.0](https://github.com/kernel/kernel-python-sdk/compare/v0.135.0...v0.136.0) (2026-10-28) diff --git a/README.md b/README.md index a8c9fc1c..fd6bec8e 100644 --- a/README.md +++ b/README.md @@ -203,9 +203,9 @@ from kernel import Kernel client = Kernel() browser = client.browsers.create( - location={}, + fingerprint={"os": "macos"}, ) -print(browser.location) +print(browser.fingerprint) ``` ## File uploads diff --git a/api.md b/api.md index 734e8fbb..06153f73 100644 --- a/api.md +++ b/api.md @@ -118,6 +118,7 @@ Types: ```python from kernel.types import ( BrowserAllowlistState, + BrowserFingerprint, BrowserLocation, BrowserLocationRequest, BrowserLocationSource, @@ -634,6 +635,10 @@ from kernel.types.vaults import ( RemovePaymentMethodVaultItemOperationRequest, RemovePaymentMethodVaultItemOperationResult, RestartVerificationVaultItemOperationRequest, + ShopPayCardState, + ShopPayCardVaultItemSpec, + ShopPayWalletState, + ShopPayWalletVaultItemSpec, VaultCardAliases, VaultCardFillField, VaultCheckoutContext, diff --git a/pyproject.toml b/pyproject.toml index a1f90afe..b811df5d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "kernel" -version = "0.136.0" +version = "0.137.0" description = "The official Python library for the kernel API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/kernel/_client.py b/src/kernel/_client.py index 4a06456d..66394d5a 100644 --- a/src/kernel/_client.py +++ b/src/kernel/_client.py @@ -316,11 +316,7 @@ def credentials(self) -> CredentialsResource: @cached_property def projects(self) -> ProjectsResource: - """ - Create and manage projects for resource isolation within an organization. - When projects are disabled for the organization, project operations return - `404` with code `projects_disabled`. - """ + """Create and manage projects for resource isolation within an organization.""" from .resources.projects import ProjectsResource return ProjectsResource(self) @@ -729,11 +725,7 @@ def credentials(self) -> AsyncCredentialsResource: @cached_property def projects(self) -> AsyncProjectsResource: - """ - Create and manage projects for resource isolation within an organization. - When projects are disabled for the organization, project operations return - `404` with code `projects_disabled`. - """ + """Create and manage projects for resource isolation within an organization.""" from .resources.projects import AsyncProjectsResource return AsyncProjectsResource(self) @@ -1042,11 +1034,7 @@ def credentials(self) -> credentials.CredentialsResourceWithRawResponse: @cached_property def projects(self) -> projects.ProjectsResourceWithRawResponse: - """ - Create and manage projects for resource isolation within an organization. - When projects are disabled for the organization, project operations return - `404` with code `projects_disabled`. - """ + """Create and manage projects for resource isolation within an organization.""" from .resources.projects import ProjectsResourceWithRawResponse return ProjectsResourceWithRawResponse(self._client.projects) @@ -1187,11 +1175,7 @@ def credentials(self) -> credentials.AsyncCredentialsResourceWithRawResponse: @cached_property def projects(self) -> projects.AsyncProjectsResourceWithRawResponse: - """ - Create and manage projects for resource isolation within an organization. - When projects are disabled for the organization, project operations return - `404` with code `projects_disabled`. - """ + """Create and manage projects for resource isolation within an organization.""" from .resources.projects import AsyncProjectsResourceWithRawResponse return AsyncProjectsResourceWithRawResponse(self._client.projects) @@ -1332,11 +1316,7 @@ def credentials(self) -> credentials.CredentialsResourceWithStreamingResponse: @cached_property def projects(self) -> projects.ProjectsResourceWithStreamingResponse: - """ - Create and manage projects for resource isolation within an organization. - When projects are disabled for the organization, project operations return - `404` with code `projects_disabled`. - """ + """Create and manage projects for resource isolation within an organization.""" from .resources.projects import ProjectsResourceWithStreamingResponse return ProjectsResourceWithStreamingResponse(self._client.projects) @@ -1477,11 +1457,7 @@ def credentials(self) -> credentials.AsyncCredentialsResourceWithStreamingRespon @cached_property def projects(self) -> projects.AsyncProjectsResourceWithStreamingResponse: - """ - Create and manage projects for resource isolation within an organization. - When projects are disabled for the organization, project operations return - `404` with code `projects_disabled`. - """ + """Create and manage projects for resource isolation within an organization.""" from .resources.projects import AsyncProjectsResourceWithStreamingResponse return AsyncProjectsResourceWithStreamingResponse(self._client.projects) diff --git a/src/kernel/_version.py b/src/kernel/_version.py index 42db1fdd..5a9d3ffb 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.136.0" # x-release-please-version +__version__ = "0.137.0" # x-release-please-version diff --git a/src/kernel/lib/browser_routing/routing.py b/src/kernel/lib/browser_routing/routing.py index 61f786cb..6a431795 100644 --- a/src/kernel/lib/browser_routing/routing.py +++ b/src/kernel/lib/browser_routing/routing.py @@ -62,6 +62,8 @@ def browser_routing_config_from_env() -> BrowserRoutingConfig: "process", "fs", "logs/stream", + "repl", + "webmcp", ) ) if raw.strip() == "": diff --git a/src/kernel/resources/browsers/browsers.py b/src/kernel/resources/browsers/browsers.py index 00010a7e..5932ede3 100644 --- a/src/kernel/resources/browsers/browsers.py +++ b/src/kernel/resources/browsers/browsers.py @@ -113,6 +113,7 @@ from ...types.browser_memory_request import BrowserMemoryRequest from ...types.browser_create_response import BrowserCreateResponse from ...types.browser_update_response import BrowserUpdateResponse +from ...types.browser_fingerprint_param import BrowserFingerprintParam from ...types.browser_retrieve_response import BrowserRetrieveResponse from ...types.browser_proxy_config_param import BrowserProxyConfigParam from ...types.browser_network_config_param import BrowserNetworkConfigParam @@ -195,6 +196,7 @@ def create( *, chrome_policy: Dict[str, object] | Omit = omit, extensions: Iterable[BrowserExtension] | Omit = omit, + fingerprint: BrowserFingerprintParam | Omit = omit, gpu: bool | Omit = omit, headless: bool | Omit = omit, invocation_id: str | Omit = omit, @@ -234,6 +236,11 @@ def create( extensions: List of browser extensions to load into the session. Provide each by id or name. + fingerprint: Closed beta: contact Kernel to enable fingerprint overrides for your + organization. Overrides browser attributes presented to websites for this + session. Currently supports macos and requires stealth=true, headless=false, + gpu=false, and default memory. Omit to preserve the default browser behavior. + gpu: If true, enables GPU acceleration for the browser session. Requires Start-Up or Enterprise plan, headless=false, and region=us-east. @@ -340,6 +347,7 @@ def create( { "chrome_policy": chrome_policy, "extensions": extensions, + "fingerprint": fingerprint, "gpu": gpu, "headless": headless, "invocation_id": invocation_id, @@ -1049,6 +1057,7 @@ async def create( *, chrome_policy: Dict[str, object] | Omit = omit, extensions: Iterable[BrowserExtension] | Omit = omit, + fingerprint: BrowserFingerprintParam | Omit = omit, gpu: bool | Omit = omit, headless: bool | Omit = omit, invocation_id: str | Omit = omit, @@ -1088,6 +1097,11 @@ async def create( extensions: List of browser extensions to load into the session. Provide each by id or name. + fingerprint: Closed beta: contact Kernel to enable fingerprint overrides for your + organization. Overrides browser attributes presented to websites for this + session. Currently supports macos and requires stealth=true, headless=false, + gpu=false, and default memory. Omit to preserve the default browser behavior. + gpu: If true, enables GPU acceleration for the browser session. Requires Start-Up or Enterprise plan, headless=false, and region=us-east. @@ -1194,6 +1208,7 @@ async def create( { "chrome_policy": chrome_policy, "extensions": extensions, + "fingerprint": fingerprint, "gpu": gpu, "headless": headless, "invocation_id": invocation_id, diff --git a/src/kernel/resources/projects/limits.py b/src/kernel/resources/projects/limits.py index 502c98fa..211c76d1 100644 --- a/src/kernel/resources/projects/limits.py +++ b/src/kernel/resources/projects/limits.py @@ -24,11 +24,7 @@ class LimitsResource(SyncAPIResource): - """ - Create and manage projects for resource isolation within an organization. - When projects are disabled for the organization, project operations return - `404` with code `projects_disabled`. - """ + """Create and manage projects for resource isolation within an organization.""" @cached_property def with_raw_response(self) -> LimitsResourceWithRawResponse: @@ -150,11 +146,7 @@ def update( class AsyncLimitsResource(AsyncAPIResource): - """ - Create and manage projects for resource isolation within an organization. - When projects are disabled for the organization, project operations return - `404` with code `projects_disabled`. - """ + """Create and manage projects for resource isolation within an organization.""" @cached_property def with_raw_response(self) -> AsyncLimitsResourceWithRawResponse: diff --git a/src/kernel/resources/projects/projects.py b/src/kernel/resources/projects/projects.py index 42b54d3d..52d57fdd 100644 --- a/src/kernel/resources/projects/projects.py +++ b/src/kernel/resources/projects/projects.py @@ -33,19 +33,11 @@ class ProjectsResource(SyncAPIResource): - """ - Create and manage projects for resource isolation within an organization. - When projects are disabled for the organization, project operations return - `404` with code `projects_disabled`. - """ + """Create and manage projects for resource isolation within an organization.""" @cached_property def limits(self) -> LimitsResource: - """ - Create and manage projects for resource isolation within an organization. - When projects are disabled for the organization, project operations return - `404` with code `projects_disabled`. - """ + """Create and manage projects for resource isolation within an organization.""" return LimitsResource(self._client) @cached_property @@ -295,19 +287,11 @@ def delete( class AsyncProjectsResource(AsyncAPIResource): - """ - Create and manage projects for resource isolation within an organization. - When projects are disabled for the organization, project operations return - `404` with code `projects_disabled`. - """ + """Create and manage projects for resource isolation within an organization.""" @cached_property def limits(self) -> AsyncLimitsResource: - """ - Create and manage projects for resource isolation within an organization. - When projects are disabled for the organization, project operations return - `404` with code `projects_disabled`. - """ + """Create and manage projects for resource isolation within an organization.""" return AsyncLimitsResource(self._client) @cached_property @@ -578,11 +562,7 @@ def __init__(self, projects: ProjectsResource) -> None: @cached_property def limits(self) -> LimitsResourceWithRawResponse: - """ - Create and manage projects for resource isolation within an organization. - When projects are disabled for the organization, project operations return - `404` with code `projects_disabled`. - """ + """Create and manage projects for resource isolation within an organization.""" return LimitsResourceWithRawResponse(self._projects.limits) @@ -608,11 +588,7 @@ def __init__(self, projects: AsyncProjectsResource) -> None: @cached_property def limits(self) -> AsyncLimitsResourceWithRawResponse: - """ - Create and manage projects for resource isolation within an organization. - When projects are disabled for the organization, project operations return - `404` with code `projects_disabled`. - """ + """Create and manage projects for resource isolation within an organization.""" return AsyncLimitsResourceWithRawResponse(self._projects.limits) @@ -638,11 +614,7 @@ def __init__(self, projects: ProjectsResource) -> None: @cached_property def limits(self) -> LimitsResourceWithStreamingResponse: - """ - Create and manage projects for resource isolation within an organization. - When projects are disabled for the organization, project operations return - `404` with code `projects_disabled`. - """ + """Create and manage projects for resource isolation within an organization.""" return LimitsResourceWithStreamingResponse(self._projects.limits) @@ -668,9 +640,5 @@ def __init__(self, projects: AsyncProjectsResource) -> None: @cached_property def limits(self) -> AsyncLimitsResourceWithStreamingResponse: - """ - Create and manage projects for resource isolation within an organization. - When projects are disabled for the organization, project operations return - `404` with code `projects_disabled`. - """ + """Create and manage projects for resource isolation within an organization.""" return AsyncLimitsResourceWithStreamingResponse(self._projects.limits) diff --git a/src/kernel/resources/vaults/items.py b/src/kernel/resources/vaults/items.py index 75c72d17..c89f26b0 100644 --- a/src/kernel/resources/vaults/items.py +++ b/src/kernel/resources/vaults/items.py @@ -386,13 +386,13 @@ def delete( Unresolved payment operations normally block deletion, including operations on child cards of a wallet. Deleting a connected Kernel wallet first blocks new payments on it, then removes its enrolled card. If that fails, the wallet is - kept and keeps refusing payments; retry the deletion. A Link or Kernel card in - recovery_required for issuance, or an AgentCard card in recovery_required whose - checkout create response returned no authorization ID, may be explicitly - abandoned by deleting that card directly; deleting its wallet or vault remains - blocked until the card is deleted. Deleting or recreating an item is not proof - that a payment did not occur. Deleting a managed auth credential item leaves the - connection and its saved credential unchanged. + kept and keeps refusing payments; retry the deletion. A Link, Kernel or Shop Pay + card in recovery_required for issuance, or an AgentCard card in + recovery_required whose checkout create response returned no authorization ID, + may be explicitly abandoned by deleting that card directly; deleting its wallet + or vault remains blocked until the card is deleted. Deleting or recreating an + item is not proof that a payment did not occur. Deleting a managed auth + credential item leaves the connection and its saved credential unchanged. Args: extra_headers: Send extra headers @@ -615,6 +615,9 @@ def perform_operation( "PURCHASE", "AUTHORIZATION", "CAPTURE", "REFUND", "REVERSAL", "VERIFICATION", "CHARGEBACK", "FRAUD" ], type: Literal["confirm_transaction"], + failure_code: str | Omit = omit, + order_id: str | Omit = omit, + order_url: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -662,6 +665,17 @@ def perform_operation( occurred_at: Time the merchant outcome was observed. + failure_code: Why the purchase did not succeed, for any other outcome, such as 3ds_challenge, + captcha, login_required, timeout or site_error. payment_declined goes only with + DECLINED, which takes no other code. Shop Pay reports it; other providers ignore + it. + + order_id: The merchant's order number, for an APPROVED outcome. Shop Pay reports it; other + providers ignore it. + + order_url: The merchant's https order page, for an APPROVED outcome. Shop Pay reports it; + other providers ignore it. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1655,6 +1669,9 @@ def perform_operation( "PURCHASE", "AUTHORIZATION", "CAPTURE", "REFUND", "REVERSAL", "VERIFICATION", "CHARGEBACK", "FRAUD" ] | Omit = omit, + failure_code: str | Omit = omit, + order_id: str | Omit = omit, + order_url: str | Omit = omit, checkout: VaultCheckoutContextParam | Omit = omit, fields: Iterable[VaultFillFieldParam] | Omit = omit, timeout_ms: int | Omit = omit, @@ -1698,6 +1715,9 @@ def perform_operation( "occurred_at": occurred_at, "status": status, "transaction_type": transaction_type, + "failure_code": failure_code, + "order_id": order_id, + "order_url": order_url, "checkout": checkout, "fields": fields, "timeout_ms": timeout_ms, @@ -2371,13 +2391,13 @@ async def delete( Unresolved payment operations normally block deletion, including operations on child cards of a wallet. Deleting a connected Kernel wallet first blocks new payments on it, then removes its enrolled card. If that fails, the wallet is - kept and keeps refusing payments; retry the deletion. A Link or Kernel card in - recovery_required for issuance, or an AgentCard card in recovery_required whose - checkout create response returned no authorization ID, may be explicitly - abandoned by deleting that card directly; deleting its wallet or vault remains - blocked until the card is deleted. Deleting or recreating an item is not proof - that a payment did not occur. Deleting a managed auth credential item leaves the - connection and its saved credential unchanged. + kept and keeps refusing payments; retry the deletion. A Link, Kernel or Shop Pay + card in recovery_required for issuance, or an AgentCard card in + recovery_required whose checkout create response returned no authorization ID, + may be explicitly abandoned by deleting that card directly; deleting its wallet + or vault remains blocked until the card is deleted. Deleting or recreating an + item is not proof that a payment did not occur. Deleting a managed auth + credential item leaves the connection and its saved credential unchanged. Args: extra_headers: Send extra headers @@ -2600,6 +2620,9 @@ async def perform_operation( "PURCHASE", "AUTHORIZATION", "CAPTURE", "REFUND", "REVERSAL", "VERIFICATION", "CHARGEBACK", "FRAUD" ], type: Literal["confirm_transaction"], + failure_code: str | Omit = omit, + order_id: str | Omit = omit, + order_url: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -2647,6 +2670,17 @@ async def perform_operation( occurred_at: Time the merchant outcome was observed. + failure_code: Why the purchase did not succeed, for any other outcome, such as 3ds_challenge, + captcha, login_required, timeout or site_error. payment_declined goes only with + DECLINED, which takes no other code. Shop Pay reports it; other providers ignore + it. + + order_id: The merchant's order number, for an APPROVED outcome. Shop Pay reports it; other + providers ignore it. + + order_url: The merchant's https order page, for an APPROVED outcome. Shop Pay reports it; + other providers ignore it. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -3640,6 +3674,9 @@ async def perform_operation( "PURCHASE", "AUTHORIZATION", "CAPTURE", "REFUND", "REVERSAL", "VERIFICATION", "CHARGEBACK", "FRAUD" ] | Omit = omit, + failure_code: str | Omit = omit, + order_id: str | Omit = omit, + order_url: str | Omit = omit, checkout: VaultCheckoutContextParam | Omit = omit, fields: Iterable[VaultFillFieldParam] | Omit = omit, timeout_ms: int | Omit = omit, @@ -3683,6 +3720,9 @@ async def perform_operation( "occurred_at": occurred_at, "status": status, "transaction_type": transaction_type, + "failure_code": failure_code, + "order_id": order_id, + "order_url": order_url, "checkout": checkout, "fields": fields, "timeout_ms": timeout_ms, diff --git a/src/kernel/types/__init__.py b/src/kernel/types/__init__.py index 10bb66a1..eb5e4193 100644 --- a/src/kernel/types/__init__.py +++ b/src/kernel/types/__init__.py @@ -120,6 +120,7 @@ from .invocation_follow_params import InvocationFollowParams as InvocationFollowParams from .invocation_list_response import InvocationListResponse as InvocationListResponse from .invocation_update_params import InvocationUpdateParams as InvocationUpdateParams +from .browser_fingerprint_param import BrowserFingerprintParam as BrowserFingerprintParam from .browser_repl_text_content import BrowserReplTextContent as BrowserReplTextContent from .browser_retrieve_response import BrowserRetrieveResponse as BrowserRetrieveResponse from .extension_upload_response import ExtensionUploadResponse as ExtensionUploadResponse diff --git a/src/kernel/types/browser_create_params.py b/src/kernel/types/browser_create_params.py index 8e319668..516cade1 100644 --- a/src/kernel/types/browser_create_params.py +++ b/src/kernel/types/browser_create_params.py @@ -9,6 +9,7 @@ from .browser_video_memory import BrowserVideoMemory from .vault_reference_param import VaultReferenceParam from .browser_memory_request import BrowserMemoryRequest +from .browser_fingerprint_param import BrowserFingerprintParam from .browser_proxy_config_param import BrowserProxyConfigParam from .browser_network_config_param import BrowserNetworkConfigParam from .shared_params.browser_profile import BrowserProfile @@ -42,6 +43,14 @@ class BrowserCreateParams(TypedDict, total=False): Provide each by id or name. """ + fingerprint: BrowserFingerprintParam + """ + Closed beta: contact Kernel to enable fingerprint overrides for your + organization. Overrides browser attributes presented to websites for this + session. Currently supports macos and requires stealth=true, headless=false, + gpu=false, and default memory. Omit to preserve the default browser behavior. + """ + gpu: bool """If true, enables GPU acceleration for the browser session. diff --git a/src/kernel/types/browser_fingerprint_param.py b/src/kernel/types/browser_fingerprint_param.py new file mode 100644 index 00000000..07de2630 --- /dev/null +++ b/src/kernel/types/browser_fingerprint_param.py @@ -0,0 +1,14 @@ +# 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__ = ["BrowserFingerprintParam"] + + +class BrowserFingerprintParam(TypedDict, total=False): + """Overrides for browser attributes presented to websites.""" + + os: Required[Literal["macos"]] + """Operating system the browser reports to websites.""" diff --git a/src/kernel/types/browsers/browser_proxy_error_event.py b/src/kernel/types/browsers/browser_proxy_error_event.py index 574f525d..a60ef38e 100644 --- a/src/kernel/types/browsers/browser_proxy_error_event.py +++ b/src/kernel/types/browsers/browser_proxy_error_event.py @@ -30,6 +30,7 @@ class Data(BrowserEventContext): "upstream_timeout", "upstream_dns_failure", "upstream_connect_failed", + "payment_window_expired", "unknown", ] """ @@ -39,8 +40,9 @@ class Data(BrowserEventContext): network_policy_denied, provider_blacklisted, provider_unreachable, provider_rejected, origin_tls_timeout, origin_response_incomplete, proxy_unavailable, restricted_route_unavailable, upstream_timeout, - upstream_dns_failure, upstream_connect_failed. A header value the browser image - does not recognize is reported as unknown, with the header value in raw_code. + upstream_dns_failure, upstream_connect_failed, payment_window_expired. A header + value the browser image does not recognize is reported as unknown, with the + header value in raw_code. """ request_id: str diff --git a/src/kernel/types/vaults/__init__.py b/src/kernel/types/vaults/__init__.py index 439d7d3b..fa731977 100644 --- a/src/kernel/types/vaults/__init__.py +++ b/src/kernel/types/vaults/__init__.py @@ -13,6 +13,7 @@ from .item_upsert_params import ItemUpsertParams as ItemUpsertParams from .vault_card_aliases import VaultCardAliases as VaultCardAliases from .kernel_wallet_state import KernelWalletState as KernelWalletState +from .shop_pay_card_state import ShopPayCardState as ShopPayCardState from .card_vault_item_spec import CardVaultItemSpec as CardVaultItemSpec from .item_events_response import ItemEventsResponse as ItemEventsResponse from .item_retrieve_params import ItemRetrieveParams as ItemRetrieveParams @@ -20,6 +21,7 @@ from .card_vault_item_state import CardVaultItemState as CardVaultItemState 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 .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 @@ -39,9 +41,11 @@ from .credential_account_vault_item import CredentialAccountVaultItem as CredentialAccountVaultItem from .item_perform_operation_params import ItemPerformOperationParams as ItemPerformOperationParams from .kernel_wallet_vault_item_spec import KernelWalletVaultItemSpec as KernelWalletVaultItemSpec +from .shop_pay_card_vault_item_spec import ShopPayCardVaultItemSpec as ShopPayCardVaultItemSpec from .vault_item_operation_response import VaultItemOperationResponse as VaultItemOperationResponse 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 .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 @@ -55,11 +59,13 @@ from .kernel_wallet_vault_item_spec_param import KernelWalletVaultItemSpecParam as KernelWalletVaultItemSpecParam from .managed_auth_credential_vault_field import ManagedAuthCredentialVaultField as ManagedAuthCredentialVaultField from .passkey_vault_item_operation_result import PasskeyVaultItemOperationResult as PasskeyVaultItemOperationResult +from .shop_pay_card_vault_item_spec_param import ShopPayCardVaultItemSpecParam as ShopPayCardVaultItemSpecParam from .one_password_credential_account_spec import OnePasswordCredentialAccountSpec as OnePasswordCredentialAccountSpec from .one_password_credential_account_state import ( OnePasswordCredentialAccountState as OnePasswordCredentialAccountState, ) from .payment_request_vault_item_spec_param import PaymentRequestVaultItemSpecParam as PaymentRequestVaultItemSpecParam +from .shop_pay_wallet_vault_item_spec_param import ShopPayWalletVaultItemSpecParam as ShopPayWalletVaultItemSpecParam from .credential_vault_item_spec_input_param import ( CredentialVaultItemSpecInputParam as CredentialVaultItemSpecInputParam, ) diff --git a/src/kernel/types/vaults/authorize_vault_item_operation_request_param.py b/src/kernel/types/vaults/authorize_vault_item_operation_request_param.py index b2f9bee5..00dbe39f 100644 --- a/src/kernel/types/vaults/authorize_vault_item_operation_request_param.py +++ b/src/kernel/types/vaults/authorize_vault_item_operation_request_param.py @@ -8,9 +8,8 @@ class AuthorizeVaultItemOperationRequestParam(TypedDict, total=False): - """Authorize a Link or Kernel card using its existing purchase specification. - - Use only after explicit user approval and when the item advertises authorize; follow the advertised operation's description. For a Link card, pass browser_id and page_url together while the browser is on the final checkout page: Kernel inspects that checkout, chooses how the credential is delivered, and limits fill to that checkout's origin in any browser with this vault attached. Omitting both issues a virtual card that fills any page on the merchant origin. Once approval has been requested, browser_id and page_url are ignored and authorize only re-checks the request. Browser and checkout inspection errors occur before any provider request and can be retried once the browser is on the checkout page. Kernel cards do not accept browser_id or page_url. + """ + Authorize a Link, Kernel or Shop Pay card using its existing purchase specification. Use only after explicit user approval and when the item advertises authorize; follow the advertised operation's description. For a Link or Shop Pay card, pass browser_id and page_url together while the browser is on the final checkout page: Kernel confirms that checkout is open, sends it to the provider as the merchant, and limits fill to that checkout's origin in any browser with this vault attached. For Link it also chooses how the credential is delivered. Omitting both issues a virtual card that fills any page on the merchant origin. Once approval has been requested, browser_id and page_url are ignored and authorize only re-checks the request. Browser and checkout inspection errors occur before any provider request and can be retried once the browser is on the checkout page. Kernel cards do not accept browser_id or page_url. """ type: Required[Literal["authorize"]] diff --git a/src/kernel/types/vaults/card_vault_item_spec.py b/src/kernel/types/vaults/card_vault_item_spec.py index 77a8a508..7fa62cac 100644 --- a/src/kernel/types/vaults/card_vault_item_spec.py +++ b/src/kernel/types/vaults/card_vault_item_spec.py @@ -6,6 +6,7 @@ from ..._utils import PropertyInfo from ..._models import BaseModel from .kernel_card_vault_item_spec import KernelCardVaultItemSpec +from .shop_pay_card_vault_item_spec import ShopPayCardVaultItemSpec __all__ = [ "CardVaultItemSpec", @@ -161,6 +162,6 @@ class AgentCardCardVaultItemSpec(BaseModel): CardVaultItemSpec: TypeAlias = Annotated[ - Union[LinkCardVaultItemSpec, AgentCardCardVaultItemSpec, KernelCardVaultItemSpec], + Union[LinkCardVaultItemSpec, AgentCardCardVaultItemSpec, KernelCardVaultItemSpec, ShopPayCardVaultItemSpec], PropertyInfo(discriminator="provider"), ] diff --git a/src/kernel/types/vaults/card_vault_item_spec_param.py b/src/kernel/types/vaults/card_vault_item_spec_param.py index 45825f2a..9adcc531 100644 --- a/src/kernel/types/vaults/card_vault_item_spec_param.py +++ b/src/kernel/types/vaults/card_vault_item_spec_param.py @@ -6,6 +6,7 @@ from typing_extensions import Literal, Required, TypeAlias, TypedDict from .kernel_card_vault_item_spec_param import KernelCardVaultItemSpecParam +from .shop_pay_card_vault_item_spec_param import ShopPayCardVaultItemSpecParam __all__ = [ "CardVaultItemSpecParam", @@ -161,5 +162,5 @@ class AgentCardCardVaultItemSpec(TypedDict, total=False): CardVaultItemSpecParam: TypeAlias = Union[ - LinkCardVaultItemSpec, AgentCardCardVaultItemSpec, KernelCardVaultItemSpecParam + LinkCardVaultItemSpec, AgentCardCardVaultItemSpec, KernelCardVaultItemSpecParam, ShopPayCardVaultItemSpecParam ] diff --git a/src/kernel/types/vaults/card_vault_item_state.py b/src/kernel/types/vaults/card_vault_item_state.py index 37dbdecd..5abec114 100644 --- a/src/kernel/types/vaults/card_vault_item_state.py +++ b/src/kernel/types/vaults/card_vault_item_state.py @@ -9,6 +9,7 @@ from ..._models import BaseModel from .kernel_card_state import KernelCardState from .vault_card_aliases import VaultCardAliases +from .shop_pay_card_state import ShopPayCardState from .agentcard_checkout_preparation import AgentcardCheckoutPreparation from .agentcard_checkout_authorization import AgentcardCheckoutAuthorization @@ -134,5 +135,5 @@ class AgentCardCardState(BaseModel): CardVaultItemState: TypeAlias = Annotated[ - Union[LinkCardState, AgentCardCardState, KernelCardState], PropertyInfo(discriminator="provider") + Union[LinkCardState, AgentCardCardState, KernelCardState, ShopPayCardState], PropertyInfo(discriminator="provider") ] diff --git a/src/kernel/types/vaults/confirm_transaction_vault_item_operation_request_param.py b/src/kernel/types/vaults/confirm_transaction_vault_item_operation_request_param.py index 3b0e0485..a57f9e2e 100644 --- a/src/kernel/types/vaults/confirm_transaction_vault_item_operation_request_param.py +++ b/src/kernel/types/vaults/confirm_transaction_vault_item_operation_request_param.py @@ -12,9 +12,8 @@ class ConfirmTransactionVaultItemOperationRequestParam(TypedDict, total=False): - """Report a real merchant transaction outcome for an issued Kernel Visa credential. - - Never infer success from filling checkout or receiving a credential. Supply the observed status, transaction type, actual amount, currency and timestamp. Unknown outcomes require manual reconciliation, not retry. Each purchase accepts one report. + """ + Report a real merchant transaction outcome for an issued Kernel Visa or Shop Pay credential. Shop Pay accepts PURCHASE, AUTHORIZATION and CAPTURE. Never infer success from filling checkout or receiving a credential. Supply the observed status, transaction type, actual amount, currency and timestamp. Unknown outcomes require manual reconciliation, not retry. Each purchase accepts one report. """ amount: Required[int] @@ -33,3 +32,23 @@ class ConfirmTransactionVaultItemOperationRequestParam(TypedDict, total=False): ] type: Required[Literal["confirm_transaction"]] + + failure_code: str + """ + Why the purchase did not succeed, for any other outcome, such as 3ds_challenge, + captcha, login_required, timeout or site_error. payment_declined goes only with + DECLINED, which takes no other code. Shop Pay reports it; other providers ignore + it. + """ + + order_id: str + """The merchant's order number, for an APPROVED outcome. + + Shop Pay reports it; other providers ignore it. + """ + + order_url: str + """The merchant's https order page, for an APPROVED outcome. + + Shop Pay reports it; other providers ignore it. + """ diff --git a/src/kernel/types/vaults/item_perform_operation_params.py b/src/kernel/types/vaults/item_perform_operation_params.py index c458957c..722c5daa 100644 --- a/src/kernel/types/vaults/item_perform_operation_params.py +++ b/src/kernel/types/vaults/item_perform_operation_params.py @@ -75,6 +75,26 @@ class ConfirmTransactionVaultItemOperationRequest(TypedDict, total=False): type: Required[Literal["confirm_transaction"]] + failure_code: str + """ + Why the purchase did not succeed, for any other outcome, such as 3ds_challenge, + captcha, login_required, timeout or site_error. payment_declined goes only with + DECLINED, which takes no other code. Shop Pay reports it; other providers ignore + it. + """ + + order_id: str + """The merchant's order number, for an APPROVED outcome. + + Shop Pay reports it; other providers ignore it. + """ + + order_url: str + """The merchant's https order page, for an APPROVED outcome. + + Shop Pay reports it; other providers ignore it. + """ + class CollectVaultItemOperationRequest(TypedDict, total=False): id_or_name: Required[str] diff --git a/src/kernel/types/vaults/item_upsert_params.py b/src/kernel/types/vaults/item_upsert_params.py index 1930e04e..8732049f 100644 --- a/src/kernel/types/vaults/item_upsert_params.py +++ b/src/kernel/types/vaults/item_upsert_params.py @@ -8,6 +8,7 @@ from .card_vault_item_spec_param import CardVaultItemSpecParam from .kernel_wallet_vault_item_spec_param import KernelWalletVaultItemSpecParam from .payment_request_vault_item_spec_param import PaymentRequestVaultItemSpecParam +from .shop_pay_wallet_vault_item_spec_param import ShopPayWalletVaultItemSpecParam from .credential_vault_item_spec_input_param import CredentialVaultItemSpecInputParam from .one_password_credential_account_spec_param import OnePasswordCredentialAccountSpecParam @@ -190,6 +191,7 @@ class WalletVaultItemRequestSpecAgentCardWalletVaultItemSpec(TypedDict, total=Fa WalletVaultItemRequestSpecLinkWalletVaultItemRequestSpec, WalletVaultItemRequestSpecAgentCardWalletVaultItemSpec, KernelWalletVaultItemSpecParam, + ShopPayWalletVaultItemSpecParam, ] diff --git a/src/kernel/types/vaults/shop_pay_card_state.py b/src/kernel/types/vaults/shop_pay_card_state.py new file mode 100644 index 00000000..f46e1ae2 --- /dev/null +++ b/src/kernel/types/vaults/shop_pay_card_state.py @@ -0,0 +1,59 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import TYPE_CHECKING, Dict, List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["ShopPayCardState", "Masks"] + + +class Masks(BaseModel): + brand: Optional[str] = None + + last4: Optional[str] = None + + token_last4: Optional[str] = None + """Last four digits of the network token presented to the merchant.""" + + if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, str] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + # Stub to indicate that arbitrary properties are accepted. + # To access properties that are not valid identifiers you can use `getattr`, e.g. + # `getattr(obj, '$type')` + def __getattr__(self, attr: str) -> str: ... + else: + __pydantic_extra__: Dict[str, str] + + +class ShopPayCardState(BaseModel): + """Issued Shop Pay cards retain encrypted card material for the fill operation.""" + + provider: Literal["shop_pay"] + + status: Literal[ + "requested", "pending_authorization", "ready", "consumed", "expired", "declined", "recovery_required" + ] + """declined covers a request the shopper declined or that was cancelled. + + consumed means the transaction outcome was reported with confirm_transaction; + the card is no longer stored and cannot be filled again, whatever the outcome. + recovery_required means a provider operation has an unresolved outcome and the + card cannot be used. When status_reason says the provider refused retrieval + before acceptance, no card was issued and a later read retries. Otherwise follow + status_reason: recovery for issuance can be abandoned by deleting the card and + creating a new one, and the shopper approves the new purchase separately. + Recovery while reporting a merchant transaction cannot be deleted and must be + reconciled with support. + """ + + domains: Optional[List[str]] = None + + masks: Optional[Masks] = None + + status_reason: Optional[str] = None diff --git a/src/kernel/types/vaults/shop_pay_card_vault_item_spec.py b/src/kernel/types/vaults/shop_pay_card_vault_item_spec.py new file mode 100644 index 00000000..c78153bb --- /dev/null +++ b/src/kernel/types/vaults/shop_pay_card_vault_item_spec.py @@ -0,0 +1,64 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ShopPayCardVaultItemSpec", "Total", "LineItem"] + + +class Total(BaseModel): + amount: int + """Amount in minor currency units; discounts are negative.""" + + type: str + """For example subtotal, fulfillment, tax, discount or total.""" + + +class LineItem(BaseModel): + name: str + + quantity: int + + unit_amount: int + """Unit price in minor currency units.""" + + +class ShopPayCardVaultItemSpec(BaseModel): + """Shop Pay purchase. + + amount is the limit the shopper approves; totals must include exactly one positive total no higher than it. context is shown to the shopper when they approve. Authorize it with the browser at final checkout: Kernel sends that page to Shop as the merchant and the card fills only on its origin. Without browser_id and page_url it authorizes for merchant_url. authorize returns an approval action for the shopper, who approves the purchase in Shop; share its URL unchanged when present. Report the merchant outcome with confirm_transaction, which ends the card. + """ + + amount: int + """Approval limit in minor currency units, at most 1000000.""" + + context: str + """What is being bought and why, written for the shopper.""" + + currency: str + """Shop accepts USD spend requests only.""" + + merchant_name: str + + merchant_url: str + """The merchant's https URL.""" + + provider: Literal["shop_pay"] + + totals: List[Total] + + wallet: str + """Connected Shop Pay wallet item key.""" + + line_items: Optional[List[LineItem]] = None + + merchant_country: Optional[str] = None + """ISO 3166-1 alpha-2 country of the merchant.""" + + payment_method_id: Optional[str] = None + """Card ID from the wallet's payment_methods expansion. + + Omit it to use the shopper's preferred card. + """ diff --git a/src/kernel/types/vaults/shop_pay_card_vault_item_spec_param.py b/src/kernel/types/vaults/shop_pay_card_vault_item_spec_param.py new file mode 100644 index 00000000..0b32d125 --- /dev/null +++ b/src/kernel/types/vaults/shop_pay_card_vault_item_spec_param.py @@ -0,0 +1,64 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Iterable +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["ShopPayCardVaultItemSpecParam", "Total", "LineItem"] + + +class Total(TypedDict, total=False): + amount: Required[int] + """Amount in minor currency units; discounts are negative.""" + + type: Required[str] + """For example subtotal, fulfillment, tax, discount or total.""" + + +class LineItem(TypedDict, total=False): + name: Required[str] + + quantity: Required[int] + + unit_amount: Required[int] + """Unit price in minor currency units.""" + + +class ShopPayCardVaultItemSpecParam(TypedDict, total=False): + """Shop Pay purchase. + + amount is the limit the shopper approves; totals must include exactly one positive total no higher than it. context is shown to the shopper when they approve. Authorize it with the browser at final checkout: Kernel sends that page to Shop as the merchant and the card fills only on its origin. Without browser_id and page_url it authorizes for merchant_url. authorize returns an approval action for the shopper, who approves the purchase in Shop; share its URL unchanged when present. Report the merchant outcome with confirm_transaction, which ends the card. + """ + + amount: Required[int] + """Approval limit in minor currency units, at most 1000000.""" + + context: Required[str] + """What is being bought and why, written for the shopper.""" + + currency: Required[str] + """Shop accepts USD spend requests only.""" + + merchant_name: Required[str] + + merchant_url: Required[str] + """The merchant's https URL.""" + + provider: Required[Literal["shop_pay"]] + + totals: Required[Iterable[Total]] + + wallet: Required[str] + """Connected Shop Pay wallet item key.""" + + line_items: Iterable[LineItem] + + merchant_country: str + """ISO 3166-1 alpha-2 country of the merchant.""" + + payment_method_id: str + """Card ID from the wallet's payment_methods expansion. + + Omit it to use the shopper's preferred card. + """ diff --git a/src/kernel/types/vaults/shop_pay_wallet_state.py b/src/kernel/types/vaults/shop_pay_wallet_state.py new file mode 100644 index 00000000..1e2c5f47 --- /dev/null +++ b/src/kernel/types/vaults/shop_pay_wallet_state.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ShopPayWalletState"] + + +class ShopPayWalletState(BaseModel): + provider: Literal["shop_pay"] + + status: Literal["pending_authorization", "connected", "declined", "reconnect_required", "degraded"] + + status_reason: Optional[str] = None diff --git a/src/kernel/types/vaults/shop_pay_wallet_vault_item_spec.py b/src/kernel/types/vaults/shop_pay_wallet_vault_item_spec.py new file mode 100644 index 00000000..75ad805d --- /dev/null +++ b/src/kernel/types/vaults/shop_pay_wallet_vault_item_spec.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ShopPayWalletVaultItemSpec", "Authorization", "AuthorizationClient"] + + +class AuthorizationClient(BaseModel): + type: Literal["kernel_managed"] + + +class Authorization(BaseModel): + client: AuthorizationClient + + method: Literal["oauth"] + + +class ShopPayWalletVaultItemSpec(BaseModel): + """Shop Pay wallet connected through Kernel's Shop app. + + Creation returns a shop_pay_oauth action: the shopper signs in to Shop and approves the connection, and the wallet connects. The connected wallet's payment_methods expansion lists the shopper's saved Shop Pay cards. + """ + + authorization: Authorization + + provider: Literal["shop_pay"] diff --git a/src/kernel/types/vaults/shop_pay_wallet_vault_item_spec_param.py b/src/kernel/types/vaults/shop_pay_wallet_vault_item_spec_param.py new file mode 100644 index 00000000..77e52770 --- /dev/null +++ b/src/kernel/types/vaults/shop_pay_wallet_vault_item_spec_param.py @@ -0,0 +1,28 @@ +# 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__ = ["ShopPayWalletVaultItemSpecParam", "Authorization", "AuthorizationClient"] + + +class AuthorizationClient(TypedDict, total=False): + type: Required[Literal["kernel_managed"]] + + +class Authorization(TypedDict, total=False): + client: Required[AuthorizationClient] + + method: Required[Literal["oauth"]] + + +class ShopPayWalletVaultItemSpecParam(TypedDict, total=False): + """Shop Pay wallet connected through Kernel's Shop app. + + Creation returns a shop_pay_oauth action: the shopper signs in to Shop and approves the connection, and the wallet connects. The connected wallet's payment_methods expansion lists the shopper's saved Shop Pay cards. + """ + + authorization: Required[Authorization] + + provider: Required[Literal["shop_pay"]] diff --git a/src/kernel/types/vaults/vault_item_action.py b/src/kernel/types/vaults/vault_item_action.py index 7b9e3e83..b2eccf05 100644 --- a/src/kernel/types/vaults/vault_item_action.py +++ b/src/kernel/types/vaults/vault_item_action.py @@ -10,6 +10,7 @@ __all__ = [ "VaultItemAction", "LinkOAuthAction", + "ShopPayOAuthAction", "SpendApprovalAction", "PushApprovalAction", "CollectAction", @@ -25,6 +26,13 @@ class LinkOAuthAction(BaseModel): url: str +class ShopPayOAuthAction(BaseModel): + name: Literal["shop_pay_oauth"] + + url: str + """Shop sign-in page where the shopper connects their Shop account to Kernel.""" + + class SpendApprovalAction(BaseModel): name: Literal["spend_approval"] @@ -63,6 +71,7 @@ class CardEnrollmentAction(BaseModel): VaultItemAction: TypeAlias = Annotated[ Union[ LinkOAuthAction, + ShopPayOAuthAction, OnePasswordOAuthAction, SpendApprovalAction, PushApprovalAction, diff --git a/src/kernel/types/vaults/wallet_vault_item_spec.py b/src/kernel/types/vaults/wallet_vault_item_spec.py index 6c4d9950..6082f625 100644 --- a/src/kernel/types/vaults/wallet_vault_item_spec.py +++ b/src/kernel/types/vaults/wallet_vault_item_spec.py @@ -6,6 +6,7 @@ from ..._utils import PropertyInfo from ..._models import BaseModel from .kernel_wallet_vault_item_spec import KernelWalletVaultItemSpec +from .shop_pay_wallet_vault_item_spec import ShopPayWalletVaultItemSpec __all__ = [ "WalletVaultItemSpec", @@ -96,6 +97,6 @@ class AgentCardWalletVaultItemSpec(BaseModel): WalletVaultItemSpec: TypeAlias = Annotated[ - Union[LinkWalletVaultItemSpec, AgentCardWalletVaultItemSpec, KernelWalletVaultItemSpec], + Union[LinkWalletVaultItemSpec, AgentCardWalletVaultItemSpec, KernelWalletVaultItemSpec, ShopPayWalletVaultItemSpec], PropertyInfo(discriminator="provider"), ] diff --git a/src/kernel/types/vaults/wallet_vault_item_state.py b/src/kernel/types/vaults/wallet_vault_item_state.py index e91b08d8..0628b721 100644 --- a/src/kernel/types/vaults/wallet_vault_item_state.py +++ b/src/kernel/types/vaults/wallet_vault_item_state.py @@ -6,6 +6,7 @@ from ..._utils import PropertyInfo from ..._models import BaseModel from .kernel_wallet_state import KernelWalletState +from .shop_pay_wallet_state import ShopPayWalletState __all__ = ["WalletVaultItemState", "LinkWalletState", "AgentCardWalletState"] @@ -30,5 +31,6 @@ class AgentCardWalletState(BaseModel): WalletVaultItemState: TypeAlias = Annotated[ - Union[LinkWalletState, AgentCardWalletState, KernelWalletState], PropertyInfo(discriminator="provider") + Union[LinkWalletState, AgentCardWalletState, KernelWalletState, ShopPayWalletState], + PropertyInfo(discriminator="provider"), ] diff --git a/tests/api_resources/test_browsers.py b/tests/api_resources/test_browsers.py index 9c6d8a79..fa20fda0 100644 --- a/tests/api_resources/test_browsers.py +++ b/tests/api_resources/test_browsers.py @@ -43,6 +43,7 @@ def test_method_create_with_all_params(self, client: Kernel) -> None: "name": "name", } ], + fingerprint={"os": "macos"}, gpu=False, headless=False, invocation_id="rr33xuugxj9h0bkf1rdt2bet", @@ -654,6 +655,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncKernel) -> "name": "name", } ], + fingerprint={"os": "macos"}, gpu=False, headless=False, invocation_id="rr33xuugxj9h0bkf1rdt2bet", diff --git a/tests/api_resources/vaults/test_items.py b/tests/api_resources/vaults/test_items.py index e24c9f65..cf14bbe1 100644 --- a/tests/api_resources/vaults/test_items.py +++ b/tests/api_resources/vaults/test_items.py @@ -793,6 +793,24 @@ def test_method_perform_operation_overload_3(self, client: Kernel) -> None: ) assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_perform_operation_with_all_params_overload_3(self, client: Kernel) -> None: + item = client.vaults.items.perform_operation( + key="key", + id_or_name="id_or_name", + amount=0, + currency="SEW", + occurred_at=parse_datetime("2019-12-27T18:11:19.117Z"), + status="APPROVED", + transaction_type="PURCHASE", + type="confirm_transaction", + failure_code="failure_code", + order_id="x", + order_url="https://example.com", + ) + 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: @@ -3396,6 +3414,24 @@ async def test_method_perform_operation_overload_3(self, async_client: AsyncKern ) assert_matches_type(VaultItemOperationResponse, item, path=["response"]) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_perform_operation_with_all_params_overload_3(self, async_client: AsyncKernel) -> None: + item = await async_client.vaults.items.perform_operation( + key="key", + id_or_name="id_or_name", + amount=0, + currency="SEW", + occurred_at=parse_datetime("2019-12-27T18:11:19.117Z"), + status="APPROVED", + transaction_type="PURCHASE", + type="confirm_transaction", + failure_code="failure_code", + order_id="x", + order_url="https://example.com", + ) + 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: diff --git a/tests/test_browser_routing.py b/tests/test_browser_routing.py index 1908c688..7b16e2f4 100644 --- a/tests/test_browser_routing.py +++ b/tests/test_browser_routing.py @@ -554,9 +554,37 @@ def test_browser_routing_config_from_env_defaults(monkeypatch: pytest.MonkeyPatc "process", "fs", "logs/stream", + "repl", + "webmcp", ) +def test_default_routing_sends_repl_and_webmcp_to_vm_but_not_replays(monkeypatch: pytest.MonkeyPatch) -> None: + from kernel._models import FinalRequestOptions + from kernel.lib.browser_routing.routing import ( + BrowserRoute, + BrowserRouteCache, + rewrite_direct_vm_options, + ) + + monkeypatch.delenv("KERNEL_BROWSER_ROUTING_SUBRESOURCES", raising=False) + cache = BrowserRouteCache() + cache.set(BrowserRoute(session_id="sess-1", base_url="http://browser-session.test/browser/kernel", jwt="token-abc")) + config = browser_routing_config_from_env() + + for path in ("repl", "webmcp/tools", "webmcp/invoke", "webmcp/custom-tools/ct_1"): + routed = rewrite_direct_vm_options( + FinalRequestOptions(method="post", url=f"/browsers/sess-1/{path}"), cache=cache, config=config + ) + assert str(routed.url).startswith(f"http://browser-session.test/browser/kernel/{path}") + + for path in ("replays", "replays/rec-1"): + unrouted = rewrite_direct_vm_options( + FinalRequestOptions(method="get", url=f"/browsers/sess-1/{path}"), cache=cache, config=config + ) + assert unrouted.url == f"/browsers/sess-1/{path}" + + def test_direct_vm_routing_allowlist_segment_boundary() -> None: # Pins the fix: telemetry/stream (live SSE) routes to the VM; telemetry/events # (historical, served by the control plane from S2) does NOT; and a