Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,20 @@ Two identity paths: `X-Wallet-Address` (wallet-based) and `X-Operator-Token` (cr
## Methods (sync + async)

- `get_reputation` / `aget_reputation` — cached reputation lookup (free)
- `assess` / `aassess` — identity gate with policy (paid). Accepts `operator_token` for non-wallet agents. Response includes `linked_wallets[]` and `resolved_operator`.
- `assess` / `aassess` — identity gate with policy (paid). Accepts `operator_token` for non-wallet agents. Response includes `linked_wallets[]` and `resolved_operator`. Optional `resolve_signer: { address, network }` opts into server-side wallet-signer-match — the response then carries a `signer_match` block describing whether the supplied signer wallet resolves to the same operator as the claimed `address`.
- `create_session` / `acreate_session` — create verification session. Returns `agent_memory` + `next_steps`.
- `poll_session` / `apoll_session` — poll session status, returns credential when verified, plus `next_steps.action`.
- `create_credential` / `acreate_credential` — create operator credential (24h TTL default). Response includes `agent_memory`.
- `list_credentials` / `alist_credentials` — list active credentials
- `revoke_credential` / `arevoke_credential` — revoke a credential
- `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.
- `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.

## Errors + observability

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.

`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.

## Architecture

Expand Down
52 changes: 47 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,18 +147,60 @@ except AgentScoreError as e:
print(e.code, e.status_code, str(e))
```

`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:
`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.

### Typed error classes

For status-code-specific recovery, the SDK raises typed subclasses of `AgentScoreError`. All inherit from `AgentScoreError` so existing `except AgentScoreError` still catches them.

| Class | Triggered by | What it adds |
|---|---|---|
| `PaymentRequiredError` | HTTP 402 | The endpoint is not enabled for this account |
| `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` |
| `InvalidCredentialError` | HTTP 401 with `error.code = "invalid_credential"` | Permanent — switch tokens or restart |
| `QuotaExceededError` | HTTP 429 with `error.code = "quota_exceeded"` | Account-level cap reached; don't retry |
| `RateLimitedError` | HTTP 429 with `error.code = "rate_limited"` | Per-second sliding-window cap; retry after `Retry-After` |
| `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. |

All non-timeout `httpx.HTTPError` (ConnectError, ProtocolError, NetworkError, etc.) are wrapped as `AgentScoreError(code="network_error", status_code=0)`.

```python
from agentscore import (
AgentScore, AgentScoreError, TokenExpiredError, QuotaExceededError,
)
from agentscore.errors import TimeoutError as AgentScoreTimeoutError

try:
client.assess("0xabc...", policy={"require_kyc": True})
except TokenExpiredError as e:
print("Verify at:", e.verify_url, "poll with:", e.poll_secret)
except QuotaExceededError as e:
print("Account quota reached — surface to user; don't retry.")
except AgentScoreTimeoutError:
print("Network timeout — retry with backoff.")
except AgentScoreError as e:
if e.code == "wallet_signer_mismatch":
print("Re-sign from one of:", e.details.get("linked_wallets"))
elif e.code == "token_expired":
print("Verify at:", e.details.get("verify_url"))
print(e.code, e.message)
```

## Quota observability

`assess()` (and `aassess()`) responses include an optional `quota` field captured from `X-Quota-Limit` / `X-Quota-Used` / `X-Quota-Reset` response headers. Use it to monitor approach-to-cap proactively (warn at 80%, alert at 95%) before a 429:

```python
result = client.assess("0xabc...", policy={"require_kyc": True})
quota = result.get("quota")
if quota and quota["limit"] and quota["used"]:
pct = (quota["used"] / quota["limit"]) * 100
if pct > 80:
print(f"AgentScore quota at {pct:.1f}% — resets {quota['reset']}")
```

`quota` is absent when the API doesn't emit the headers (Enterprise / unlimited tiers).

## Telemetry

`telemetry_signer_match(payload)` and `atelemetry_signer_match(payload)` are fire-and-forget POSTs to `/v1/telemetry/signer-match` so AgentScore can track aggregate signer-binding behavior across merchants. Used internally by `agentscore-commerce`'s gate; available directly for custom integrations that perform their own wallet-signer-match checks.

## Documentation

- [API Reference](https://docs.agentscore.sh)
Expand Down
22 changes: 21 additions & 1 deletion agentscore/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
from importlib.metadata import version as _pkg_version

from agentscore.client import AgentScore
from agentscore.errors import AgentScoreError
from agentscore.errors import (
AgentScoreError,
InvalidCredentialError,
PaymentRequiredError,
QuotaExceededError,
RateLimitedError,
TimeoutError,
TokenExpiredError,
)
from agentscore.test_mode import AGENTSCORE_TEST_ADDRESSES, is_agentscore_test_address
from agentscore.types import (
AccountVerification,
Expand All @@ -21,12 +29,15 @@
Network,
NextStepsAction,
OperatorVerification,
QuotaInfo,
Reputation,
ReputationResponse,
ReputationStatus,
ResolveSigner,
SessionCreateRequest,
SessionCreateResponse,
SessionPollResponse,
SignerMatch,
VerificationLevel,
WalletAuthRequiresSigningBody,
WalletSignerMismatchBody,
Expand All @@ -52,15 +63,24 @@
"DenialCode",
"EntityType",
"Grade",
"InvalidCredentialError",
"Network",
"NextStepsAction",
"OperatorVerification",
"PaymentRequiredError",
"QuotaExceededError",
"QuotaInfo",
"RateLimitedError",
"Reputation",
"ReputationResponse",
"ReputationStatus",
"ResolveSigner",
"SessionCreateRequest",
"SessionCreateResponse",
"SessionPollResponse",
"SignerMatch",
"TimeoutError",
"TokenExpiredError",
"VerificationLevel",
"WalletAuthRequiresSigningBody",
"WalletSignerMismatchBody",
Expand Down
Loading
Loading