agentscore-commerce 2.1.1__tar.gz → 2.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/.github/workflows/security.yml +8 -2
  2. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/CLAUDE.md +5 -1
  3. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/PKG-INFO +1 -1
  4. agentscore_commerce-2.2.0/agentscore_commerce/_warnings.py +39 -0
  5. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/challenge/how_to_pay.py +17 -6
  6. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/checkout.py +165 -7
  7. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/checkout_compute_first.py +93 -1
  8. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/core.py +4 -4
  9. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/middleware.py +4 -4
  10. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/types.py +4 -4
  11. agentscore_commerce-2.2.0/osv-scanner.toml +25 -0
  12. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/pyproject.toml +1 -1
  13. agentscore_commerce-2.2.0/tests/test_checkout_compute_first_ofac.py +207 -0
  14. agentscore_commerce-2.2.0/tests/test_checkout_wallet_ofac_default.py +259 -0
  15. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/uv.lock +123 -146
  16. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  17. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  18. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  19. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/.github/dependabot.yml +0 -0
  20. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/.github/workflows/ci.yml +0 -0
  21. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/.github/workflows/publish.yml +0 -0
  22. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/.gitignore +0 -0
  23. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/CODE_OF_CONDUCT.md +0 -0
  24. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/CONTRIBUTING.md +0 -0
  25. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/LICENSE +0 -0
  26. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/README.md +0 -0
  27. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/SECURITY.md +0 -0
  28. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/__init__.py +0 -0
  29. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/_headers.py +0 -0
  30. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/_mppx_receipt.py +0 -0
  31. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/_redis.py +0 -0
  32. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/api/__init__.py +0 -0
  33. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/challenge/__init__.py +0 -0
  34. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/challenge/accepted_methods.py +0 -0
  35. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/challenge/agent_instructions.py +0 -0
  36. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/challenge/agent_memory.py +0 -0
  37. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/challenge/body.py +0 -0
  38. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/challenge/identity.py +0 -0
  39. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/challenge/pricing.py +0 -0
  40. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/challenge/receipt.py +0 -0
  41. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/challenge/respond_402.py +0 -0
  42. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/challenge/validation_error.py +0 -0
  43. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/checkout_hooks.py +0 -0
  44. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/discovery/__init__.py +0 -0
  45. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/discovery/agentscore_content.py +0 -0
  46. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/discovery/bazaar.py +0 -0
  47. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/discovery/llms_txt.py +0 -0
  48. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/discovery/openapi.py +0 -0
  49. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/discovery/probe.py +0 -0
  50. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/discovery/redemption_md.py +0 -0
  51. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/discovery/robots_tag.py +0 -0
  52. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/discovery/skill_md.py +0 -0
  53. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/discovery/well_known.py +0 -0
  54. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/discovery/well_known_mpp.py +0 -0
  55. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/discovery/well_known_x402.py +0 -0
  56. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/errors.py +0 -0
  57. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/__init__.py +0 -0
  58. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/_denial.py +0 -0
  59. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/_response.py +0 -0
  60. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/a2a.py +0 -0
  61. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/address.py +0 -0
  62. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/aiohttp.py +0 -0
  63. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/cache.py +0 -0
  64. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/default_denied.py +0 -0
  65. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/django.py +0 -0
  66. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/fastapi.py +0 -0
  67. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/flask.py +0 -0
  68. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/policy.py +0 -0
  69. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/py.typed +0 -0
  70. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/sanic.py +0 -0
  71. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/sessions.py +0 -0
  72. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/signer.py +0 -0
  73. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/tokens.py +0 -0
  74. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/ucp.py +0 -0
  75. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/identity/ucp_jwks.py +0 -0
  76. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/middleware/__init__.py +0 -0
  77. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/middleware/_core.py +0 -0
  78. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/middleware/aiohttp.py +0 -0
  79. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/middleware/asgi.py +0 -0
  80. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/middleware/django.py +0 -0
  81. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/middleware/fastapi.py +0 -0
  82. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/middleware/flask.py +0 -0
  83. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/middleware/sanic.py +0 -0
  84. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/__init__.py +0 -0
  85. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/amounts.py +0 -0
  86. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/compose_rails.py +0 -0
  87. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/constants.py +0 -0
  88. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/default_rails.py +0 -0
  89. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/directive.py +0 -0
  90. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/dispatch.py +0 -0
  91. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/headers.py +0 -0
  92. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/idempotency.py +0 -0
  93. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/lazy.py +0 -0
  94. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/mppx_failures.py +0 -0
  95. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/mppx_server.py +0 -0
  96. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/network_kind.py +0 -0
  97. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/networks.py +0 -0
  98. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/payment_header.py +0 -0
  99. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/rail_spec.py +0 -0
  100. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/rails.py +0 -0
  101. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/settlement_override.py +0 -0
  102. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/signer.py +0 -0
  103. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/solana.py +0 -0
  104. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/usdc.py +0 -0
  105. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/wwwauthenticate.py +0 -0
  106. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/x402.py +0 -0
  107. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/x402_server.py +0 -0
  108. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/x402_settle.py +0 -0
  109. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/x402_validation.py +0 -0
  110. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/payment/zero_settle.py +0 -0
  111. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/quote_cache.py +0 -0
  112. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/stripe_multichain/__init__.py +0 -0
  113. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/stripe_multichain/mppx_stripe.py +0 -0
  114. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/stripe_multichain/pay_to_address.py +0 -0
  115. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/stripe_multichain/payment_intent.py +0 -0
  116. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/stripe_multichain/pi_cache.py +0 -0
  117. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/stripe_multichain/simulate_deposit.py +0 -0
  118. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/agentscore_commerce/stripe_multichain/simulate_dispatch.py +0 -0
  119. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/examples/README.md +0 -0
  120. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/examples/api_provider.py +0 -0
  121. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/examples/compliance_merchant.py +0 -0
  122. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/examples/compute_first_merchant.py +0 -0
  123. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/examples/identity_only.py +0 -0
  124. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/examples/multi_rail_merchant.py +0 -0
  125. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/examples/per_product_policy_merchant.py +0 -0
  126. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/examples/signed_ucp_merchant.py +0 -0
  127. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/examples/stripe_multichain_merchant.py +0 -0
  128. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/lefthook.yml +0 -0
  129. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/ruff.toml +0 -0
  130. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/scripts/regenerate_cross_lang_fixtures.py +0 -0
  131. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/__init__.py +0 -0
  132. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/conftest.py +0 -0
  133. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/node-agentscore-gate-blocked.json +0 -0
  134. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/node-agentscore-gate-full.json +0 -0
  135. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/node-capability.json +0 -0
  136. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/node-emoji-keys.json +0 -0
  137. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/node-es256-rails.json +0 -0
  138. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/node-extras-int.json +0 -0
  139. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/node-int-boundary.json +0 -0
  140. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/node-minimal.json +0 -0
  141. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/node-multikey.json +0 -0
  142. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/node-unicode.json +0 -0
  143. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/py-agentscore-gate-blocked.json +0 -0
  144. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/py-agentscore-gate-full.json +0 -0
  145. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/py-capability.json +0 -0
  146. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/py-emoji-keys.json +0 -0
  147. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/py-es256-rails.json +0 -0
  148. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/py-extras-int.json +0 -0
  149. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/py-int-boundary.json +0 -0
  150. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/py-minimal.json +0 -0
  151. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/py-multikey.json +0 -0
  152. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/fixtures/cross-lang/py-unicode.json +0 -0
  153. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_a2a.py +0 -0
  154. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_a2a_jws_roundtrip.py +0 -0
  155. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_address.py +0 -0
  156. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_agent_memory_emitter.py +0 -0
  157. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_aiohttp.py +0 -0
  158. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_amounts.py +0 -0
  159. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_api_reexport.py +0 -0
  160. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_cache.py +0 -0
  161. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_challenge.py +0 -0
  162. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_checkout.py +0 -0
  163. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_checkout_compute_first.py +0 -0
  164. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_checkout_compute_first_adapters.py +0 -0
  165. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_checkout_compute_first_settle.py +0 -0
  166. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_checkout_coverage_gaps.py +0 -0
  167. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_classify_orchestration_error.py +0 -0
  168. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_compose_rails.py +0 -0
  169. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_core.py +0 -0
  170. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_coverage_fillers.py +0 -0
  171. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_default_denied.py +0 -0
  172. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_default_rails.py +0 -0
  173. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_default_read_only_on_denied.py +0 -0
  174. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_denial.py +0 -0
  175. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_discovery.py +0 -0
  176. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_dispatch.py +0 -0
  177. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_django.py +0 -0
  178. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_extract_owner_scope.py +0 -0
  179. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_fastapi.py +0 -0
  180. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_flask.py +0 -0
  181. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_gate_quota_info.py +0 -0
  182. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_get_signer_verdict.py +0 -0
  183. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_idempotency_helper.py +0 -0
  184. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_integration.py +0 -0
  185. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_internal_helpers.py +0 -0
  186. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_lifted_helpers.py +0 -0
  187. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_load_ucp_signing_key_from_env.py +0 -0
  188. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_middleware.py +0 -0
  189. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_mppx_failures.py +0 -0
  190. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_mppx_receipt.py +0 -0
  191. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_network_kind.py +0 -0
  192. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_pay_to_address.py +0 -0
  193. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_payment_directive.py +0 -0
  194. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_payment_dispatch.py +0 -0
  195. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_payment_handlers.py +0 -0
  196. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_payment_header.py +0 -0
  197. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_payment_headers.py +0 -0
  198. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_payment_misc.py +0 -0
  199. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_payment_servers.py +0 -0
  200. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_payment_signer.py +0 -0
  201. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_policy.py +0 -0
  202. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_pricing.py +0 -0
  203. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_public_surface.py +0 -0
  204. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_quote_cache.py +0 -0
  205. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_quote_cache_redis.py +0 -0
  206. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_rail_spec.py +0 -0
  207. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_rate_limit.py +0 -0
  208. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_redis_internal.py +0 -0
  209. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_response.py +0 -0
  210. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_robots_tag.py +0 -0
  211. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_sanic.py +0 -0
  212. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_seamless_helpers.py +0 -0
  213. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_sessions.py +0 -0
  214. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_signer_match.py +0 -0
  215. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_simulate_dispatch.py +0 -0
  216. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_skill_md.py +0 -0
  217. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_solana.py +0 -0
  218. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_solana_mpp_rail_spec_post_init.py +0 -0
  219. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_stripe_multichain.py +0 -0
  220. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_tokens.py +0 -0
  221. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_ucp.py +0 -0
  222. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_ucp_cross_lang.py +0 -0
  223. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_ucp_jwks.py +0 -0
  224. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_validation_error.py +0 -0
  225. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_x402_settle_extra.py +0 -0
  226. {agentscore_commerce-2.1.1 → agentscore_commerce-2.2.0}/tests/test_zero_settle.py +0 -0
@@ -27,7 +27,10 @@ jobs:
27
27
  chmod +x osv-scanner
28
28
 
29
29
  - name: Scan dependencies
30
- run: ./osv-scanner scan source --lockfile=uv.lock --format=table
30
+ # `--config osv-scanner.toml` honors the in-repo allowlist (e.g. PYSEC-2025-183;
31
+ # see the toml for justifications). Keep the pip-audit `--ignore-vuln` flags below
32
+ # in sync with the same IDs.
33
+ run: ./osv-scanner scan source --lockfile=uv.lock --config=osv-scanner.toml --format=table
31
34
 
32
35
  pip-audit:
33
36
  name: Python Audit
@@ -44,6 +47,9 @@ jobs:
44
47
  run: pip install pip-audit
45
48
 
46
49
  - name: Audit dependencies
50
+ # `--ignore-vuln` allowlist mirrors `osv-scanner.toml` (e.g. PYSEC-2025-183;
51
+ # see that file for justifications). Keep both lists in sync.
47
52
  run: |
48
53
  uv export --format requirements-txt --no-hashes > requirements.txt
49
- pip-audit -r requirements.txt --disable-pip --no-deps
54
+ pip-audit -r requirements.txt --disable-pip --no-deps \
55
+ --ignore-vuln PYSEC-2025-183
@@ -88,7 +88,11 @@ Two identity types: wallet (`X-Wallet-Address`) and operator-token (`X-Operator-
88
88
 
89
89
  Captured wallets: `capture_wallet(...)` is fire-and-forget. Reads `operator_token` stashed during gating and POSTs to `/v1/credentials/wallets`. No-ops for wallet-authenticated requests.
90
90
 
91
- Wallet-signer-match + signer-sanctions: the gate adapter calls `extract_payment_signer(x402_header)` pre-evaluate and passes `signer={address, network}` to the SDK's `assess`. The API returns both `signer_match` (wallet-binding) and `signer_sanctions` (OFAC SDN wallet-address) on the same response; commerce caches the raw body alongside the projected verdicts so `get_signer_verdict` is a pure cache read. Under `policy.require_sanctions_clear`, an OFAC SDN signer hit already flips `decision -> deny` before the handler runs.
91
+ Wallet-signer-match + signer-sanctions: the gate adapter calls `extract_payment_signer(x402_header)` pre-evaluate and passes `signer={address, network}` to the SDK's `assess`. The API returns both `signer_match` (wallet-binding) and `signer_sanctions` (OFAC SDN wallet-address) on the same response; commerce caches the raw body alongside the projected verdicts so `get_signer_verdict` is a pure cache read. **Wallet-OFAC SDN enforcement on the `signer` block is unconditional** whenever a signer is present — no `policy.require_sanctions_clear` opt-in required. An SDN hit (or `sanctions_check_unavailable`) flips `decision -> deny` and the gate returns 403 before the handler runs.
92
+
93
+ ### Always-on wallet OFAC default for gateless merchants
94
+
95
+ Merchants who do NOT configure a `gate` on `Checkout` (or `compute_first_checkout`) still get wallet OFAC SDN enforcement on every settle leg. The SDK resolves the API key from `AGENTSCORE_API_KEY` (or `gate.api_key` if a partial gate config is present), reads `AGENTSCORE_BASE_URL` for staging/dev overrides, extracts the payment signer, and calls `/v1/assess` with the signer block. SDN hit → 403 with `wallet_not_trusted` + `decision_reasons: ["sanctions_flagged"]`. No key → one-time `[checkout]` / `[<name>.compute_first]` warning + skip (dev/testnet pattern; see `agentscore_commerce/_warnings.py`). Stripe SPT (no extractable wallet signer) skips silently — Stripe runs its own OFAC screen at customer creation. `_has_identity_gate()` detects identity-bearing flags (`require_kyc`, `require_sanctions_clear`, `min_age`, `allowed/blocked_jurisdictions`); when none are set, the 402 body omits `agent_memory` and per-rail commands strip the `-H 'X-Operator-Token: ...'` flag (gateless merchants have no operator to identify). Gate configured WITHOUT `api_key` falls through to the wallet-OFAC-only path so the merchant gets the strict-liability floor instead of silently allowing.
92
96
 
93
97
  ### Fail-open (opt-in)
94
98
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentscore-commerce
3
- Version: 2.1.1
3
+ Version: 2.2.0
4
4
  Summary: Agent commerce SDK for Python — identity middleware (FastAPI, Flask, Django, AIOHTTP, Sanic, ASGI) + payment helpers + 402 builders + discovery + Stripe multichain. The full merchant-side toolkit for AgentScore-powered agent commerce.
5
5
  Project-URL: Homepage, https://agentscore.sh
6
6
  Project-URL: Repository, https://github.com/agentscore/python-commerce
@@ -0,0 +1,39 @@
1
+ """Shared one-shot warning helpers for the SDK.
2
+
3
+ Module-level state ensures each warning fires at most once per process,
4
+ regardless of how many ``Checkout`` / ``compute_first_checkout`` instances
5
+ trigger it. Mirrors ``node-commerce/src/_warnings.ts``.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import logging
11
+
12
+ _warned_no_api_key = False
13
+
14
+
15
+ def warn_missing_api_key_once(label: str) -> None:
16
+ """Emit a one-time warning when AGENTSCORE_API_KEY is unset.
17
+
18
+ Triggered on a settle path that would otherwise enforce wallet OFAC SDN
19
+ sanctions. Both ``Checkout`` and ``compute_first_checkout`` route through
20
+ this so a single multi-surface app sees the warning ONCE, not once per
21
+ surface.
22
+
23
+ ``label`` is the caller's identifier for the log message; e.g.
24
+ ``"checkout"`` or the compute-first handler's ``name``.
25
+ """
26
+ global _warned_no_api_key
27
+ if _warned_no_api_key:
28
+ return
29
+ _warned_no_api_key = True
30
+ logging.getLogger(__name__).warning(
31
+ f"[{label}] AGENTSCORE_API_KEY is not set — wallet OFAC SDN sanctions are NOT being enforced. "
32
+ "Set the env var to enable strict-liability protection on settle."
33
+ )
34
+
35
+
36
+ def _reset_warned_no_api_key() -> None:
37
+ """Test-only: reset the warn-once flag."""
38
+ global _warned_no_api_key
39
+ _warned_no_api_key = False
@@ -43,7 +43,7 @@ async def build_how_to_pay(
43
43
  retry_body_json: str,
44
44
  total_usd: float | str,
45
45
  rails: dict[str, TempoRailSpec | X402BaseRailSpec | SolanaMppRailSpec | StripeRailSpec],
46
- op_token_placeholder: str = "<your_opc_token>", # noqa: S107 — literal placeholder, not a secret
46
+ op_token_placeholder: str | None = "<your_opc_token>", # noqa: S107 — literal placeholder, not a secret
47
47
  max_spend: float | str | None = None,
48
48
  decimals: int = 2,
49
49
  ) -> dict[str, Any]:
@@ -62,6 +62,12 @@ async def build_how_to_pay(
62
62
  ≥ $1 the default is still ``ceil(total) + 1`` formatted at ``decimals``; for
63
63
  sub-dollar totals the default uses ``total.toFixed(decimals)`` so the cap
64
64
  flag reflects the real amount instead of always being ``"1.00"``.
65
+
66
+ ``op_token_placeholder`` defaults to ``"<your_opc_token>"``. Pass ``None``
67
+ (gateless merchants) to strip the ``-H 'X-Operator-Token: ...'`` snippet
68
+ from every rail command — appropriate when the merchant doesn't run an
69
+ identity gate. The wallet OFAC SDN default (TEC-311) does NOT need an
70
+ operator token, so gateless merchants emit cleaner commands.
65
71
  """
66
72
  total_num = float(total_usd) if isinstance(total_usd, str) else total_usd
67
73
  if max_spend is not None:
@@ -70,7 +76,9 @@ async def build_how_to_pay(
70
76
  max_spend_str = f"{math.ceil(total_num) + 1:.{decimals}f}"
71
77
  else:
72
78
  max_spend_str = f"{total_num:.{decimals}f}"
73
- op_token = op_token_placeholder
79
+ gateless = op_token_placeholder is None
80
+ op_token = op_token_placeholder or "<your_opc_token>"
81
+ op_token_header_flag = "" if gateless else f"-H 'X-Operator-Token: {op_token}' "
74
82
  block: dict[str, Any] = {}
75
83
 
76
84
  tempo = rails.get("tempo")
@@ -79,11 +87,11 @@ async def build_how_to_pay(
79
87
  chain_id = tempo.chain_id
80
88
  recommend = tempo.recommend
81
89
  tempo_command = (
82
- f"tempo request -X POST -H 'X-Operator-Token: {op_token}' -H 'Content-Type: application/json' "
90
+ f"tempo request -X POST {op_token_header_flag}-H 'Content-Type: application/json' "
83
91
  f"--json '{retry_body_json}' --max-spend {max_spend_str} {url}"
84
92
  )
85
93
  pay_command = (
86
- f"agentscore-pay pay POST {url} --chain tempo -H 'X-Operator-Token: {op_token}' "
94
+ f"agentscore-pay pay POST {url} --chain tempo {op_token_header_flag}"
87
95
  f"-H 'Content-Type: application/json' -d '{retry_body_json}' --max-spend {max_spend_str}"
88
96
  )
89
97
  entry: dict[str, Any] = {
@@ -115,7 +123,7 @@ async def build_how_to_pay(
115
123
  f"least ${max_spend_str}. If the CLI is not installed, run the setup commands above first."
116
124
  ),
117
125
  "command": (
118
- f"agentscore-pay pay POST {url} --chain base -H 'X-Operator-Token: {op_token}' "
126
+ f"agentscore-pay pay POST {url} --chain base {op_token_header_flag}"
119
127
  f"-H 'Content-Type: application/json' -d '{retry_body_json}' --max-spend {max_spend_str}"
120
128
  ),
121
129
  "what_it_does": (
@@ -135,7 +143,7 @@ async def build_how_to_pay(
135
143
  f"is at least ${max_spend_str}. If the CLI is not installed, run the setup commands above first."
136
144
  ),
137
145
  "command": (
138
- f"agentscore-pay pay POST {url} --chain solana -H 'X-Operator-Token: {op_token}' "
146
+ f"agentscore-pay pay POST {url} --chain solana {op_token_header_flag}"
139
147
  f"-H 'Content-Type: application/json' -d '{retry_body_json}' --max-spend {max_spend_str}"
140
148
  ),
141
149
  "what_it_does": (
@@ -182,6 +190,9 @@ async def build_how_to_pay(
182
190
  ),
183
191
  (
184
192
  f"link-cli mpp pay {url} --spend-request-id $SPEND_ID --method POST "
193
+ f"--data '{retry_body_json}' --output-json"
194
+ if gateless
195
+ else f"link-cli mpp pay {url} --spend-request-id $SPEND_ID --method POST "
185
196
  f"--data '{retry_body_json}' --header 'X-Operator-Token: {op_token}' --output-json"
186
197
  ),
187
198
  ]
@@ -706,6 +706,30 @@ class Checkout:
706
706
  body's ``extensions`` field so Bazaar crawlers and other spec-compliant
707
707
  clients read the route's declared input/output schema."""
708
708
 
709
+ def _has_identity_gate(self) -> bool:
710
+ """Return True when the merchant configured an identity-bearing flag.
711
+
712
+ The identity-bearing flags are ``require_kyc``, ``require_sanctions_clear``
713
+ (name screening on the KYC identity), ``min_age``, or jurisdiction
714
+ lists. Wallet OFAC SDN enforcement (the always-on default) does NOT
715
+ count as an identity gate; agents don't need an AgentScore credential
716
+ to satisfy it.
717
+
718
+ Used to conditionally emit AgentScore identity boilerplate in 402
719
+ bodies (``agent_memory``, ``X-Operator-Token`` references in per-rail
720
+ commands).
721
+ """
722
+ g = self.gate
723
+ if g is None:
724
+ return False
725
+ return bool(
726
+ g.require_kyc
727
+ or g.require_sanctions_clear
728
+ or g.min_age is not None
729
+ or (g.allowed_jurisdictions and len(g.allowed_jurisdictions) > 0)
730
+ or (g.blocked_jurisdictions and len(g.blocked_jurisdictions) > 0)
731
+ )
732
+
709
733
  async def _get_x402_server(self) -> Any:
710
734
  """Resolve the x402 server.
711
735
 
@@ -899,13 +923,22 @@ class Checkout:
899
923
 
900
924
  ctx.pricing = await _maybe_await(self.compute_pricing(ctx))
901
925
 
902
- # Per-request gate: runs on the settle leg only (anonymous discovery
903
- # passes through to 402). Sets ctx.assess["identity_status"]; 403s
904
- # short-circuit. Merchant-supplied per_request_policy resolves the
905
- # policy block (read from the product row, tier, etc.).
926
+ # Per-request compliance: runs on the settle leg only (anonymous
927
+ # discovery passes through to 402).
928
+ #
929
+ # Two paths converge here:
930
+ # - Merchants with an explicit ``gate`` config run the full identity
931
+ # policy (KYC / age / sanctions / jurisdiction) via ``_run_gate``.
932
+ # - Merchants WITHOUT a ``gate`` config still get wallet OFAC SDN
933
+ # enforcement via ``_run_wallet_sanctions_only`` — the always-on
934
+ # strict-liability default. Falls back to AGENTSCORE_API_KEY env
935
+ # var when set; logs a warning and skips when no key is set
936
+ # (dev/testnet pattern).
906
937
  has_payment_header = has_x402_header(request.headers) or has_mppx_header(request.headers)
907
- if self.gate is not None and has_payment_header:
908
- gate_result = await self._run_gate(ctx)
938
+ if has_payment_header:
939
+ gate_result = (
940
+ await self._run_gate(ctx) if self.gate is not None else await self._run_wallet_sanctions_only(ctx)
941
+ )
909
942
  if gate_result is not None:
910
943
  return gate_result
911
944
 
@@ -1402,6 +1435,13 @@ class Checkout:
1402
1435
  result = await _maybe_await(gate.run_gate(ctx))
1403
1436
  return self._coerce_run_gate_result(ctx, result)
1404
1437
 
1438
+ # Gate configured without an API key — full policy enforcement requires
1439
+ # /v1/assess access, which we can't reach. Fall through to wallet OFAC
1440
+ # SDN enforcement (the strict-liability default) so the merchant still
1441
+ # gets the basic protection layer instead of silently allowing.
1442
+ if not gate.api_key:
1443
+ return await self._run_wallet_sanctions_only(ctx)
1444
+
1405
1445
  # 2. per_request_policy resolves per-product compliance (e.g. wine vs
1406
1446
  # generic merch). Return None to skip the gate entirely for this request.
1407
1447
  policy: Any = None
@@ -1500,6 +1540,117 @@ class Checkout:
1500
1540
  )
1501
1541
  return None
1502
1542
 
1543
+ async def _run_wallet_sanctions_only(self, ctx: CheckoutContext) -> CheckoutResult | None:
1544
+ """Wallet OFAC SDN enforcement.
1545
+
1546
+ Runs on settle (payment header present) when either ``self.gate`` is
1547
+ None OR a gate is configured but has no ``api_key`` to reach
1548
+ ``/v1/assess`` for full policy enforcement (fallback to the
1549
+ strict-liability default).
1550
+
1551
+ Env knobs:
1552
+ - ``AGENTSCORE_API_KEY`` — required. No key → one-time warning + skip
1553
+ (dev/testnet pattern; production should always configure a key).
1554
+ - ``AGENTSCORE_BASE_URL`` — optional override for staging/dev API
1555
+ (e.g. ``https://api-dev.agentscore.sh`` or ``http://localhost:3002``).
1556
+
1557
+ Stripe SPT (no extractable wallet signer) → skip silently; Stripe runs
1558
+ its own OFAC screen on the buyer's Stripe account at customer creation.
1559
+
1560
+ Calls ``/v1/assess`` with the signer wallet as both the primary address
1561
+ and the signer block. The API enforces signer-sanctions unconditionally
1562
+ when a signer is present (no policy flag needed). Denies on OFAC SDN
1563
+ hit; fail-closed on unavailable lookup (strict liability — falsely
1564
+ allowing a sanctioned settle is an OFAC violation, falsely denying a
1565
+ clean buyer is just bad UX).
1566
+ """
1567
+ import os
1568
+
1569
+ from agentscore_commerce._warnings import warn_missing_api_key_once
1570
+
1571
+ api_key = (self.gate.api_key if self.gate is not None else None) or os.environ.get("AGENTSCORE_API_KEY")
1572
+ if not api_key:
1573
+ warn_missing_api_key_once("checkout")
1574
+ return None
1575
+
1576
+ from agentscore_commerce.payment.signer import extract_payment_signer, read_x402_payment_header
1577
+
1578
+ x402_header = read_x402_payment_header(ctx.request.headers)
1579
+ authorization_header: str | None = None
1580
+ for header_key, header_value in ctx.request.headers.items():
1581
+ if header_key.lower() == "authorization":
1582
+ authorization_header = header_value
1583
+ break
1584
+ signer = extract_payment_signer(x402_header, authorization_header=authorization_header)
1585
+ if signer is None:
1586
+ # Stripe SPT path — no wallet signer, no OFAC check possible. Stripe
1587
+ # screens its own customer accounts; we have nothing to add here.
1588
+ return None
1589
+
1590
+ from agentscore_commerce.api import AgentScore
1591
+ from agentscore_commerce.identity._denial import denial_reason_status
1592
+ from agentscore_commerce.identity._response import denial_reason_to_body
1593
+ from agentscore_commerce.identity.types import DenialReason
1594
+
1595
+ base_url = os.environ.get("AGENTSCORE_BASE_URL")
1596
+ client_kwargs: dict[str, Any] = {"api_key": api_key}
1597
+ if base_url:
1598
+ client_kwargs["base_url"] = base_url
1599
+ client = AgentScore(**client_kwargs)
1600
+ from agentscore.errors import (
1601
+ AgentScoreError,
1602
+ InvalidCredentialError,
1603
+ TokenExpiredError,
1604
+ )
1605
+
1606
+ try:
1607
+ result = await client.aassess(
1608
+ address=signer.address,
1609
+ signer={"address": signer.address, "network": signer.network},
1610
+ )
1611
+ except (TokenExpiredError, InvalidCredentialError) as err:
1612
+ # 401 — credential issues map to invalid_credential. These are
1613
+ # unusual on the wallet-OFAC-only path (no operator_token) but
1614
+ # carried for parity with node's mapping.
1615
+ reason = DenialReason(
1616
+ code="invalid_credential" if isinstance(err, InvalidCredentialError) else "token_expired",
1617
+ message=str(err),
1618
+ )
1619
+ return CheckoutResult(
1620
+ status=denial_reason_status(reason),
1621
+ body=denial_reason_to_body(reason),
1622
+ headers={},
1623
+ reference_id=ctx.reference_id,
1624
+ settled=False,
1625
+ )
1626
+ except (AgentScoreError, Exception) as err:
1627
+ # 503 — API outage or network failure. Fail-closed: strict-liability.
1628
+ reason = DenialReason(code="api_error", message=str(err))
1629
+ return CheckoutResult(
1630
+ status=denial_reason_status(reason),
1631
+ body=denial_reason_to_body(reason),
1632
+ headers={},
1633
+ reference_id=ctx.reference_id,
1634
+ settled=False,
1635
+ )
1636
+
1637
+ decision = result.get("decision") if isinstance(result, dict) else None
1638
+ if decision == "deny":
1639
+ decision_reasons = result.get("decision_reasons") or [] if isinstance(result, dict) else []
1640
+ reason = DenialReason(
1641
+ code="wallet_not_trusted",
1642
+ reasons=list(decision_reasons),
1643
+ decision=decision,
1644
+ )
1645
+ return CheckoutResult(
1646
+ status=denial_reason_status(reason),
1647
+ body=denial_reason_to_body(reason),
1648
+ headers={},
1649
+ reference_id=ctx.reference_id,
1650
+ settled=False,
1651
+ )
1652
+ return None
1653
+
1503
1654
  def _coerce_run_gate_result(
1504
1655
  self,
1505
1656
  ctx: CheckoutContext,
@@ -1849,6 +2000,10 @@ class Checkout:
1849
2000
  total_usd=f"{ctx.pricing.amount_usd:.{pricing_decimals}f}",
1850
2001
  rails=how_to_pay_rails,
1851
2002
  decimals=pricing_decimals,
2003
+ # Merchants without an identity-bearing policy flag get clean commands
2004
+ # without an X-Operator-Token header — agents don't need one to satisfy
2005
+ # wallet OFAC enforcement (the always-on default per TEC-311).
2006
+ op_token_placeholder=None if not self._has_identity_gate() else "<your_opc_token>",
1852
2007
  )
1853
2008
  pricing_block = ctx.pricing.block or build_pricing_block(
1854
2009
  subtotal_cents=ctx.pricing.amount_usd * 100,
@@ -1898,7 +2053,10 @@ class Checkout:
1898
2053
  pricing=pricing_block,
1899
2054
  amount_usd=f"{ctx.pricing.amount_usd:.{pricing_decimals}f}",
1900
2055
  retry_body=ctx.request.body,
1901
- agent_memory=first_encounter_agent_memory(first_encounter=True),
2056
+ # Merchants without an identity-bearing gate get a clean 402: no
2057
+ # AgentScore-identity bootstrap describing a verification flow they
2058
+ # don't run. Wallet OFAC (the always-on default) doesn't need it.
2059
+ agent_memory=first_encounter_agent_memory(first_encounter=self._has_identity_gate()),
1902
2060
  product=ctx.pricing.product,
1903
2061
  extra=ctx.pricing.body_extras,
1904
2062
  x402=X402PaymentRequired(
@@ -34,7 +34,7 @@ from __future__ import annotations
34
34
  import logging
35
35
  import uuid
36
36
  from dataclasses import dataclass, field
37
- from datetime import datetime, timezone
37
+ from datetime import UTC, datetime, timezone
38
38
  from decimal import Decimal
39
39
  from typing import TYPE_CHECKING, Any
40
40
 
@@ -434,6 +434,91 @@ class ComputeFirstCheckout:
434
434
  )
435
435
  return 402, body_402, headers
436
436
 
437
+ async def _enforce_wallet_sanctions(
438
+ self,
439
+ request: ComputeFirstRequest,
440
+ reference_id: str,
441
+ ) -> tuple[int, dict[str, Any], dict[str, str]] | None:
442
+ """Always-on wallet OFAC SDN enforcement for compute-first merchants.
443
+
444
+ Mirrors :meth:`Checkout._run_wallet_sanctions_only`. Resolves API key
445
+ from ``AGENTSCORE_API_KEY``, optional base URL from
446
+ ``AGENTSCORE_BASE_URL``, extracts the signer from the payment header,
447
+ calls ``/v1/assess`` with the signer block (no policy). Denies on SDN
448
+ hit or unavailable lookup. Skips silently for Stripe SPT (no wallet
449
+ signer) and when no API key is set (log-once warning).
450
+ """
451
+ import os
452
+
453
+ from agentscore_commerce._warnings import warn_missing_api_key_once
454
+
455
+ api_key = os.environ.get("AGENTSCORE_API_KEY")
456
+ if not api_key:
457
+ warn_missing_api_key_once(f"{self.name}.compute_first")
458
+ return None
459
+
460
+ from agentscore_commerce.payment.signer import extract_payment_signer, read_x402_payment_header
461
+
462
+ x402_header = read_x402_payment_header(request.headers)
463
+ authorization_header: str | None = None
464
+ for header_key, header_value in request.headers.items():
465
+ if header_key.lower() == "authorization":
466
+ authorization_header = header_value
467
+ break
468
+ signer = extract_payment_signer(x402_header, authorization_header=authorization_header)
469
+ if signer is None:
470
+ return None # Stripe SPT — no wallet signer to screen
471
+
472
+ from agentscore_commerce.api import AgentScore
473
+
474
+ base_url = os.environ.get("AGENTSCORE_BASE_URL")
475
+ client_kwargs: dict[str, Any] = {"api_key": api_key}
476
+ if base_url:
477
+ client_kwargs["base_url"] = base_url
478
+ client = AgentScore(**client_kwargs)
479
+
480
+ from agentscore_commerce.identity._denial import denial_reason_status
481
+ from agentscore_commerce.identity._response import denial_reason_to_body
482
+ from agentscore_commerce.identity.types import DenialReason
483
+
484
+ denial_reason: DenialReason | None = None
485
+ try:
486
+ result = await client.aassess(
487
+ address=signer.address,
488
+ signer={"address": signer.address, "network": signer.network},
489
+ )
490
+ except Exception as err:
491
+ denial_reason = DenialReason(code="api_error", message=str(err))
492
+ result = None
493
+
494
+ if denial_reason is None:
495
+ decision = result.get("decision") if isinstance(result, dict) else None
496
+ if decision == "deny":
497
+ decision_reasons = list(result.get("decision_reasons") or []) if isinstance(result, dict) else []
498
+ denial_reason = DenialReason(
499
+ code="wallet_not_trusted",
500
+ reasons=decision_reasons,
501
+ decision=decision,
502
+ )
503
+
504
+ if denial_reason is not None:
505
+ # Spread denial_reason_to_body so agent_instructions / verify_url
506
+ # ride through (parity with node's enforceWalletSanctions which
507
+ # spreads denialReasonToBody into the compute-first envelope).
508
+ denial_body = denial_reason_to_body(denial_reason)
509
+ return (
510
+ denial_reason_status(denial_reason),
511
+ {
512
+ "id": reference_id,
513
+ "endpoint": self.name,
514
+ "created_at": datetime.now(UTC).isoformat(),
515
+ "payment_status": "failed",
516
+ **denial_body,
517
+ },
518
+ {"Content-Type": "application/json"},
519
+ )
520
+ return None
521
+
437
522
  async def _handle_x402_settle(
438
523
  self,
439
524
  request: ComputeFirstRequest,
@@ -693,6 +778,13 @@ class ComputeFirstCheckout:
693
778
  {"Content-Type": "application/json"},
694
779
  )
695
780
  recipients = quote.recipients if hasattr(quote, "recipients") else {}
781
+ # Wallet OFAC SDN enforcement (always-on default — mirrors
782
+ # Checkout._run_wallet_sanctions_only). Strict-liability check
783
+ # before the rail-specific settle so funds don't move (x402) or
784
+ # order doesn't fulfill (MPP) for a sanctioned wallet.
785
+ ofac_denial = await self._enforce_wallet_sanctions(request, reference_id)
786
+ if ofac_denial is not None:
787
+ return ofac_denial
696
788
  if has_x402_header(request.headers):
697
789
  return await self._handle_x402_settle(
698
790
  request, reference_id, quote.body, int(quote.price_cents), recipients
@@ -415,10 +415,10 @@ class AgentScoreCore:
415
415
  request — single round trip. Returns ``None`` when the gate didn't run with
416
416
  a signer (operator-token-only paths, discovery legs).
417
417
 
418
- Under ``policy.require_sanctions_clear``, OFAC SDN wallet-address hits are
419
- already enforced by the gate (decision -> deny before the handler runs);
420
- merchant code typically only needs this for the signer_match wallet-binding
421
- verdict.
418
+ Wallet-OFAC SDN enforcement is unconditional whenever a signer is in the
419
+ request SDN wallet-address hits are already enforced by the gate
420
+ (decision -> deny before the handler runs); merchant code typically only
421
+ needs this for the signer_match wallet-binding verdict.
422
422
  """
423
423
  claimed_norm = normalize_address(claimed_address)
424
424
  key = self._cache_key(address=claimed_norm)
@@ -322,10 +322,10 @@ def get_signer_verdict(request: Request) -> SignerVerdict | None:
322
322
  Returns ``None`` when the gate didn't run with a signer: operator-token-only paths,
323
323
  discovery legs that arrive without a payment credential, and fail-open pass-throughs.
324
324
 
325
- Under ``policy.require_sanctions_clear``, OFAC SDN wallet-address hits already flip the
326
- gate to ``decision=deny`` before the handler runs — merchant code typically only reads
327
- ``signer_match`` for the wallet-binding verdict (e.g. via
328
- :func:`build_signer_mismatch_body`).
325
+ Wallet-OFAC SDN enforcement is unconditional whenever a signer is in the request —
326
+ SDN wallet-address hits already flip the gate to ``decision=deny`` before the handler
327
+ runs. Merchant code typically only reads ``signer_match`` for the wallet-binding
328
+ verdict (e.g. via :func:`build_signer_mismatch_body`).
329
329
  """
330
330
  state = request.scope.get("state", {}).get(GATE_STATE_KEY)
331
331
  if not state or not state.get("wallet_address"):
@@ -128,10 +128,10 @@ class SignerVerdict:
128
128
  ``/v1/assess`` call (single round trip). ``signer_match`` describes the wallet-
129
129
  binding; ``signer_sanctions`` describes the OFAC SDN wallet-address check.
130
130
 
131
- Under ``policy.require_sanctions_clear``, a ``signer_sanctions`` hit OR an
132
- unavailable lookup already flips ``decision -> deny`` inside the gate before the
133
- handler runs merchant code typically only needs to read ``signer_match`` here
134
- for the wallet-binding verdict.
131
+ Wallet-OFAC SDN enforcement is unconditional whenever a signer is in the request —
132
+ a ``signer_sanctions`` hit OR an unavailable lookup already flips
133
+ ``decision -> deny`` inside the gate before the handler runs. Merchant code
134
+ typically only needs to read ``signer_match`` here for the wallet-binding verdict.
135
135
  """
136
136
 
137
137
  signer_match: VerifyWalletSignerResult | None = None
@@ -0,0 +1,25 @@
1
+ # OSV scanner allowlist
2
+ #
3
+ # Each entry suppresses a vulnerability that the OSV / pip-audit scanners
4
+ # flag but that we've reviewed and determined does NOT apply to us. The
5
+ # `pip-audit` step in `.github/workflows/security.yml` passes the same IDs
6
+ # via `--ignore-vuln` flags; keep both in sync.
7
+ #
8
+ # Conventions:
9
+ # - `id` — the OSV / PYSEC / GHSA identifier
10
+ # - `ignoreUntil` — review-by date (ISO 8601). Leave empty for permanent
11
+ # ignores (e.g. disputed-by-upstream). Otherwise pick a date that forces
12
+ # us to re-evaluate when the upstream releases a fix.
13
+ # - `reason` — short justification
14
+
15
+ [[IgnoredVulns]]
16
+ id = "PYSEC-2025-183"
17
+ # pyjwt 2.10.1+ flagged for "weak encryption" — disputed by upstream pyjwt
18
+ # maintainers ("the key length is chosen by the application that uses the
19
+ # library"). No fixed version exists in the advisory. pyjwt is a transitive
20
+ # of cdp-sdk (Coinbase) under the optional `coinbase` extra; merchants
21
+ # install it only when wiring the x402 Coinbase facilitator, and the JWT
22
+ # keys used in that path are user-provided. Library is library; the dispute
23
+ # is justified for our usage. Re-evaluate if upstream releases a fix.
24
+ ignoreUntil = "2027-01-01T00:00:00Z"
25
+ reason = "Disputed by pyjwt maintainers; key-length is an application-layer concern; cdp-sdk's keys are user-provided"
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agentscore-commerce"
7
- version = "2.1.1"
7
+ version = "2.2.0"
8
8
  description = "Agent commerce SDK for Python — identity middleware (FastAPI, Flask, Django, AIOHTTP, Sanic, ASGI) + payment helpers + 402 builders + discovery + Stripe multichain. The full merchant-side toolkit for AgentScore-powered agent commerce."
9
9
  readme = "README.md"
10
10
  license = "MIT"