agentscore-py 2.7.0__tar.gz → 2.8.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 (34) hide show
  1. agentscore_py-2.8.0/.claude/CLAUDE.md +76 -0
  2. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/.github/workflows/ci.yml +2 -2
  3. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/.github/workflows/publish.yml +1 -1
  4. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/.github/workflows/security.yml +1 -1
  5. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/PKG-INFO +3 -6
  6. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/README.md +2 -5
  7. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/agentscore/__init__.py +0 -4
  8. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/agentscore/client.py +18 -36
  9. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/agentscore/errors.py +7 -7
  10. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/agentscore/test_mode.py +1 -1
  11. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/agentscore/types.py +25 -63
  12. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/pyproject.toml +6 -5
  13. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/tests/test_client.py +8 -109
  14. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/tests/test_types.py +1 -1
  15. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/uv.lock +96 -81
  16. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/vulture_whitelist.py +1 -1
  17. agentscore_py-2.7.0/.claude/CLAUDE.md +0 -76
  18. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  19. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  20. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  21. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/.github/dependabot.yml +0 -0
  22. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/.gitignore +0 -0
  23. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/CODE_OF_CONDUCT.md +0 -0
  24. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/CONTRIBUTING.md +0 -0
  25. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/LICENSE +0 -0
  26. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/SECURITY.md +0 -0
  27. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/agentscore/py.typed +0 -0
  28. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/lefthook.yml +0 -0
  29. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/ruff.toml +0 -0
  30. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/tests/__init__.py +0 -0
  31. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/tests/conftest.py +0 -0
  32. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/tests/test_errors.py +0 -0
  33. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/tests/test_integration.py +0 -0
  34. {agentscore_py-2.7.0 → agentscore_py-2.8.0}/tests/test_test_mode.py +0 -0
@@ -0,0 +1,76 @@
1
+ # agentscore-py
2
+
3
+ Python client for the AgentScore APIs.
4
+
5
+ ## Identity Model
6
+
7
+ Two identity paths: `X-Wallet-Address` (wallet-based) and `X-Operator-Token` (credential-based). Wallet addresses accept both EVM (`0x...` 40-hex) and Solana (base58, 32–44 chars) formats; network is auto-detected from the address shape. `assess` responses include `resolved_operator` and `linked_wallets[]` (same-operator sibling wallets, normalized per network, EVM lowercased, Solana base58 verbatim; may mix chains for cross-chain operators). `create_session` and `create_credential` responses include an `agent_memory` cross-merchant pattern hint. `create_session` also returns `next_steps.action="deliver_verify_url_and_poll"` + polling instructions. `poll_session` returns `next_steps.action` values: `continue_polling`, `retry_merchant_request_with_operator_token`, `use_stored_operator_token`, `create_new_session`, `verification_failed`, `contact_support`.
8
+
9
+ ## Methods (sync + async)
10
+
11
+ - `assess` / `aassess`: identity gate with policy (paid). Accepts `operator_token` for non-wallet agents. Response includes `linked_wallets[]` and `resolved_operator`. Optional `signer: { address, network }` opts into server-side wallet-signer-match AND OFAC SDN wallet-address screening; the response then carries both a `signer_match` block (wallet-binding verdict: `pass` / `wallet_signer_mismatch` / `wallet_auth_requires_wallet_signing`) and a `signer_sanctions` block (discriminated union: `{status: "clear"}` | `{sanctioned: True, ofac_label, sdn_uid, listed_at}` | `{status: "unavailable"}`). Wallet-OFAC SDN enforcement on the `signer` block is unconditional whenever a signer is supplied, no `policy.require_sanctions_clear` opt-in required. A `sanctioned: True` OR `status: "unavailable"` verdict flips the response `decision` to `deny` with `decision_reasons` including `sanctions_flagged` or `sanctions_check_unavailable` respectively (fail-closed; OFAC strict-liability). `policy.require_sanctions_clear` is the separate NAME-based screen on the resolved operator's KYC identity.
12
+ - `create_session` / `acreate_session`: create verification session. Returns `agent_memory` + `next_steps`.
13
+ - `poll_session` / `apoll_session`: poll session status, returns credential when verified, plus `next_steps.action`.
14
+ - `create_credential` / `acreate_credential`: create operator credential (24h TTL default). Response includes `agent_memory`.
15
+ - `list_credentials` / `alist_credentials`: list active credentials
16
+ - `revoke_credential` / `arevoke_credential`: revoke a credential
17
+ - `associate_wallet` / `aassociate_wallet`: report a signer wallet seen paying under a credential. Accepts optional `idempotency_key` (payment intent id / tx hash) so retries don't inflate transaction_count.
18
+ - `telemetry_signer_match` / `atelemetry_signer_match`: fire-and-forget POST to `/v1/telemetry/signer-match`; commerce gate uses this to report `pass` / `wallet_signer_mismatch` / `wallet_auth_requires_wallet_signing` verdicts.
19
+
20
+ ## Errors + observability
21
+
22
+ Typed error subclasses of `AgentScoreError` so callers can `except` on the specific class without parsing `err.code`: `PaymentRequiredError` (402), `TokenExpiredError` (401 token_expired, exposes parsed `verify_url` / `session_id` / `poll_secret` / `poll_url` / `next_steps` / `agent_memory` instance attributes), `InvalidCredentialError` (401 invalid_credential), `QuotaExceededError` (429 quota_exceeded, don't retry), `RateLimitedError` (429 rate_limited, retry after Retry-After), `TimeoutError` (httpx.TimeoutException, note: subclasses `AgentScoreError`, not the builtin; import explicitly from `agentscore.errors` to disambiguate). All non-timeout `httpx.HTTPError` (ConnectError, ProtocolError, NetworkError, etc.) wrap to `AgentScoreError(code="network_error", status_code=0)` for parity with node-sdk.
23
+
24
+ `assess()` / `aassess()` responses include an optional `quota` field captured from `X-Quota-Limit` / `X-Quota-Used` / `X-Quota-Reset` response headers, so callers can monitor approach-to-cap proactively before hitting 429.
25
+
26
+ ## Architecture
27
+
28
+ Single-package Python library published to PyPI.
29
+
30
+ | File | Purpose |
31
+ |------|---------|
32
+ | `agentscore/` | Source code |
33
+ | `tests/` | pytest tests |
34
+
35
+ ## Tooling
36
+
37
+ - **uv**: package manager. Use `uv sync`, `uv run`.
38
+ - **ruff**: linting + formatting. `uv run ruff check .` and `uv run ruff format --check .`.
39
+ - **ty**: type checker (Astral). `uv run ty check agentscore/`.
40
+ - **vulture**: dead code detection.
41
+ - **pytest**: tests. `uv run pytest tests/`.
42
+ - **Lefthook**: git hooks. Pre-commit: ruff. Pre-push: ty + vulture (parallel).
43
+
44
+ ## Key Commands
45
+
46
+ ```bash
47
+ uv sync --all-extras
48
+ uv run lefthook install # one-time per clone, wires pre-commit + pre-push
49
+ uv run ruff check .
50
+ uv run ruff format .
51
+ uv run ty check agentscore/
52
+ uv run pytest tests/
53
+ ```
54
+
55
+ ## Workflow
56
+
57
+ 1. Create a branch
58
+ 2. Make changes
59
+ 3. Lefthook runs ruff on commit, ty + vulture on push
60
+ 4. Open a PR, CI runs automatically
61
+ 5. Merge (squash)
62
+
63
+ ## Rules
64
+
65
+ - **No silent refactors**
66
+ - **Never commit .env files or secrets**
67
+ - **Use PRs**: never push directly to main
68
+
69
+ ## Releasing
70
+
71
+ 1. Update `version` in `pyproject.toml`
72
+ 2. Commit: `git commit -am "chore: bump to vX.Y.Z"`
73
+ 3. Tag: `git tag vX.Y.Z`
74
+ 4. Push: `git push && git push origin vX.Y.Z`
75
+
76
+ The publish workflow runs on `ubuntu-latest` (required for PyPI trusted publishing), builds, publishes to PyPI via OIDC, and creates a GitHub Release.
@@ -22,7 +22,7 @@ jobs:
22
22
  - uses: useblacksmith/checkout@v1
23
23
  - uses: astral-sh/setup-uv@v10.2.0
24
24
  with:
25
- version: "0.12.22"
25
+ version: "0.12.23"
26
26
  - run: uv python install 3.12
27
27
  - uses: actions/cache@v6
28
28
  with:
@@ -49,7 +49,7 @@ jobs:
49
49
  - uses: useblacksmith/checkout@v1
50
50
  - uses: astral-sh/setup-uv@v10.2.0
51
51
  with:
52
- version: "0.12.22"
52
+ version: "0.12.23"
53
53
  - run: uv python install ${{ matrix.python }}
54
54
  - run: uv sync --frozen --all-extras --all-groups --python ${{ matrix.python }}
55
55
  - run: uv run --python ${{ matrix.python }} pytest tests/ -q
@@ -21,7 +21,7 @@ jobs:
21
21
 
22
22
  - uses: astral-sh/setup-uv@v10.2.0
23
23
  with:
24
- version: "0.12.22"
24
+ version: "0.12.23"
25
25
 
26
26
  - name: Set version from tag
27
27
  run: sed -i "s/^version = .*/version = \"${GITHUB_REF_NAME#v}\"/" pyproject.toml
@@ -38,7 +38,7 @@ jobs:
38
38
  - uses: useblacksmith/checkout@v1
39
39
  - uses: astral-sh/setup-uv@v10.2.0
40
40
  with:
41
- version: "0.12.22"
41
+ version: "0.12.23"
42
42
  - uses: actions/setup-python@v7
43
43
  with:
44
44
  python-version: "3.13"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentscore-py
3
- Version: 2.7.0
3
+ Version: 2.8.0
4
4
  Summary: Python client for the AgentScore APIs
5
5
  Project-URL: Homepage, https://www.agentscore.com
6
6
  Project-URL: Repository, https://github.com/agentscore/python-sdk
@@ -86,11 +86,8 @@ status = client.poll_session(session["session_id"], session["poll_secret"])
86
86
  if status["status"] == "verified":
87
87
  print(status["operator_token"]) # "opc_...", use for future requests
88
88
 
89
- # Optional pre-association: attach the session to a known wallet or refresh KYC
90
- # for an existing operator credential.
91
- client.create_session(address="0x...")
92
- client.create_session(operator_token="opc_...") # KYC refresh
93
- client.create_session(kind="sign_in") # registration-only: account sign-in, no identity documents
89
+ # Registration-only session: the buyer signs in with an AgentScore account, no identity documents.
90
+ client.create_session(kind="sign_in")
94
91
  ```
95
92
 
96
93
  ### Wallet resolution
@@ -58,11 +58,8 @@ status = client.poll_session(session["session_id"], session["poll_secret"])
58
58
  if status["status"] == "verified":
59
59
  print(status["operator_token"]) # "opc_...", use for future requests
60
60
 
61
- # Optional pre-association: attach the session to a known wallet or refresh KYC
62
- # for an existing operator credential.
63
- client.create_session(address="0x...")
64
- client.create_session(operator_token="opc_...") # KYC refresh
65
- client.create_session(kind="sign_in") # registration-only: account sign-in, no identity documents
61
+ # Registration-only session: the buyer signs in with an AgentScore account, no identity documents.
62
+ client.create_session(kind="sign_in")
66
63
  ```
67
64
 
68
65
  ### Wallet resolution
@@ -15,8 +15,6 @@ from agentscore.types import (
15
15
  AccountVerification,
16
16
  AgentMemoryHint,
17
17
  AgentMemoryIdentityPaths,
18
- AipProvenance,
19
- AipSignatureMaterial,
20
18
  AssessResponse,
21
19
  AssociateWalletResponse,
22
20
  CredentialCreateErrorNextSteps,
@@ -61,8 +59,6 @@ __all__ = [
61
59
  "AgentMemoryIdentityPaths",
62
60
  "AgentScore",
63
61
  "AgentScoreError",
64
- "AipProvenance",
65
- "AipSignatureMaterial",
66
62
  "AssessResponse",
67
63
  "AssociateWalletResponse",
68
64
  "CredentialCreateErrorNextSteps",
@@ -111,7 +111,7 @@ def _build_error_from_response(response: httpx.Response) -> AgentScoreError:
111
111
  # verify_url, linked_wallets, reasons, etc. for granular denial recovery.
112
112
  details = {k: v for k, v in body.items() if k != "error"}
113
113
  except ValueError:
114
- # Body wasn't JSON or didn't have the expected shape — keep defaults.
114
+ # Body wasn't JSON or didn't have the expected shape: keep defaults.
115
115
  pass
116
116
 
117
117
  if response.status_code == 402:
@@ -133,7 +133,6 @@ if TYPE_CHECKING:
133
133
  from collections.abc import Awaitable, Callable
134
134
 
135
135
  from agentscore.types import (
136
- AipSignatureMaterial,
137
136
  AssessResponse,
138
137
  AssociateWalletResponse,
139
138
  CredentialCreateResponse,
@@ -254,30 +253,20 @@ class AgentScore:
254
253
  policy: DecisionPolicy | None = None,
255
254
  operator_token: str | None = None,
256
255
  signer: Signer | None = None,
257
- aip_token: str | None = None,
258
- aip_signature: AipSignatureMaterial | None = None,
259
256
  ) -> AssessResponse:
260
- """Assess a wallet or operator (paid, writes score on-the-fly).
257
+ """Assess a wallet or operator against a compliance policy.
258
+
259
+ ``refresh`` is deprecated: the API ignores it, and every assess is evaluated live.
261
260
 
262
261
  ``signer`` opts into server-side wallet-signer-match: when supplied,
263
262
  the API resolves the signer wallet against the claimed ``address`` and emits
264
263
  a ``signer_match`` block on the response. See :class:`Signer`.
265
-
266
- ``aip_token`` (+ ``aip_signature``) supplies an AIP Agent Identity Token in place of
267
- ``address`` / ``operator_token``: the API re-verifies the issuer signature, the RFC 9421
268
- proof-of-possession, and the attested claims, then evaluates policy against them.
269
264
  """
270
265
  body: dict[str, Any] = {}
271
266
  if address:
272
267
  body["address"] = address
273
268
  if operator_token:
274
269
  body["operator_token"] = operator_token
275
- # AIP Agent Identity Token path: the API re-verifies the IdP signature + claims
276
- # server-side and evaluates policy against the attested identity.
277
- if aip_token:
278
- body["aip_token"] = aip_token
279
- if aip_signature is not None:
280
- body["aip_signature"] = dict(aip_signature)
281
270
  if chain:
282
271
  body["chain"] = chain
283
272
  if refresh is not None:
@@ -303,9 +292,8 @@ class AgentScore:
303
292
  ) -> SessionCreateResponse:
304
293
  """Create a verification or sign-in session.
305
294
 
306
- ``address`` pre-associates the session with a known wallet (EVM ``0x...`` or
307
- Solana base58). ``operator_token`` pre-associates with an existing ``opc_...`` —
308
- e.g. refresh KYC for a credential. ``kind`` selects the session kind: ``"kyc"``
295
+ ``address`` and ``operator_token`` are deprecated: the API ignores them, so a
296
+ session is never pre-associated with a wallet or credential. ``kind`` selects the session kind: ``"kyc"``
309
297
  (the API default) runs identity verification; ``"sign_in"`` is registration-only
310
298
  (the buyer signs in with an AgentScore account, no identity documents) and mints a
311
299
  ``sign_in``-scoped credential, for merchants that key durable state on the account
@@ -368,20 +356,20 @@ class AgentScore:
368
356
  ) -> AssociateWalletResponse:
369
357
  """Report that a wallet paid under an operator credential.
370
358
 
371
- ``network`` is the key-derivation family (``"evm"`` or ``"solana"``) — EVM EOAs share
359
+ ``network`` is the key-derivation family (``"evm"`` or ``"solana"``): EVM EOAs share
372
360
  identity across every EVM chain (Base, Tempo, Ethereum, …) so one value covers them all.
373
361
 
374
- ``idempotency_key`` is optional — pass a stable per-payment key (e.g., payment intent id,
362
+ ``idempotency_key`` is optional: pass a stable per-payment key (e.g., payment intent id,
375
363
  x402 tx hash) so agent retries of the same logical payment don't inflate transaction_count.
376
364
 
377
- Fire-and-forget friendly — the returned ``first_seen`` boolean is informational only.
365
+ Fire-and-forget friendly: the returned ``first_seen`` boolean is informational only.
378
366
  """
379
367
  body: dict[str, Any] = {
380
368
  "operator_token": operator_token,
381
369
  "wallet_address": wallet_address,
382
370
  "network": network,
383
371
  }
384
- # Truthy check (not `is not None`) so empty strings don't ship a useless key —
372
+ # Truthy check (not `is not None`) so empty strings don't ship a useless key:
385
373
  # only forward when the key actually has content.
386
374
  if idempotency_key:
387
375
  if len(idempotency_key) > _IDEMPOTENCY_KEY_MAX:
@@ -403,23 +391,18 @@ class AgentScore:
403
391
  policy: DecisionPolicy | None = None,
404
392
  operator_token: str | None = None,
405
393
  signer: Signer | None = None,
406
- aip_token: str | None = None,
407
- aip_signature: AipSignatureMaterial | None = None,
408
394
  ) -> AssessResponse:
409
- """Assess a wallet or operator (paid, writes score on-the-fly).
395
+ """Assess a wallet or operator against a compliance policy.
396
+
397
+ ``refresh`` is deprecated: the API ignores it, and every assess is evaluated live.
410
398
 
411
- ``signer`` opts into server-side wallet-signer-match; ``aip_token`` (+ ``aip_signature``)
412
- supplies an AIP Agent Identity Token. Async mirror of :meth:`assess`.
399
+ ``signer`` opts into server-side wallet-signer-match. Async mirror of :meth:`assess`.
413
400
  """
414
401
  body: dict[str, Any] = {}
415
402
  if address:
416
403
  body["address"] = address
417
404
  if operator_token:
418
405
  body["operator_token"] = operator_token
419
- if aip_token:
420
- body["aip_token"] = aip_token
421
- if aip_signature is not None:
422
- body["aip_signature"] = dict(aip_signature)
423
406
  if chain:
424
407
  body["chain"] = chain
425
408
  if refresh is not None:
@@ -445,9 +428,8 @@ class AgentScore:
445
428
  ) -> SessionCreateResponse:
446
429
  """Create a verification or sign-in session.
447
430
 
448
- ``address`` pre-associates the session with a known wallet (EVM ``0x...`` or
449
- Solana base58). ``operator_token`` pre-associates with an existing ``opc_...`` —
450
- e.g. refresh KYC for a credential. ``kind`` selects the session kind: ``"kyc"``
431
+ ``address`` and ``operator_token`` are deprecated: the API ignores them, so a
432
+ session is never pre-associated with a wallet or credential. ``kind`` selects the session kind: ``"kyc"``
451
433
  (the API default) runs identity verification; ``"sign_in"`` is registration-only
452
434
  (the buyer signs in with an AgentScore account, no identity documents) and mints a
453
435
  ``sign_in``-scoped credential, for merchants that key durable state on the account
@@ -514,7 +496,7 @@ class AgentScore:
514
496
  "wallet_address": wallet_address,
515
497
  "network": network,
516
498
  }
517
- # Truthy check (not `is not None`) so empty strings don't ship a useless key —
499
+ # Truthy check (not `is not None`) so empty strings don't ship a useless key:
518
500
  # only forward when the key actually has content.
519
501
  if idempotency_key:
520
502
  if len(idempotency_key) > _IDEMPOTENCY_KEY_MAX:
@@ -527,7 +509,7 @@ class AgentScore:
527
509
  return await self._send_async(lambda: client.post("/v1/credentials/wallets", json=body))
528
510
 
529
511
  def telemetry_signer_match(self, payload: dict[str, Any]) -> None:
530
- """Fire-and-forget telemetry — report a wallet-signer-match verdict.
512
+ """Fire-and-forget telemetry: report a wallet-signer-match verdict.
531
513
 
532
514
  Tracks aggregate signer-binding behavior across merchants. Does not raise;
533
515
  failures are logged at warning level so persistent telemetry outages are visible
@@ -12,7 +12,7 @@ class AgentScoreError(Exception):
12
12
  super().__init__(message)
13
13
  self.code = code
14
14
  self.status_code = status_code
15
- # Response-body fields beyond `error.{code,message}` — e.g. verify_url,
15
+ # Response-body fields beyond `error.{code,message}`: e.g. verify_url,
16
16
  # linked_wallets, claimed_operator, actual_signer, reasons. Consumers
17
17
  # branch on these for granular recovery. Defaults to {} so callers
18
18
  # constructing this error by hand without a body can omit it.
@@ -25,17 +25,17 @@ class AgentScoreError(Exception):
25
25
 
26
26
 
27
27
  class PaymentRequiredError(AgentScoreError):
28
- """HTTP 402 — the endpoint is not enabled for this account."""
28
+ """HTTP 402: the endpoint is not enabled for this account."""
29
29
 
30
30
  def __init__(self, message: str, details: dict[str, Any] | None = None) -> None:
31
31
  super().__init__("payment_required", message, 402, details)
32
32
 
33
33
 
34
34
  class TokenExpiredError(AgentScoreError):
35
- """HTTP 401 with ``error.code = 'token_expired'`` — credential is no longer valid.
35
+ """HTTP 401 with ``error.code = 'token_expired'``: credential is no longer valid.
36
36
 
37
37
  Covers both revoked and TTL-expired credentials; the API does not distinguish
38
- which. Body carries an auto-minted verification session — exposed here so callers recover
38
+ which. Body carries an auto-minted verification session: exposed here so callers recover
39
39
  without re-parsing ``details``.
40
40
  """
41
41
 
@@ -51,7 +51,7 @@ class TokenExpiredError(AgentScoreError):
51
51
 
52
52
 
53
53
  class InvalidCredentialError(AgentScoreError):
54
- """HTTP 401 with ``error.code = 'invalid_credential'`` — operator_token doesn't exist.
54
+ """HTTP 401 with ``error.code = 'invalid_credential'``: operator_token doesn't exist.
55
55
 
56
56
  Permanent: no auto-session is issued. Caller should switch tokens or restart.
57
57
  """
@@ -61,7 +61,7 @@ class InvalidCredentialError(AgentScoreError):
61
61
 
62
62
 
63
63
  class QuotaExceededError(AgentScoreError):
64
- """HTTP 429 with ``error.code = 'quota_exceeded'`` — account-level cap reached.
64
+ """HTTP 429 with ``error.code = 'quota_exceeded'``: account-level cap reached.
65
65
 
66
66
  Don't retry; the cap won't lift through retry alone. Distinct from per-second
67
67
  :class:`RateLimitedError`.
@@ -72,7 +72,7 @@ class QuotaExceededError(AgentScoreError):
72
72
 
73
73
 
74
74
  class RateLimitedError(AgentScoreError):
75
- """HTTP 429 with ``error.code = 'rate_limited'`` — per-second sliding-window cap hit.
75
+ """HTTP 429 with ``error.code = 'rate_limited'``: per-second sliding-window cap hit.
76
76
 
77
77
  Retry after the interval indicated by the ``Retry-After`` header (typically <= 1s).
78
78
  """
@@ -2,7 +2,7 @@
2
2
 
3
3
  AgentScore's ``/v1/assess`` endpoint recognizes seven EVM addresses
4
4
  (``0x0000…0001`` through ``0x0000…0007``) as test fixtures with deterministic
5
- policy outcomes — KYC verified, sanctions clear, age gates passing — so dev/test
5
+ policy outcomes (KYC verified, sanctions clear, age gates passing) so dev/test
6
6
  interactions don't burn real KYC credits and produce predictable results.
7
7
 
8
8
  Use this in test suites and dev/staging tooling to label test-mode interactions
@@ -40,12 +40,12 @@ class Signer(TypedDict):
40
40
  When passed to ``assess()`` / ``aassess()``, the API resolves this signer wallet
41
41
  against the claimed ``address`` and emits a ``signer_match`` block on the response.
42
42
  Lets commerce gates collapse the legacy 2 follow-up assess calls (one per wallet)
43
- into the gate's primary assess call. Strictly additive — old clients that don't
43
+ into the gate's primary assess call. Strictly additive: old clients that don't
44
44
  pass this field see no ``signer_match`` on the response.
45
45
  """
46
46
 
47
47
  # Recovered payment-signer wallet. ``None`` indicates the rail carries no wallet
48
- # signature (Stripe SPT, card) — produces
48
+ # signature (Stripe SPT, card): produces
49
49
  # ``signer_match.kind = "wallet_auth_requires_wallet_signing"``.
50
50
  address: str | None
51
51
  # Key-derivation family of the signer wallet.
@@ -63,24 +63,24 @@ class SignerMatch(TypedDict):
63
63
  ``kind`` is always present; other fields depend on which kind was emitted.
64
64
  """
65
65
 
66
- # ``pass`` — claimed wallet and signer wallet resolve to the same operator (or are
67
- # byte-equal). ``wallet_signer_mismatch`` — operators differ.
68
- # ``wallet_auth_requires_wallet_signing`` — request supplied ``address: None`` (rail
66
+ # ``pass``: claimed wallet and signer wallet resolve to the same operator (or are
67
+ # byte-equal). ``wallet_signer_mismatch``: operators differ.
68
+ # ``wallet_auth_requires_wallet_signing``: request supplied ``address: None`` (rail
69
69
  # has no wallet signer); agent should switch to operator_token auth.
70
70
  kind: Literal["pass", "wallet_signer_mismatch", "wallet_auth_requires_wallet_signing"]
71
71
  # Operator the claimed wallet resolves to. ``None`` if unlinked.
72
72
  claimed_operator: NotRequired[str | None]
73
73
  # Operator the signer wallet resolves to. ``None`` if unlinked.
74
74
  signer_operator: NotRequired[str | None]
75
- # Echoed only on ``wallet_auth_requires_wallet_signing`` — the claimed wallet from
75
+ # Echoed only on ``wallet_auth_requires_wallet_signing``: the claimed wallet from
76
76
  # the request. Helps agents construct the recovery message.
77
77
  claimed_wallet: NotRequired[str]
78
- # Echoed on ``wallet_signer_mismatch`` — the claimed wallet, normalized.
78
+ # Echoed on ``wallet_signer_mismatch``: the claimed wallet, normalized.
79
79
  expected_signer: NotRequired[str]
80
- # Echoed on ``wallet_signer_mismatch`` — the signer wallet, normalized.
80
+ # Echoed on ``wallet_signer_mismatch``: the signer wallet, normalized.
81
81
  actual_signer: NotRequired[str]
82
82
  # Same-operator linked wallets the agent could re-sign from to satisfy the claim.
83
- # Mirrors the top-level ``linked_wallets`` deny-guard — omitted on ``deny`` verdicts.
83
+ # Mirrors the top-level ``linked_wallets`` deny-guard: omitted on ``deny`` verdicts.
84
84
  linked_wallets: NotRequired[list[str]]
85
85
  # JSON-encoded ``{action, steps, user_message}`` envelope for SDK denial bodies.
86
86
  # SDK consumers spread this into their 403
@@ -89,13 +89,13 @@ class SignerMatch(TypedDict):
89
89
 
90
90
 
91
91
  class SignerSanctionsClear(TypedDict):
92
- """Server-side wallet-sanctions verdict — address NOT on the OFAC SDN list."""
92
+ """Server-side wallet-sanctions verdict: address NOT on the OFAC SDN list."""
93
93
 
94
94
  status: Literal["clear"]
95
95
 
96
96
 
97
97
  class SignerSanctionsHit(TypedDict):
98
- """Server-side wallet-sanctions verdict — address IS on the OFAC SDN list.
98
+ """Server-side wallet-sanctions verdict: address IS on the OFAC SDN list.
99
99
 
100
100
  Wallet-OFAC enforcement on the ``signer`` block is unconditional whenever
101
101
  a signer is supplied. This verdict flips the response ``decision`` to
@@ -119,7 +119,7 @@ class SignerSanctionsHit(TypedDict):
119
119
 
120
120
 
121
121
  class SignerSanctionsUnavailable(TypedDict):
122
- """Server-side wallet-sanctions verdict — lookup itself failed.
122
+ """Server-side wallet-sanctions verdict: lookup itself failed.
123
123
 
124
124
  Fail-closed: the gate denies whenever a signer was supplied and the OFAC
125
125
  lookup couldn't resolve, because OFAC sanctions screening is strict-liability.
@@ -132,43 +132,10 @@ class SignerSanctionsUnavailable(TypedDict):
132
132
  SignerSanctions = SignerSanctionsClear | SignerSanctionsHit | SignerSanctionsUnavailable
133
133
 
134
134
 
135
- class AipSignatureMaterial(TypedDict):
136
- """RFC 9421 HTTP Message Signature material proving possession of the AIT-bound ``cnf`` key.
137
-
138
- Forwarded alongside ``aip_token`` so ``/v1/assess`` can re-verify proof-of-possession
139
- authoritatively; the API never sees the original agent-to-merchant request itself.
140
- """
141
-
142
- method: str # HTTP method of the original agent-to-merchant request (``@method``)
143
- authority: str # Authority/host the agent signed (``@authority``)
144
- path: str # Request path the agent signed (``@path``)
145
- signature_input: str # Raw ``Signature-Input`` header value the agent sent
146
- signature: str # Raw ``Signature`` header value the agent sent
147
-
148
-
149
- class _AipProvenanceRequired(TypedDict):
150
- issuer: str # Canonical issuer URL of the AIT
151
- subject: str # The token's ``sub``: the IdP's subject identifier for the verified human
152
-
153
-
154
- class AipProvenance(_AipProvenanceRequired, total=False):
155
- """Provenance block returned when the identity input was an AIP Agent Identity Token.
156
-
157
- Surfaces which issuer attested the identity and the trust level it asserted.
158
- """
159
-
160
- trust_level: Literal["autonomous", "human_present", "human_confirmed"]
161
- agent_provider: str
162
- # True when /v1/assess re-verified the RFC 9421 proof-of-possession. Always true on a
163
- # success response: the API fail-closes with an HTTP 400/401 error (not a 200 deny)
164
- # when possession can't be proven.
165
- pop_verified: bool
166
-
167
-
168
135
  class _AssessResponseRequired(TypedDict):
169
136
  decision: str | None
170
137
  decision_reasons: list[str]
171
- identity_method: Literal["wallet", "operator_token", "aip_token"]
138
+ identity_method: Literal["wallet", "operator_token"]
172
139
 
173
140
 
174
141
  class PolicyExplanation(TypedDict, total=False):
@@ -190,8 +157,8 @@ class QuotaInfo(TypedDict):
190
157
 
191
158
  limit: int | None
192
159
  used: int | None
193
- # ``X-Quota-Reset`` is an ISO-8601 timestamp, or the literal string "never" for lifetime
194
- # caps. The API emits "never" for tiers without a reset.
160
+ # ``X-Quota-Reset`` is the UTC date (``YYYY-MM-DD``) the calendar-month quota resets,
161
+ # e.g. "2026-06-01", or "never" when the API has no reset date for the account.
195
162
  reset: str | None
196
163
 
197
164
 
@@ -223,8 +190,6 @@ class AssessResponse(_AssessResponseRequired, total=False):
223
190
  # Server-side OFAC SDN wallet-address verdict, returned only when the request supplied
224
191
  # ``signer``. Empty otherwise.
225
192
  signer_sanctions: NotRequired[SignerSanctions]
226
- # Issuer provenance, returned only when ``identity_method == "aip_token"``.
227
- aip: NotRequired[AipProvenance]
228
193
  # Quota state for this account, captured from response headers on the success path.
229
194
  # Use to monitor approach-to-cap proactively (warn at 80%, alert at 95%) before 429.
230
195
  quota: NotRequired[QuotaInfo]
@@ -240,7 +205,7 @@ class SessionCreateRequest(TypedDict, total=False):
240
205
  class SessionCreateNextSteps(TypedDict, total=False):
241
206
  """Structured action guidance on POST /v1/sessions success.
242
207
 
243
- action is always ``deliver_verify_url_and_poll`` — tells the agent to share verify_url
208
+ action is always ``deliver_verify_url_and_poll``: tells the agent to share verify_url
244
209
  with the user and poll poll_url with X-Poll-Secret until an operator_token is issued.
245
210
  """
246
211
 
@@ -384,27 +349,27 @@ DenialCode = Literal[
384
349
  # Gate-emitted codes from commerce middleware (canonical 9-element union)
385
350
  "missing_identity",
386
351
  "identity_verification_required",
387
- # Credential is no longer valid (revoked or past its TTL — the two cases share this
352
+ # Credential is no longer valid (revoked or past its TTL: the two cases share this
388
353
  # code deliberately so the API doesn't leak which one). The 401 body carries an
389
354
  # auto-minted session so agents recover without holding an API key.
390
355
  "token_expired",
391
356
  # Credential doesn't exist at all (typo, fabricated, never minted). Permanent state;
392
357
  # no auto-session is issued because the agent may have other valid tokens to try.
393
358
  "invalid_credential",
394
- # Wallet-signer binding — claimed X-Wallet-Address must resolve to the same operator
359
+ # Wallet-signer binding: claimed X-Wallet-Address must resolve to the same operator
395
360
  # as the payment signer; wallet-auth is rejected on rails with no wallet signer.
396
361
  "wallet_signer_mismatch",
397
362
  "wallet_auth_requires_wallet_signing",
398
363
  "wallet_not_trusted",
399
364
  "api_error",
400
365
  "payment_required",
401
- # Merchant-emitted convenience codes — not emitted by the AgentScore API itself,
366
+ # Merchant-emitted convenience codes: not emitted by the AgentScore API itself,
402
367
  # but may appear in 4xx bodies passing through the SDK from merchants whose gate
403
368
  # `on_denied` hook wraps AgentScore denials into business-domain codes.
404
369
  "operator_verification_required",
405
370
  "compliance_denied",
406
371
  "compliance_error",
407
- # Decision-reason code surfaced in error.code by some merchants — kept for back-compat
372
+ # Decision-reason code surfaced in error.code by some merchants: kept for back-compat
408
373
  # with merchants that flatten policy reasons into the error envelope.
409
374
  "kyc_required",
410
375
  ]
@@ -422,15 +387,15 @@ NextStepsAction = Literal[
422
387
  "done",
423
388
  "use_operator_token",
424
389
  "regenerate_payment_from_linked_wallet",
425
- # Gate-emitted probe strategy — try wallet on signing rails, fall back to stored
390
+ # Gate-emitted probe strategy: try wallet on signing rails, fall back to stored
426
391
  # opc_..., fall back to session flow. Emitted on bare missing_identity 403s.
427
392
  "probe_identity_then_session",
428
- # Wallet signer mismatch — re-sign from expected_signer / any linked_wallets entry,
393
+ # Wallet signer mismatch: re-sign from expected_signer / any linked_wallets entry,
429
394
  # or drop X-Wallet-Address and retry with X-Operator-Token.
430
395
  "resign_or_switch_to_operator_token",
431
- # Non-signing rail (Stripe SPT, card) — drop X-Wallet-Address, use X-Operator-Token.
396
+ # Non-signing rail (Stripe SPT, card): drop X-Wallet-Address, use X-Operator-Token.
432
397
  "switch_to_operator_token",
433
- # POST /v1/sessions success — deliver verify_url + poll poll_url.
398
+ # POST /v1/sessions success: deliver verify_url + poll poll_url.
434
399
  "deliver_verify_url_and_poll",
435
400
  # Session poll states.
436
401
  "continue_polling",
@@ -446,14 +411,13 @@ NextStepsAction = Literal[
446
411
  class AgentMemoryIdentityPaths(TypedDict):
447
412
  wallet: str
448
413
  operator_token: str
449
- agent_identity: NotRequired[str]
450
414
 
451
415
 
452
416
  class AgentMemoryHint(TypedDict):
453
417
  """Structured hint emitted on bootstrap denials and POST /v1/sessions success.
454
418
 
455
419
  LLM-hosted agents should save this to their long-term memory so future AgentScore encounters
456
- skip the cold-start session flow. Contents are the pattern, not secrets —
420
+ skip the cold-start session flow. Contents are the pattern, not secrets:
457
421
  do_not_persist_in_memory names bearer credentials that belong in a credential store.
458
422
  """
459
423
 
@@ -463,8 +427,6 @@ class AgentMemoryHint(TypedDict):
463
427
  identity_check_endpoint: str
464
428
  list_wallets_endpoint: NotRequired[str]
465
429
  identity_paths: AgentMemoryIdentityPaths
466
- # Issuer allowlist a merchant accepts for AIP Agent Identity Tokens, when advertised.
467
- aip_trusted_issuers: NotRequired[list[str]]
468
430
  bootstrap: str
469
431
  do_not_persist_in_memory: list[str]
470
432
  persist_in_credential_store: list[str]
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agentscore-py"
7
- version = "2.7.0"
7
+ version = "2.8.0"
8
8
  description = "Python client for the AgentScore APIs"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -63,7 +63,8 @@ include = ["agentscore"]
63
63
  required-version = ">=0.11.0"
64
64
  # An unbounded transitive requirement lets a routine refresh resolve a BETA and
65
65
  # drag a subtree with it, and the tests pass either way, so nothing reports it.
66
- # That is how python-commerce once resolved web3 8.0.0b3 through three
67
- # unbounded parents. Blocking prereleases outright beats pinning whichever
68
- # package surfaced last, since the same shape exists on several transitives.
69
- prerelease = "disallow"
66
+ # `if-necessary` admits a prerelease only for a package that has never shipped
67
+ # a stable release; a package that reaches for a beta it does not need gets its
68
+ # own constraint (python-commerce pins web3 below 8). A blanket `disallow` also
69
+ # refuses the never-stable packages and silently holds back whatever needs them.
70
+ prerelease = "if-necessary"