agentscore-py 2.4.2__tar.gz → 2.5.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.4.2 → agentscore_py-2.5.0}/.github/workflows/ci.yml +2 -2
  2. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/.github/workflows/publish.yml +3 -3
  3. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/.github/workflows/security.yml +1 -1
  4. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/CODE_OF_CONDUCT.md +1 -1
  5. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/PKG-INFO +21 -21
  6. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/README.md +19 -19
  7. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/SECURITY.md +1 -1
  8. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/agentscore/__init__.py +4 -0
  9. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/agentscore/client.py +26 -5
  10. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/agentscore/errors.py +1 -1
  11. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/agentscore/types.py +42 -5
  12. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/pyproject.toml +2 -2
  13. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/tests/test_client.py +148 -14
  14. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/tests/test_types.py +5 -5
  15. agentscore_py-2.5.0/uv.lock +452 -0
  16. agentscore_py-2.4.2/uv.lock +0 -467
  17. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/.claude/CLAUDE.md +0 -0
  18. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  19. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  20. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  21. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/.github/dependabot.yml +0 -0
  22. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/.gitignore +0 -0
  23. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/CONTRIBUTING.md +0 -0
  24. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/LICENSE +0 -0
  25. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/agentscore/py.typed +0 -0
  26. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/agentscore/test_mode.py +0 -0
  27. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/lefthook.yml +0 -0
  28. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/ruff.toml +0 -0
  29. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/tests/__init__.py +0 -0
  30. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/tests/conftest.py +0 -0
  31. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/tests/test_errors.py +0 -0
  32. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/tests/test_integration.py +0 -0
  33. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/tests/test_test_mode.py +0 -0
  34. {agentscore_py-2.4.2 → agentscore_py-2.5.0}/vulture_whitelist.py +0 -0
@@ -19,9 +19,9 @@ jobs:
19
19
  timeout-minutes: 10
20
20
  steps:
21
21
  - uses: useblacksmith/checkout@v1
22
- - uses: astral-sh/setup-uv@v8.1.0
22
+ - uses: astral-sh/setup-uv@v8.3.0
23
23
  - run: uv python install 3.12
24
- - uses: actions/cache@v5
24
+ - uses: actions/cache@v6
25
25
  with:
26
26
  path: |
27
27
  ~/.cache/uv
@@ -17,9 +17,9 @@ jobs:
17
17
  runs-on: ubuntu-latest
18
18
  timeout-minutes: 15
19
19
  steps:
20
- - uses: actions/checkout@v6
20
+ - uses: actions/checkout@v7
21
21
 
22
- - uses: astral-sh/setup-uv@v8.1.0
22
+ - uses: astral-sh/setup-uv@v8.3.0
23
23
 
24
24
  - name: Set version from tag
25
25
  run: sed -i "s/^version = .*/version = \"${GITHUB_REF_NAME#v}\"/" pyproject.toml
@@ -27,7 +27,7 @@ jobs:
27
27
  - run: uv build
28
28
 
29
29
  - name: Publish to PyPI
30
- uses: pypa/gh-action-pypi-publish@release/v1
30
+ uses: pypa/gh-action-pypi-publish@v1.14.0
31
31
 
32
32
  - name: Create GitHub Release
33
33
  run: gh release create "$GITHUB_REF_NAME" --generate-notes
@@ -35,7 +35,7 @@ jobs:
35
35
  timeout-minutes: 5
36
36
  steps:
37
37
  - uses: useblacksmith/checkout@v1
38
- - uses: astral-sh/setup-uv@v8.1.0
38
+ - uses: astral-sh/setup-uv@v8.3.0
39
39
  - uses: actions/setup-python@v6
40
40
  with:
41
41
  python-version: "3.13"
@@ -24,7 +24,7 @@ Examples of unacceptable behavior:
24
24
 
25
25
  ## Enforcement
26
26
 
27
- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the project team at engineering@agentscore.sh. All complaints will be reviewed and investigated promptly and fairly.
27
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the project team at engineering@agentscore.com. All complaints will be reviewed and investigated promptly and fairly.
28
28
 
29
29
  ## Attribution
30
30
 
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentscore-py
3
- Version: 2.4.2
3
+ Version: 2.5.0
4
4
  Summary: Python client for the AgentScore APIs
5
- Project-URL: Homepage, https://agentscore.sh
5
+ Project-URL: Homepage, https://www.agentscore.com
6
6
  Project-URL: Repository, https://github.com/agentscore/python-sdk
7
7
  Project-URL: Issues, https://github.com/agentscore/python-sdk/issues
8
8
  License-Expression: MIT
@@ -26,7 +26,7 @@ Description-Content-Type: text/markdown
26
26
  [![PyPI version](https://img.shields.io/pypi/v/agentscore-py.svg)](https://pypi.org/project/agentscore-py/)
27
27
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
28
28
 
29
- Python client for the [AgentScore](https://agentscore.sh) APIs.
29
+ The official Python client for [AgentScore](https://www.agentscore.com). Identity verification and compliance gating for agentic commerce (KYC, age, sanctions, jurisdiction), sync and async.
30
30
 
31
31
  ## Install
32
32
 
@@ -84,7 +84,7 @@ print(session["next_steps"]["action"]) # "deliver_verify_url_and_poll"
84
84
 
85
85
  status = client.poll_session(session["session_id"], session["poll_secret"])
86
86
  if status["status"] == "verified":
87
- print(status["operator_token"]) # "opc_..." — use for future requests
87
+ print(status["operator_token"]) # "opc_...", use for future requests
88
88
 
89
89
  # Optional pre-association: attach the session to a known wallet or refresh KYC
90
90
  # for an existing operator credential.
@@ -94,7 +94,7 @@ client.create_session(operator_token="opc_...") # KYC refresh
94
94
 
95
95
  ### Wallet resolution
96
96
 
97
- `assess()` responses include `resolved_operator` and `linked_wallets` — all same-operator sibling wallets (claimed via SIWE or captured via prior `associate_wallet`). The list may mix EVM addresses (`0x...` lowercased) and Solana addresses (base58, case-preserved) for cross-chain operators; merchants doing wallet-signer-match checks should accept a payment signed by any address in the list, regardless of chain. The `address` parameter on `assess()` and `get_reputation()` accepts either format — network is auto-detected from the address shape.
97
+ `assess()` responses include `resolved_operator` and `linked_wallets`, all same-operator sibling wallets (claimed via SIWE or captured via prior `associate_wallet`). The list may mix EVM addresses (`0x...` lowercased) and Solana addresses (base58, case-preserved) for cross-chain operators; merchants doing wallet-signer-match checks should accept a payment signed by any address in the list, regardless of chain. The `address` parameter on `assess()` and `get_reputation()` accepts either format; the network is auto-detected from the address shape.
98
98
 
99
99
  ### Server-side signer-match + sanctions screening
100
100
 
@@ -124,9 +124,9 @@ if sanctions and sanctions.get("sanctioned"):
124
124
  print("OFAC hit:", sanctions["ofac_label"], sanctions["sdn_uid"])
125
125
  ```
126
126
 
127
- Wallet-OFAC SDN enforcement on the `signer` block is unconditional whenever a signer is supplied — no `policy.require_sanctions_clear` opt-in required. The API flips `decision` to `deny` when `signer_sanctions` is `sanctioned: True` OR `status: "unavailable"`, 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.
127
+ Wallet-OFAC SDN enforcement on the `signer` block is unconditional whenever a signer is supplied; no `policy.require_sanctions_clear` opt-in is required. The API flips `decision` to `deny` when `signer_sanctions` is `sanctioned: True` OR `status: "unavailable"`, 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.
128
128
 
129
- Pass `signer["address"] = None` for rails without a wallet signer (Stripe SPT, card-only). The API responds with `signer_match["kind"] == "wallet_auth_requires_wallet_signing"` and a parsed `agent_instructions` block telling the agent to switch to `X-Operator-Token` auth — spread the block directly into a 403 body.
129
+ Pass `signer["address"] = None` for rails without a wallet signer (Stripe SPT, card-only). The API responds with `signer_match["kind"] == "wallet_auth_requires_wallet_signing"` and a parsed `agent_instructions` block telling the agent to switch to `X-Operator-Token` auth, spread the block directly into a 403 body.
130
130
 
131
131
  ### Credential Management
132
132
 
@@ -140,14 +140,14 @@ client.revoke_credential(cred["id"])
140
140
 
141
141
  ### Report an Agent's Wallet (Cross-Merchant Attribution)
142
142
 
143
- After an agent authenticated via `operator_token` completes a payment, report the signer wallet so AgentScore can build a cross-merchant credential↔wallet profile. Fire-and-forget — `first_seen` is informational only. `network` is the key-derivation family: `"evm"` for any EVM chain (Base, Tempo, Ethereum, …) or `"solana"` for Solana.
143
+ After an agent authenticated via `operator_token` completes a payment, report the signer wallet so AgentScore can build a cross-merchant credential↔wallet profile. Fire-and-forget, `first_seen` is informational only. `network` is the key-derivation family: `"evm"` for any EVM chain (Base, Tempo, Ethereum, …) or `"solana"` for Solana.
144
144
 
145
145
  ```python
146
146
  client.associate_wallet(
147
147
  operator_token="opc_...",
148
148
  wallet_address=signer_from_payment, # e.g. EIP-3009 `from` or Tempo MPP DID address
149
149
  network="evm",
150
- idempotency_key=payment_intent_id, # optional — agent retries of the same payment no-op
150
+ idempotency_key=payment_intent_id, # optional, agent retries of the same payment no-op
151
151
  )
152
152
  ```
153
153
 
@@ -184,8 +184,8 @@ with AgentScore(api_key="as_live_...") as client:
184
184
 
185
185
  | Parameter | Default | Description |
186
186
  |---------------|-----------------------------|--------------------------|
187
- | `api_key` | `None` | API key from [agentscore.sh](https://agentscore.sh) |
188
- | `base_url` | `https://api.agentscore.sh` | API base URL |
187
+ | `api_key` | _(required)_ | API key from [agentscore.com](https://www.agentscore.com); raises `ValueError` if missing |
188
+ | `base_url` | `https://api.agentscore.com` | API base URL |
189
189
  | `timeout` | `10.0` | Request timeout (seconds)|
190
190
  | `user_agent` | `None` | Prepended to the default `User-Agent` as `"{user_agent} (agentscore-py/{version})"`. Use to attribute API calls to your app. |
191
191
 
@@ -202,7 +202,7 @@ except AgentScoreError as e:
202
202
  print(e.code, e.status_code, str(e))
203
203
  ```
204
204
 
205
- `AgentScoreError.details` carries the rest of the response body — `verify_url`, `linked_wallets`, `claimed_operator`, `actual_signer`, `expected_signer`, `reasons`, `agent_memory` — so callers can branch on granular denial codes without re-parsing.
205
+ `AgentScoreError.details` carries the rest of the response body (`verify_url`, `linked_wallets`, `claimed_operator`, `actual_signer`, `expected_signer`, `reasons`, `agent_memory`) so callers can branch on granular denial codes without re-parsing.
206
206
 
207
207
  ### Typed error classes
208
208
 
@@ -211,11 +211,11 @@ For status-code-specific recovery, the SDK raises typed subclasses of `AgentScor
211
211
  | Class | Triggered by | What it adds |
212
212
  |---|---|---|
213
213
  | `PaymentRequiredError` | HTTP 402 | The endpoint is not enabled for this account |
214
- | `TokenExpiredError` | HTTP 401 with `error.code = "token_expired"` | Parsed body fields on the instance: `verify_url`, `session_id`, `poll_secret`, `poll_url`, `next_steps`, `agent_memory` — recover without re-parsing `details` |
215
- | `InvalidCredentialError` | HTTP 401 with `error.code = "invalid_credential"` | Permanent — switch tokens or restart |
214
+ | `TokenExpiredError` | HTTP 401 with `error.code = "token_expired"` | Parsed body fields on the instance: `verify_url`, `session_id`, `poll_secret`, `poll_url`, `next_steps`, `agent_memory`, recover without re-parsing `details` |
215
+ | `InvalidCredentialError` | HTTP 401 with `error.code = "invalid_credential"` | Permanent, switch tokens or restart |
216
216
  | `QuotaExceededError` | HTTP 429 with `error.code = "quota_exceeded"` | Account-level cap reached; don't retry |
217
217
  | `RateLimitedError` | HTTP 429 with `error.code = "rate_limited"` | Per-second sliding-window cap; retry after `Retry-After` |
218
- | `TimeoutError` | `httpx.TimeoutException` (connect/read/write/pool timeout) | Distinct from generic network errors. Note: subclasses `AgentScoreError`, **not** the builtin `TimeoutError` — import explicitly from `agentscore.errors` to disambiguate. |
218
+ | `TimeoutError` | `httpx.TimeoutException` (connect/read/write/pool timeout) | Distinct from generic network errors. Note: subclasses `AgentScoreError`, **not** the builtin `TimeoutError`, import explicitly from `agentscore.errors` to disambiguate. |
219
219
 
220
220
  All non-timeout `httpx.HTTPError` (ConnectError, ProtocolError, NetworkError, etc.) are wrapped as `AgentScoreError(code="network_error", status_code=0)`.
221
221
 
@@ -230,11 +230,11 @@ try:
230
230
  except TokenExpiredError as e:
231
231
  print("Verify at:", e.verify_url, "poll with:", e.poll_secret)
232
232
  except QuotaExceededError as e:
233
- print("Account quota reached — surface to user; don't retry.")
233
+ print("Account quota reached, surface to user; don't retry.")
234
234
  except AgentScoreTimeoutError:
235
- print("Network timeout — retry with backoff.")
235
+ print("Network timeout, retry with backoff.")
236
236
  except AgentScoreError as e:
237
- print(e.code, e.message)
237
+ print(e.code, str(e))
238
238
  ```
239
239
 
240
240
  ## Quota observability
@@ -247,10 +247,10 @@ quota = result.get("quota")
247
247
  if quota and quota["limit"] and quota["used"]:
248
248
  pct = (quota["used"] / quota["limit"]) * 100
249
249
  if pct > 80:
250
- print(f"AgentScore quota at {pct:.1f}% — resets {quota['reset']}")
250
+ print(f"AgentScore quota at {pct:.1f}%, resets {quota['reset']}")
251
251
  ```
252
252
 
253
- `quota` is absent when the API doesn't emit the headers (Enterprise / unlimited tiers). On a 429 response the SDK raises `QuotaExceededError` / `RateLimitedError` instead of returning a body, so `quota` is only readable on successful calls — drive proactive alerting off the success-path field.
253
+ `quota` is absent when the API doesn't emit the headers (Enterprise / unlimited tiers). On a 429 response the SDK raises `QuotaExceededError` / `RateLimitedError` instead of returning a body, so `quota` is only readable on successful calls; drive proactive alerting off the success-path field.
254
254
 
255
255
  ## Telemetry
256
256
 
@@ -258,7 +258,7 @@ if quota and quota["limit"] and quota["used"]:
258
258
 
259
259
  ## Documentation
260
260
 
261
- - [API Reference](https://docs.agentscore.sh)
261
+ - [API Reference](https://docs.agentscore.com)
262
262
 
263
263
  ## License
264
264
 
@@ -3,7 +3,7 @@
3
3
  [![PyPI version](https://img.shields.io/pypi/v/agentscore-py.svg)](https://pypi.org/project/agentscore-py/)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
5
 
6
- Python client for the [AgentScore](https://agentscore.sh) APIs.
6
+ The official Python client for [AgentScore](https://www.agentscore.com). Identity verification and compliance gating for agentic commerce (KYC, age, sanctions, jurisdiction), sync and async.
7
7
 
8
8
  ## Install
9
9
 
@@ -61,7 +61,7 @@ print(session["next_steps"]["action"]) # "deliver_verify_url_and_poll"
61
61
 
62
62
  status = client.poll_session(session["session_id"], session["poll_secret"])
63
63
  if status["status"] == "verified":
64
- print(status["operator_token"]) # "opc_..." — use for future requests
64
+ print(status["operator_token"]) # "opc_...", use for future requests
65
65
 
66
66
  # Optional pre-association: attach the session to a known wallet or refresh KYC
67
67
  # for an existing operator credential.
@@ -71,7 +71,7 @@ client.create_session(operator_token="opc_...") # KYC refresh
71
71
 
72
72
  ### Wallet resolution
73
73
 
74
- `assess()` responses include `resolved_operator` and `linked_wallets` — all same-operator sibling wallets (claimed via SIWE or captured via prior `associate_wallet`). The list may mix EVM addresses (`0x...` lowercased) and Solana addresses (base58, case-preserved) for cross-chain operators; merchants doing wallet-signer-match checks should accept a payment signed by any address in the list, regardless of chain. The `address` parameter on `assess()` and `get_reputation()` accepts either format — network is auto-detected from the address shape.
74
+ `assess()` responses include `resolved_operator` and `linked_wallets`, all same-operator sibling wallets (claimed via SIWE or captured via prior `associate_wallet`). The list may mix EVM addresses (`0x...` lowercased) and Solana addresses (base58, case-preserved) for cross-chain operators; merchants doing wallet-signer-match checks should accept a payment signed by any address in the list, regardless of chain. The `address` parameter on `assess()` and `get_reputation()` accepts either format; the network is auto-detected from the address shape.
75
75
 
76
76
  ### Server-side signer-match + sanctions screening
77
77
 
@@ -101,9 +101,9 @@ if sanctions and sanctions.get("sanctioned"):
101
101
  print("OFAC hit:", sanctions["ofac_label"], sanctions["sdn_uid"])
102
102
  ```
103
103
 
104
- Wallet-OFAC SDN enforcement on the `signer` block is unconditional whenever a signer is supplied — no `policy.require_sanctions_clear` opt-in required. The API flips `decision` to `deny` when `signer_sanctions` is `sanctioned: True` OR `status: "unavailable"`, 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.
104
+ Wallet-OFAC SDN enforcement on the `signer` block is unconditional whenever a signer is supplied; no `policy.require_sanctions_clear` opt-in is required. The API flips `decision` to `deny` when `signer_sanctions` is `sanctioned: True` OR `status: "unavailable"`, 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.
105
105
 
106
- Pass `signer["address"] = None` for rails without a wallet signer (Stripe SPT, card-only). The API responds with `signer_match["kind"] == "wallet_auth_requires_wallet_signing"` and a parsed `agent_instructions` block telling the agent to switch to `X-Operator-Token` auth — spread the block directly into a 403 body.
106
+ Pass `signer["address"] = None` for rails without a wallet signer (Stripe SPT, card-only). The API responds with `signer_match["kind"] == "wallet_auth_requires_wallet_signing"` and a parsed `agent_instructions` block telling the agent to switch to `X-Operator-Token` auth, spread the block directly into a 403 body.
107
107
 
108
108
  ### Credential Management
109
109
 
@@ -117,14 +117,14 @@ client.revoke_credential(cred["id"])
117
117
 
118
118
  ### Report an Agent's Wallet (Cross-Merchant Attribution)
119
119
 
120
- After an agent authenticated via `operator_token` completes a payment, report the signer wallet so AgentScore can build a cross-merchant credential↔wallet profile. Fire-and-forget — `first_seen` is informational only. `network` is the key-derivation family: `"evm"` for any EVM chain (Base, Tempo, Ethereum, …) or `"solana"` for Solana.
120
+ After an agent authenticated via `operator_token` completes a payment, report the signer wallet so AgentScore can build a cross-merchant credential↔wallet profile. Fire-and-forget, `first_seen` is informational only. `network` is the key-derivation family: `"evm"` for any EVM chain (Base, Tempo, Ethereum, …) or `"solana"` for Solana.
121
121
 
122
122
  ```python
123
123
  client.associate_wallet(
124
124
  operator_token="opc_...",
125
125
  wallet_address=signer_from_payment, # e.g. EIP-3009 `from` or Tempo MPP DID address
126
126
  network="evm",
127
- idempotency_key=payment_intent_id, # optional — agent retries of the same payment no-op
127
+ idempotency_key=payment_intent_id, # optional, agent retries of the same payment no-op
128
128
  )
129
129
  ```
130
130
 
@@ -161,8 +161,8 @@ with AgentScore(api_key="as_live_...") as client:
161
161
 
162
162
  | Parameter | Default | Description |
163
163
  |---------------|-----------------------------|--------------------------|
164
- | `api_key` | `None` | API key from [agentscore.sh](https://agentscore.sh) |
165
- | `base_url` | `https://api.agentscore.sh` | API base URL |
164
+ | `api_key` | _(required)_ | API key from [agentscore.com](https://www.agentscore.com); raises `ValueError` if missing |
165
+ | `base_url` | `https://api.agentscore.com` | API base URL |
166
166
  | `timeout` | `10.0` | Request timeout (seconds)|
167
167
  | `user_agent` | `None` | Prepended to the default `User-Agent` as `"{user_agent} (agentscore-py/{version})"`. Use to attribute API calls to your app. |
168
168
 
@@ -179,7 +179,7 @@ except AgentScoreError as e:
179
179
  print(e.code, e.status_code, str(e))
180
180
  ```
181
181
 
182
- `AgentScoreError.details` carries the rest of the response body — `verify_url`, `linked_wallets`, `claimed_operator`, `actual_signer`, `expected_signer`, `reasons`, `agent_memory` — so callers can branch on granular denial codes without re-parsing.
182
+ `AgentScoreError.details` carries the rest of the response body (`verify_url`, `linked_wallets`, `claimed_operator`, `actual_signer`, `expected_signer`, `reasons`, `agent_memory`) so callers can branch on granular denial codes without re-parsing.
183
183
 
184
184
  ### Typed error classes
185
185
 
@@ -188,11 +188,11 @@ For status-code-specific recovery, the SDK raises typed subclasses of `AgentScor
188
188
  | Class | Triggered by | What it adds |
189
189
  |---|---|---|
190
190
  | `PaymentRequiredError` | HTTP 402 | The endpoint is not enabled for this account |
191
- | `TokenExpiredError` | HTTP 401 with `error.code = "token_expired"` | Parsed body fields on the instance: `verify_url`, `session_id`, `poll_secret`, `poll_url`, `next_steps`, `agent_memory` — recover without re-parsing `details` |
192
- | `InvalidCredentialError` | HTTP 401 with `error.code = "invalid_credential"` | Permanent — switch tokens or restart |
191
+ | `TokenExpiredError` | HTTP 401 with `error.code = "token_expired"` | Parsed body fields on the instance: `verify_url`, `session_id`, `poll_secret`, `poll_url`, `next_steps`, `agent_memory`, recover without re-parsing `details` |
192
+ | `InvalidCredentialError` | HTTP 401 with `error.code = "invalid_credential"` | Permanent, switch tokens or restart |
193
193
  | `QuotaExceededError` | HTTP 429 with `error.code = "quota_exceeded"` | Account-level cap reached; don't retry |
194
194
  | `RateLimitedError` | HTTP 429 with `error.code = "rate_limited"` | Per-second sliding-window cap; retry after `Retry-After` |
195
- | `TimeoutError` | `httpx.TimeoutException` (connect/read/write/pool timeout) | Distinct from generic network errors. Note: subclasses `AgentScoreError`, **not** the builtin `TimeoutError` — import explicitly from `agentscore.errors` to disambiguate. |
195
+ | `TimeoutError` | `httpx.TimeoutException` (connect/read/write/pool timeout) | Distinct from generic network errors. Note: subclasses `AgentScoreError`, **not** the builtin `TimeoutError`, import explicitly from `agentscore.errors` to disambiguate. |
196
196
 
197
197
  All non-timeout `httpx.HTTPError` (ConnectError, ProtocolError, NetworkError, etc.) are wrapped as `AgentScoreError(code="network_error", status_code=0)`.
198
198
 
@@ -207,11 +207,11 @@ try:
207
207
  except TokenExpiredError as e:
208
208
  print("Verify at:", e.verify_url, "poll with:", e.poll_secret)
209
209
  except QuotaExceededError as e:
210
- print("Account quota reached — surface to user; don't retry.")
210
+ print("Account quota reached, surface to user; don't retry.")
211
211
  except AgentScoreTimeoutError:
212
- print("Network timeout — retry with backoff.")
212
+ print("Network timeout, retry with backoff.")
213
213
  except AgentScoreError as e:
214
- print(e.code, e.message)
214
+ print(e.code, str(e))
215
215
  ```
216
216
 
217
217
  ## Quota observability
@@ -224,10 +224,10 @@ quota = result.get("quota")
224
224
  if quota and quota["limit"] and quota["used"]:
225
225
  pct = (quota["used"] / quota["limit"]) * 100
226
226
  if pct > 80:
227
- print(f"AgentScore quota at {pct:.1f}% — resets {quota['reset']}")
227
+ print(f"AgentScore quota at {pct:.1f}%, resets {quota['reset']}")
228
228
  ```
229
229
 
230
- `quota` is absent when the API doesn't emit the headers (Enterprise / unlimited tiers). On a 429 response the SDK raises `QuotaExceededError` / `RateLimitedError` instead of returning a body, so `quota` is only readable on successful calls — drive proactive alerting off the success-path field.
230
+ `quota` is absent when the API doesn't emit the headers (Enterprise / unlimited tiers). On a 429 response the SDK raises `QuotaExceededError` / `RateLimitedError` instead of returning a body, so `quota` is only readable on successful calls; drive proactive alerting off the success-path field.
231
231
 
232
232
  ## Telemetry
233
233
 
@@ -235,7 +235,7 @@ if quota and quota["limit"] and quota["used"]:
235
235
 
236
236
  ## Documentation
237
237
 
238
- - [API Reference](https://docs.agentscore.sh)
238
+ - [API Reference](https://docs.agentscore.com)
239
239
 
240
240
  ## License
241
241
 
@@ -6,7 +6,7 @@ If you discover a security vulnerability, please report it responsibly.
6
6
 
7
7
  **Do not open a public GitHub issue for security vulnerabilities.**
8
8
 
9
- Instead, email us at **security@agentscore.sh** with:
9
+ Instead, email us at **security@agentscore.com** with:
10
10
 
11
11
  - Description of the vulnerability
12
12
  - Steps to reproduce
@@ -17,6 +17,8 @@ from agentscore.types import (
17
17
  AgentMemoryHint,
18
18
  AgentMemoryIdentityPaths,
19
19
  AgentSummary,
20
+ AipProvenance,
21
+ AipSignatureMaterial,
20
22
  AssessResponse,
21
23
  AssociateWalletResponse,
22
24
  ChainEntry,
@@ -77,6 +79,8 @@ __all__ = [
77
79
  "AgentScore",
78
80
  "AgentScoreError",
79
81
  "AgentSummary",
82
+ "AipProvenance",
83
+ "AipSignatureMaterial",
80
84
  "AssessResponse",
81
85
  "AssociateWalletResponse",
82
86
  "ChainEntry",
@@ -27,7 +27,9 @@ _MAX_RETRY_WAIT_SECONDS = 10.0
27
27
  def _retry_after_seconds(response: httpx.Response) -> float:
28
28
  raw = response.headers.get("retry-after", "1")
29
29
  try:
30
- return min(float(raw), _MAX_RETRY_WAIT_SECONDS)
30
+ # Clamp the lower bound to 0: a negative ``Retry-After`` (buggy/malicious proxy)
31
+ # would otherwise reach ``time.sleep(-n)`` and raise ValueError, crashing the retry.
32
+ return max(0.0, min(float(raw), _MAX_RETRY_WAIT_SECONDS))
31
33
  except (TypeError, ValueError):
32
34
  return 1.0
33
35
 
@@ -131,6 +133,7 @@ if TYPE_CHECKING:
131
133
  from collections.abc import Awaitable, Callable
132
134
 
133
135
  from agentscore.types import (
136
+ AipSignatureMaterial,
134
137
  AssessResponse,
135
138
  AssociateWalletResponse,
136
139
  CredentialCreateResponse,
@@ -151,12 +154,12 @@ class AgentScore:
151
154
  def __init__(
152
155
  self,
153
156
  api_key: str,
154
- base_url: str = "https://api.agentscore.sh",
157
+ base_url: str = "https://api.agentscore.com",
155
158
  timeout: float = 10.0,
156
159
  user_agent: str | None = None,
157
160
  ):
158
161
  if not api_key:
159
- raise ValueError("AgentScore API key is required. Get one at https://agentscore.sh/sign-up")
162
+ raise ValueError("AgentScore API key is required. Get one at https://www.agentscore.com/sign-up")
160
163
  self.api_key = api_key
161
164
  self.base_url = base_url.rstrip("/")
162
165
  self.timeout = timeout
@@ -260,18 +263,30 @@ class AgentScore:
260
263
  policy: DecisionPolicy | None = None,
261
264
  operator_token: str | None = None,
262
265
  signer: Signer | None = None,
266
+ aip_token: str | None = None,
267
+ aip_signature: AipSignatureMaterial | None = None,
263
268
  ) -> AssessResponse:
264
269
  """Assess a wallet or operator (paid, writes score on-the-fly).
265
270
 
266
271
  ``signer`` opts into server-side wallet-signer-match: when supplied,
267
272
  the API resolves the signer wallet against the claimed ``address`` and emits
268
273
  a ``signer_match`` block on the response. See :class:`Signer`.
274
+
275
+ ``aip_token`` (+ ``aip_signature``) supplies an AIP Agent Identity Token in place of
276
+ ``address`` / ``operator_token``: the API re-verifies the issuer signature, the RFC 9421
277
+ proof-of-possession, and the attested claims, then evaluates policy against them.
269
278
  """
270
279
  body: dict[str, Any] = {}
271
280
  if address:
272
281
  body["address"] = address
273
282
  if operator_token:
274
283
  body["operator_token"] = operator_token
284
+ # AIP Agent Identity Token path: the API re-verifies the IdP signature + claims
285
+ # server-side and evaluates policy against the attested identity.
286
+ if aip_token:
287
+ body["aip_token"] = aip_token
288
+ if aip_signature is not None:
289
+ body["aip_signature"] = dict(aip_signature)
275
290
  if chain:
276
291
  body["chain"] = chain
277
292
  if refresh is not None:
@@ -398,17 +413,23 @@ class AgentScore:
398
413
  policy: DecisionPolicy | None = None,
399
414
  operator_token: str | None = None,
400
415
  signer: Signer | None = None,
416
+ aip_token: str | None = None,
417
+ aip_signature: AipSignatureMaterial | None = None,
401
418
  ) -> AssessResponse:
402
419
  """Assess a wallet or operator (paid, writes score on-the-fly).
403
420
 
404
- ``signer`` opts into server-side wallet-signer-match — async mirror of
405
- :meth:`assess`.
421
+ ``signer`` opts into server-side wallet-signer-match; ``aip_token`` (+ ``aip_signature``)
422
+ supplies an AIP Agent Identity Token. Async mirror of :meth:`assess`.
406
423
  """
407
424
  body: dict[str, Any] = {}
408
425
  if address:
409
426
  body["address"] = address
410
427
  if operator_token:
411
428
  body["operator_token"] = operator_token
429
+ if aip_token:
430
+ body["aip_token"] = aip_token
431
+ if aip_signature is not None:
432
+ body["aip_signature"] = dict(aip_signature)
412
433
  if chain:
413
434
  body["chain"] = chain
414
435
  if refresh is not None:
@@ -34,7 +34,7 @@ class PaymentRequiredError(AgentScoreError):
34
34
  class TokenExpiredError(AgentScoreError):
35
35
  """HTTP 401 with ``error.code = 'token_expired'`` — credential is no longer valid.
36
36
 
37
- Covers both revoked and TTL-expired credentials; the API deliberately doesn't disclose
37
+ Covers both revoked and TTL-expired credentials; the API does not distinguish
38
38
  which. Body carries an auto-minted verification session — exposed here so callers recover
39
39
  without re-parsing ``details``.
40
40
  """
@@ -191,7 +191,7 @@ class SignerMatch(TypedDict):
191
191
  """Server-side wallet-signer-match verdict.
192
192
 
193
193
  Emitted on ``AssessResponse.signer_match`` when the request supplied
194
- ``signer``. Mirrors the verdict shape commerce SDK gates produce locally;
194
+ ``signer``. Mirrors the verdict shape the gate produces locally;
195
195
  SDK consumers spread this into 403 bodies verbatim instead of re-deriving via 2
196
196
  extra ``/v1/assess`` round trips.
197
197
 
@@ -218,7 +218,7 @@ class SignerMatch(TypedDict):
218
218
  # Mirrors the top-level ``linked_wallets`` deny-guard — omitted on ``deny`` verdicts.
219
219
  linked_wallets: NotRequired[list[str]]
220
220
  # JSON-encoded ``{action, steps, user_message}`` envelope for SDK denial bodies.
221
- # Authoritative copy lives server-side; SDK consumers spread this into their 403
221
+ # SDK consumers spread this into their 403
222
222
  # body without re-parsing.
223
223
  agent_instructions: NotRequired[str]
224
224
 
@@ -257,8 +257,7 @@ class SignerSanctionsUnavailable(TypedDict):
257
257
  """Server-side wallet-sanctions verdict — lookup itself failed.
258
258
 
259
259
  Fail-closed: the gate denies whenever a signer was supplied and the OFAC
260
- lookup couldn't resolve. Falsely allowing a sanctioned settle is an OFAC
261
- strict-liability violation; falsely denying a clean buyer is bad UX.
260
+ lookup couldn't resolve, because OFAC sanctions screening is strict-liability.
262
261
  """
263
262
 
264
263
  status: Literal["unavailable"]
@@ -268,10 +267,43 @@ class SignerSanctionsUnavailable(TypedDict):
268
267
  SignerSanctions = SignerSanctionsClear | SignerSanctionsHit | SignerSanctionsUnavailable
269
268
 
270
269
 
270
+ class AipSignatureMaterial(TypedDict):
271
+ """RFC 9421 HTTP Message Signature material proving possession of the AIT-bound ``cnf`` key.
272
+
273
+ Forwarded alongside ``aip_token`` so ``/v1/assess`` can re-verify proof-of-possession
274
+ authoritatively; the API never sees the original agent-to-merchant request itself.
275
+ """
276
+
277
+ method: str # HTTP method of the original agent-to-merchant request (``@method``)
278
+ authority: str # Authority/host the agent signed (``@authority``)
279
+ path: str # Request path the agent signed (``@path``)
280
+ signature_input: str # Raw ``Signature-Input`` header value the agent sent
281
+ signature: str # Raw ``Signature`` header value the agent sent
282
+
283
+
284
+ class _AipProvenanceRequired(TypedDict):
285
+ issuer: str # Canonical issuer URL of the AIT
286
+ subject: str # The token's ``sub``: the IdP's subject identifier for the verified human
287
+
288
+
289
+ class AipProvenance(_AipProvenanceRequired, total=False):
290
+ """Provenance block returned when the identity input was an AIP Agent Identity Token.
291
+
292
+ Surfaces which issuer attested the identity and the trust level it asserted.
293
+ """
294
+
295
+ trust_level: Literal["autonomous", "human_present", "human_confirmed"]
296
+ agent_provider: str
297
+ # True when /v1/assess re-verified the RFC 9421 proof-of-possession. Always true on a
298
+ # success response: the API fail-closes with an HTTP 400/401 error (not a 200 deny)
299
+ # when possession can't be proven.
300
+ pop_verified: bool
301
+
302
+
271
303
  class _AssessResponseRequired(TypedDict):
272
304
  decision: str | None
273
305
  decision_reasons: list[str]
274
- identity_method: Literal["wallet", "operator_token"]
306
+ identity_method: Literal["wallet", "operator_token", "aip_token"]
275
307
 
276
308
 
277
309
  class PolicyExplanation(TypedDict, total=False):
@@ -314,6 +346,8 @@ class AssessResponse(_AssessResponseRequired, total=False):
314
346
  # Server-side OFAC SDN wallet-address verdict, returned only when the request supplied
315
347
  # ``signer``. Empty otherwise.
316
348
  signer_sanctions: NotRequired[SignerSanctions]
349
+ # Issuer provenance, returned only when ``identity_method == "aip_token"``.
350
+ aip: NotRequired[AipProvenance]
317
351
  # Quota state for this account, captured from response headers on the success path.
318
352
  # Use to monitor approach-to-cap proactively (warn at 80%, alert at 95%) before 429.
319
353
  quota: NotRequired[QuotaInfo]
@@ -529,6 +563,7 @@ NextStepsAction = Literal[
529
563
  class AgentMemoryIdentityPaths(TypedDict):
530
564
  wallet: str
531
565
  operator_token: str
566
+ agent_identity: NotRequired[str]
532
567
 
533
568
 
534
569
  class AgentMemoryHint(TypedDict):
@@ -545,6 +580,8 @@ class AgentMemoryHint(TypedDict):
545
580
  identity_check_endpoint: str
546
581
  list_wallets_endpoint: NotRequired[str]
547
582
  identity_paths: AgentMemoryIdentityPaths
583
+ # Issuer allowlist a merchant accepts for AIP Agent Identity Tokens, when advertised.
584
+ aip_trusted_issuers: NotRequired[list[str]]
548
585
  bootstrap: str
549
586
  do_not_persist_in_memory: list[str]
550
587
  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.4.2"
7
+ version = "2.5.0"
8
8
  description = "Python client for the AgentScore APIs"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -22,7 +22,7 @@ classifiers = [
22
22
  ]
23
23
 
24
24
  [project.urls]
25
- Homepage = "https://agentscore.sh"
25
+ Homepage = "https://www.agentscore.com"
26
26
  Repository = "https://github.com/agentscore/python-sdk"
27
27
  Issues = "https://github.com/agentscore/python-sdk/issues"
28
28