open-agentpay 0.2.2__tar.gz → 0.2.4__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 (49) hide show
  1. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/PKG-INFO +1 -1
  2. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/pyproject.toml +1 -1
  3. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/__init__.py +1 -1
  4. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/core/models.py +5 -2
  5. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/policy/evaluator.py +27 -18
  6. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/policy/models.py +27 -2
  7. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/policy/sqlite_engine.py +7 -6
  8. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/.gitignore +0 -0
  9. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/README.md +0 -0
  10. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/budget/__init__.py +0 -0
  11. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/client/__init__.py +0 -0
  12. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/client/client.py +0 -0
  13. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/client/errors.py +0 -0
  14. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/client/interfaces.py +0 -0
  15. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/client/signer.py +0 -0
  16. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/client/transport.py +0 -0
  17. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/core/__init__.py +0 -0
  18. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/core/codecs.py +0 -0
  19. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/core/errors.py +0 -0
  20. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/core/interop.py +0 -0
  21. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/core/state.py +0 -0
  22. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/evm/__init__.py +0 -0
  23. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/evm/constants.py +0 -0
  24. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/evm/eip3009.py +0 -0
  25. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/evm/rpc.py +0 -0
  26. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/integrations/__init__.py +0 -0
  27. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/integrations/langchain/__init__.py +0 -0
  28. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/integrations/langchain/http.py +0 -0
  29. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/integrations/langchain/tool.py +0 -0
  30. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/policy/__init__.py +0 -0
  31. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/policy/config.py +0 -0
  32. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/policy/db.py +0 -0
  33. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/policy/errors.py +0 -0
  34. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/policy/interfaces.py +0 -0
  35. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/policy/metered.py +0 -0
  36. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/protocol/__init__.py +0 -0
  37. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/protocol/codecs.py +0 -0
  38. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/protocol/errors.py +0 -0
  39. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/protocol/interop.py +0 -0
  40. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/protocol/models.py +0 -0
  41. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/protocol/state.py +0 -0
  42. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/signer/__init__.py +0 -0
  43. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/state/__init__.py +0 -0
  44. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/state/buyer_coordinator.py +0 -0
  45. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/state/errors.py +0 -0
  46. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/state/guards.py +0 -0
  47. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/state/reconciliation.py +0 -0
  48. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/testing/__init__.py +0 -0
  49. {open_agentpay-0.2.2 → open_agentpay-0.2.4}/src/agentpay/testing/mock_seller.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: open-agentpay
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: The Open-Source Payment Infrastructure & Financial Firewall for Autonomous AI Agents
5
5
  Project-URL: Homepage, https://github.com/open-agentpay/AgentPay
6
6
  Project-URL: Documentation, https://open-agentpay.github.io/AgentPay/
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "open-agentpay"
7
- version = "0.2.2"
7
+ version = "0.2.4"
8
8
  description = "The Open-Source Payment Infrastructure & Financial Firewall for Autonomous AI Agents"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -3,7 +3,7 @@ AgentPay Python SDK
3
3
  The Open-Source Payment Infrastructure & Financial Firewall for Autonomous AI Agents.
4
4
  """
5
5
 
6
- __version__ = "0.2.2"
6
+ __version__ = "0.2.4"
7
7
 
8
8
  from agentpay.client import (
9
9
  AgentPayClient,
@@ -46,6 +46,9 @@ _BYTES32_HEX_RE = re.compile(r"^(0x)?[0-9a-fA-F]{64}$")
46
46
  # Maximum signed 64-bit integer for base units
47
47
  SQLITE_MAX_INT = 9_223_372_036_854_775_807
48
48
 
49
+ # Supported fiat base currencies for AgentPay internal virtual wallet
50
+ WALLET_FIAT_CURRENCIES = frozenset({"CNY", "USD", "EUR"})
51
+
49
52
 
50
53
  def _validate_evm_address(value: str, field_name: str) -> str:
51
54
  """Validate EVM address format and normalise to lowercase."""
@@ -228,7 +231,7 @@ class PaymentChallenge(BaseModel):
228
231
  @field_validator("asset")
229
232
  @classmethod
230
233
  def _validate_asset(cls, v: str | None, info: Any) -> str | None:
231
- if info.data.get("scheme") == "agentpay-wallet" and v == "USD":
234
+ if info.data.get("scheme") == "agentpay-wallet" and v in WALLET_FIAT_CURRENCIES:
232
235
  return v
233
236
  if v is None:
234
237
  raise X402ValidationError("asset is required", code=X402ErrorCode.MISSING_REQUIRED_FIELD)
@@ -331,7 +334,7 @@ class PaymentChallenge(BaseModel):
331
334
  @model_validator(mode="after")
332
335
  def _validate_scheme_fields(self) -> PaymentChallenge:
333
336
  if self.scheme == "agentpay-wallet":
334
- if self.network != "agentpay:internal-ledger" or self.asset != "USD" or not self.resource_uri:
337
+ if self.network != "agentpay:internal-ledger" or self.asset not in WALLET_FIAT_CURRENCIES or not self.resource_uri:
335
338
  raise X402ValidationError("invalid agentpay-wallet challenge fields", code=X402ErrorCode.INVALID_SCHEME)
336
339
  if self.facilitator_url:
337
340
  raise X402ValidationError("agentpay-wallet challenge must not name an EIP-3009 facilitator", code=X402ErrorCode.INVALID_SCHEME)
@@ -60,26 +60,35 @@ def evaluate_static_rules(
60
60
  reason=f"Invalid payment amount: {request.amount!r}",
61
61
  )
62
62
 
63
- # 3. Whitelist Matrix (Strict Order: Network -> Asset -> Payee -> Domain)
64
- if request.network not in config.whitelist_networks:
63
+ # 3. Whitelist Matrix (EVM only). AgentPay's internal wallet ledger uses
64
+ # non-EVM identifiers; its trust boundary is the required gateway domain.
65
+ if not request.is_agentpay_wallet:
66
+ if request.network not in config.whitelist_networks:
67
+ return StaticEvaluationResult(
68
+ passed=False,
69
+ error_code=PolicyErrorCode.NETWORK_NOT_WHITELISTED,
70
+ reason=f"Network {request.network} is not in whitelisted networks",
71
+ )
72
+
73
+ if request.asset not in config.whitelist_assets:
74
+ return StaticEvaluationResult(
75
+ passed=False,
76
+ error_code=PolicyErrorCode.ASSET_NOT_WHITELISTED,
77
+ reason=f"Asset {request.asset} is not in whitelisted assets",
78
+ )
79
+
80
+ if request.payee not in config.whitelist_payees:
81
+ return StaticEvaluationResult(
82
+ passed=False,
83
+ error_code=PolicyErrorCode.PAYEE_NOT_WHITELISTED,
84
+ reason=f"Payee {request.payee} is not in whitelisted payees",
85
+ )
86
+
87
+ if request.is_agentpay_wallet and request.domain is None:
65
88
  return StaticEvaluationResult(
66
89
  passed=False,
67
- error_code=PolicyErrorCode.NETWORK_NOT_WHITELISTED,
68
- reason=f"Network {request.network} is not in whitelisted networks",
69
- )
70
-
71
- if request.asset not in config.whitelist_assets:
72
- return StaticEvaluationResult(
73
- passed=False,
74
- error_code=PolicyErrorCode.ASSET_NOT_WHITELISTED,
75
- reason=f"Asset {request.asset} is not in whitelisted assets",
76
- )
77
-
78
- if request.payee not in config.whitelist_payees:
79
- return StaticEvaluationResult(
80
- passed=False,
81
- error_code=PolicyErrorCode.PAYEE_NOT_WHITELISTED,
82
- reason=f"Payee {request.payee} is not in whitelisted payees",
90
+ error_code=PolicyErrorCode.DOMAIN_NOT_WHITELISTED,
91
+ reason="AgentPay wallet requests require a whitelisted gateway domain",
83
92
  )
84
93
 
85
94
  if request.domain is not None and request.domain.lower() not in config.whitelist_domains:
@@ -17,7 +17,7 @@ from __future__ import annotations
17
17
  import enum
18
18
  import re
19
19
 
20
- from pydantic import BaseModel, field_validator
20
+ from pydantic import BaseModel, field_validator, model_validator
21
21
 
22
22
  from agentpay.policy.errors import PolicyErrorCode
23
23
 
@@ -30,6 +30,9 @@ _CAIP2_RE = re.compile(r"^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}$")
30
30
 
31
31
  # Canonical EVM address: 0x followed by exactly 40 hex chars
32
32
  _EVM_ADDRESS_RE = re.compile(r"^0x[0-9a-fA-F]{40}$")
33
+ _AGENTPAY_ROUTE_RE = re.compile(r"^agentpay:route:[A-Za-z0-9_-]+$")
34
+ _AGENTPAY_INTERNAL_NETWORK = "agentpay:internal-ledger"
35
+ _WALLET_FIAT_CURRENCIES = frozenset({"CNY", "USD", "EUR"})
33
36
 
34
37
 
35
38
  # ---------------------------------------------------------------------------
@@ -219,7 +222,7 @@ class PolicyConfig(BaseModel):
219
222
  raise TypeError(f"whitelist_assets elements must be strings, got {type(addr).__name__}")
220
223
  normalized: list[str] = []
221
224
  for addr in v:
222
- normalized.append("USD" if addr == "USD" else _validate_evm_address(addr, "whitelist_assets"))
225
+ normalized.append(addr if addr in _WALLET_FIAT_CURRENCIES else _validate_evm_address(addr, "whitelist_assets"))
223
226
  return frozenset(normalized)
224
227
 
225
228
  @field_validator("whitelist_networks", mode="before")
@@ -278,18 +281,40 @@ class PaymentRequest(BaseModel):
278
281
  @field_validator("payee")
279
282
  @classmethod
280
283
  def _normalise_payee(cls, v: str) -> str:
284
+ if _AGENTPAY_ROUTE_RE.fullmatch(v):
285
+ return v
281
286
  return _validate_evm_address(v, "payee")
282
287
 
283
288
  @field_validator("asset")
284
289
  @classmethod
285
290
  def _normalise_asset(cls, v: str) -> str:
291
+ if v in _WALLET_FIAT_CURRENCIES:
292
+ return v
286
293
  return _validate_evm_address(v, "asset")
287
294
 
288
295
  @field_validator("network")
289
296
  @classmethod
290
297
  def _validate_network(cls, v: str) -> str:
298
+ if v == _AGENTPAY_INTERNAL_NETWORK:
299
+ return v
291
300
  return _validate_caip2(v)
292
301
 
302
+ @model_validator(mode="after")
303
+ def _validate_payment_identifiers(self) -> PaymentRequest:
304
+ wallet_fields = (
305
+ self.network == _AGENTPAY_INTERNAL_NETWORK,
306
+ self.asset in _WALLET_FIAT_CURRENCIES,
307
+ bool(_AGENTPAY_ROUTE_RE.fullmatch(self.payee)),
308
+ )
309
+ if any(wallet_fields) and not all(wallet_fields):
310
+ raise ValueError("AgentPay wallet requests require internal-ledger, a supported fiat currency (CNY/USD/EUR), and an agentpay:route payee")
311
+ return self
312
+
313
+ @property
314
+ def is_agentpay_wallet(self) -> bool:
315
+ """Whether this request uses AgentPay's internal wallet ledger rather than EVM settlement."""
316
+ return self.network == _AGENTPAY_INTERNAL_NETWORK
317
+
293
318
  @field_validator("domain", mode="before")
294
319
  @classmethod
295
320
  def _normalise_domain(cls, v: object) -> str | None:
@@ -14,23 +14,24 @@ References:
14
14
  from __future__ import annotations
15
15
 
16
16
  import json
17
+ import logging
17
18
  import sqlite3
18
19
  import time
19
20
  import uuid
20
21
  from collections.abc import Callable
21
22
 
22
- import logger
23
23
  from pydantic import ValidationError
24
24
 
25
25
  from agentpay.policy.errors import PolicyErrorCode, PolicyViolationError
26
- from agentpay.policy.evaluator import evaluate_static_rules, evaluate_metered_rules
26
+ from agentpay.policy.evaluator import evaluate_metered_rules, evaluate_static_rules
27
27
  from agentpay.policy.interfaces import IPolicyEngine
28
28
  from agentpay.policy.models import (
29
+ SQLITE_MAX_INT,
29
30
  BreakerState,
30
31
  PaymentRequest,
31
32
  PolicyConfig,
32
33
  PolicyDecision,
33
- ReservationToken, SQLITE_MAX_INT,
34
+ ReservationToken,
34
35
  )
35
36
 
36
37
 
@@ -757,7 +758,7 @@ class SQLitePolicyEngine(IPolicyEngine):
757
758
  self._rollback_quietly(cursor)
758
759
  return PolicyDecision(approved=False, error_code=PolicyErrorCode.DUPLICATE_INTENT)
759
760
  except Exception: # noqa: BLE001
760
- logger.exception("policy reserve_metered failed; failing closed")
761
+ logging.exception("policy reserve_metered failed; failing closed")
761
762
  self._rollback_quietly(cursor)
762
763
  return PolicyDecision(approved=False, error_code=PolicyErrorCode.POLICY_SYSTEM_ERROR)
763
764
 
@@ -774,7 +775,7 @@ class SQLitePolicyEngine(IPolicyEngine):
774
775
  or not isinstance(actual_amount, int)
775
776
  or not (0 < actual_amount <= SQLITE_MAX_INT)
776
777
  ):
777
- logger.warning("invalid actual_amount %r for %s; keeping reserved estimate", actual_amount, intent_id)
778
+ logging.warning("invalid actual_amount %r for %s; keeping reserved estimate", actual_amount, intent_id)
778
779
  actual_amount = None
779
780
 
780
781
  now = self._clock()
@@ -805,7 +806,7 @@ class SQLitePolicyEngine(IPolicyEngine):
805
806
  (now, self._config.id),
806
807
  )
807
808
  else:
808
- logger.warning("settle_metered: no RESERVED row for intent %s", intent_id)
809
+ logging.warning("settle_metered: no RESERVED row for intent %s", intent_id)
809
810
  self._conn.commit()
810
811
  except Exception:
811
812
  self._rollback_quietly(cursor)
File without changes
File without changes