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.
- agentscore_py-2.8.0/.claude/CLAUDE.md +76 -0
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/.github/workflows/ci.yml +2 -2
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/.github/workflows/publish.yml +1 -1
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/.github/workflows/security.yml +1 -1
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/PKG-INFO +3 -6
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/README.md +2 -5
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/agentscore/__init__.py +0 -4
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/agentscore/client.py +18 -36
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/agentscore/errors.py +7 -7
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/agentscore/test_mode.py +1 -1
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/agentscore/types.py +25 -63
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/pyproject.toml +6 -5
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/tests/test_client.py +8 -109
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/tests/test_types.py +1 -1
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/uv.lock +96 -81
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/vulture_whitelist.py +1 -1
- agentscore_py-2.7.0/.claude/CLAUDE.md +0 -76
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/.github/dependabot.yml +0 -0
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/.gitignore +0 -0
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/CODE_OF_CONDUCT.md +0 -0
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/CONTRIBUTING.md +0 -0
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/LICENSE +0 -0
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/SECURITY.md +0 -0
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/agentscore/py.typed +0 -0
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/lefthook.yml +0 -0
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/ruff.toml +0 -0
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/tests/__init__.py +0 -0
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/tests/conftest.py +0 -0
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/tests/test_errors.py +0 -0
- {agentscore_py-2.7.0 → agentscore_py-2.8.0}/tests/test_integration.py +0 -0
- {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.
|
|
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.
|
|
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agentscore-py
|
|
3
|
-
Version: 2.
|
|
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
|
-
#
|
|
90
|
-
|
|
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
|
-
#
|
|
62
|
-
|
|
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
|
|
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
|
|
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``
|
|
307
|
-
|
|
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"``)
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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``
|
|
449
|
-
|
|
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
|
|
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}
|
|
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
|
|
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'
|
|
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
|
|
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'
|
|
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'
|
|
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'
|
|
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
|
|
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
|
|
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)
|
|
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
|
|
67
|
-
# byte-equal). ``wallet_signer_mismatch
|
|
68
|
-
# ``wallet_auth_requires_wallet_signing
|
|
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
|
|
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
|
|
78
|
+
# Echoed on ``wallet_signer_mismatch``: the claimed wallet, normalized.
|
|
79
79
|
expected_signer: NotRequired[str]
|
|
80
|
-
# Echoed on ``wallet_signer_mismatch
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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"
|
|
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
|
|
194
|
-
#
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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)
|
|
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
|
|
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
|
+
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
|
-
#
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
|
|
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"
|