From 6d97fd6c5e603559feca16bc2aba5496fb003219 Mon Sep 17 00:00:00 2001 From: vvillait88 Date: Wed, 7 Oct 2026 20:17:50 -0700 Subject: [PATCH] Remove the unused identity-token assess options The API no longer accepts the token-based identity input, so the assess parameters, the response provenance block, the agent-memory fields and their types are removed. Minor bump: nothing that works against the API today changes. --- agentscore/__init__.py | 4 -- agentscore/client.py | 22 +-------- agentscore/types.py | 40 +--------------- pyproject.toml | 2 +- tests/test_client.py | 101 ----------------------------------------- uv.lock | 2 +- 6 files changed, 4 insertions(+), 167 deletions(-) diff --git a/agentscore/__init__.py b/agentscore/__init__.py index d939fdb..114aec0 100644 --- a/agentscore/__init__.py +++ b/agentscore/__init__.py @@ -15,8 +15,6 @@ AccountVerification, AgentMemoryHint, AgentMemoryIdentityPaths, - AipProvenance, - AipSignatureMaterial, AssessResponse, AssociateWalletResponse, CredentialCreateErrorNextSteps, @@ -61,8 +59,6 @@ "AgentMemoryIdentityPaths", "AgentScore", "AgentScoreError", - "AipProvenance", - "AipSignatureMaterial", "AssessResponse", "AssociateWalletResponse", "CredentialCreateErrorNextSteps", diff --git a/agentscore/client.py b/agentscore/client.py index 045c4e8..c94b85c 100644 --- a/agentscore/client.py +++ b/agentscore/client.py @@ -133,7 +133,6 @@ def _build_error_from_response(response: httpx.Response) -> AgentScoreError: from collections.abc import Awaitable, Callable from agentscore.types import ( - AipSignatureMaterial, AssessResponse, AssociateWalletResponse, CredentialCreateResponse, @@ -254,8 +253,6 @@ def assess( policy: DecisionPolicy | None = None, operator_token: str | None = None, signer: Signer | None = None, - aip_token: str | None = None, - aip_signature: AipSignatureMaterial | None = None, ) -> AssessResponse: """Assess a wallet or operator against a compliance policy. @@ -264,22 +261,12 @@ def assess( ``signer`` opts into server-side wallet-signer-match: when supplied, the API resolves the signer wallet against the claimed ``address`` and emits a ``signer_match`` block on the response. See :class:`Signer`. - - ``aip_token`` (+ ``aip_signature``) supplies an AIP Agent Identity Token in place of - ``address`` / ``operator_token``: the API re-verifies the issuer signature, the RFC 9421 - proof-of-possession, and the attested claims, then evaluates policy against them. """ body: dict[str, Any] = {} if address: body["address"] = address if operator_token: body["operator_token"] = operator_token - # AIP Agent Identity Token path: the API re-verifies the IdP signature + claims - # server-side and evaluates policy against the attested identity. - if aip_token: - body["aip_token"] = aip_token - if aip_signature is not None: - body["aip_signature"] = dict(aip_signature) if chain: body["chain"] = chain if refresh is not None: @@ -404,25 +391,18 @@ async def aassess( policy: DecisionPolicy | None = None, operator_token: str | None = None, signer: Signer | None = None, - aip_token: str | None = None, - aip_signature: AipSignatureMaterial | None = None, ) -> AssessResponse: """Assess a wallet or operator against a compliance policy. ``refresh`` is deprecated: the API ignores it, and every assess is evaluated live. - ``signer`` opts into server-side wallet-signer-match; ``aip_token`` (+ ``aip_signature``) - supplies an AIP Agent Identity Token. Async mirror of :meth:`assess`. + ``signer`` opts into server-side wallet-signer-match. Async mirror of :meth:`assess`. """ body: dict[str, Any] = {} if address: body["address"] = address if operator_token: body["operator_token"] = operator_token - if aip_token: - body["aip_token"] = aip_token - if aip_signature is not None: - body["aip_signature"] = dict(aip_signature) if chain: body["chain"] = chain if refresh is not None: diff --git a/agentscore/types.py b/agentscore/types.py index c85f2ab..e0ff1df 100644 --- a/agentscore/types.py +++ b/agentscore/types.py @@ -132,43 +132,10 @@ class SignerSanctionsUnavailable(TypedDict): SignerSanctions = SignerSanctionsClear | SignerSanctionsHit | SignerSanctionsUnavailable -class AipSignatureMaterial(TypedDict): - """RFC 9421 HTTP Message Signature material proving possession of the AIT-bound ``cnf`` key. - - Forwarded alongside ``aip_token`` so ``/v1/assess`` can re-verify proof-of-possession - authoritatively; the API never sees the original agent-to-merchant request itself. - """ - - method: str # HTTP method of the original agent-to-merchant request (``@method``) - authority: str # Authority/host the agent signed (``@authority``) - path: str # Request path the agent signed (``@path``) - signature_input: str # Raw ``Signature-Input`` header value the agent sent - signature: str # Raw ``Signature`` header value the agent sent - - -class _AipProvenanceRequired(TypedDict): - issuer: str # Canonical issuer URL of the AIT - subject: str # The token's ``sub``: the IdP's subject identifier for the verified human - - -class AipProvenance(_AipProvenanceRequired, total=False): - """Provenance block returned when the identity input was an AIP Agent Identity Token. - - Surfaces which issuer attested the identity and the trust level it asserted. - """ - - trust_level: Literal["autonomous", "human_present", "human_confirmed"] - agent_provider: str - # True when /v1/assess re-verified the RFC 9421 proof-of-possession. Always true on a - # success response: the API fail-closes with an HTTP 400/401 error (not a 200 deny) - # when possession can't be proven. - pop_verified: bool - - class _AssessResponseRequired(TypedDict): decision: str | None decision_reasons: list[str] - identity_method: Literal["wallet", "operator_token", "aip_token"] + identity_method: Literal["wallet", "operator_token"] class PolicyExplanation(TypedDict, total=False): @@ -223,8 +190,6 @@ class AssessResponse(_AssessResponseRequired, total=False): # Server-side OFAC SDN wallet-address verdict, returned only when the request supplied # ``signer``. Empty otherwise. signer_sanctions: NotRequired[SignerSanctions] - # Issuer provenance, returned only when ``identity_method == "aip_token"``. - aip: NotRequired[AipProvenance] # Quota state for this account, captured from response headers on the success path. # Use to monitor approach-to-cap proactively (warn at 80%, alert at 95%) before 429. quota: NotRequired[QuotaInfo] @@ -446,7 +411,6 @@ class AssociateWalletResponse(TypedDict): class AgentMemoryIdentityPaths(TypedDict): wallet: str operator_token: str - agent_identity: NotRequired[str] class AgentMemoryHint(TypedDict): @@ -463,8 +427,6 @@ class AgentMemoryHint(TypedDict): identity_check_endpoint: str list_wallets_endpoint: NotRequired[str] identity_paths: AgentMemoryIdentityPaths - # Issuer allowlist a merchant accepts for AIP Agent Identity Tokens, when advertised. - aip_trusted_issuers: NotRequired[list[str]] bootstrap: str do_not_persist_in_memory: list[str] persist_in_credential_store: list[str] diff --git a/pyproject.toml b/pyproject.toml index bb4c96e..9667f56 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "agentscore-py" -version = "2.7.1" +version = "2.8.0" description = "Python client for the AgentScore APIs" readme = "README.md" license = "MIT" diff --git a/tests/test_client.py b/tests/test_client.py index d65d16b..7e28433 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -353,107 +353,6 @@ def test_assess_signer_raises_token_expired_with_signer(): assert body["signer"] == {"address": "0xs", "network": "evm"} -# --------------------------------------------------------------------------- -# assess: AIP Agent Identity Token (aip_token + aip_signature) -# --------------------------------------------------------------------------- - -AIP_TOKEN = "eyJhbGciOiJFZERTQSJ9.ait.payload" - -AIP_SIGNATURE = { - "method": "POST", - "authority": "merchant.example.com", - "path": "/premium/report", - "signature_input": 'sig1=("@method" "@authority" "@path");keyid="agent-cnf-key";alg="ed25519"', - "signature": "sig1=:dGVzdC1zaWduYXR1cmU=:", -} - -AIP_ASSESS_PAYLOAD = { - **ASSESS_PAYLOAD, - "identity_method": "aip_token", - "aip": { - "issuer": "https://www.agentscore.com", - "subject": "user_2abc", - "trust_level": "human_present", - "agent_provider": "openai", - "pop_verified": True, - }, -} - - -@respx.mock -def test_assess_forwards_aip_token_and_signature_in_body(): - """aip_token + all 5 RFC 9421 PoP fields of aip_signature land in the request body.""" - route = respx.post(f"{BASE_URL}/v1/assess").mock(return_value=httpx.Response(200, json=AIP_ASSESS_PAYLOAD)) - client = AgentScore(api_key=API_KEY) - client.assess(aip_token=AIP_TOKEN, aip_signature=AIP_SIGNATURE) - body = json.loads(route.calls.last.request.content) - assert body["aip_token"] == AIP_TOKEN - assert body["aip_signature"] == { - "method": "POST", - "authority": "merchant.example.com", - "path": "/premium/report", - "signature_input": 'sig1=("@method" "@authority" "@path");keyid="agent-cnf-key";alg="ed25519"', - "signature": "sig1=:dGVzdC1zaWduYXR1cmU=:", - } - assert "address" not in body - assert "operator_token" not in body - - -@respx.mock -def test_assess_returns_aip_provenance(): - """The aip block (incl. pop_verified) and identity_method round-trip on the response.""" - respx.post(f"{BASE_URL}/v1/assess").mock(return_value=httpx.Response(200, json=AIP_ASSESS_PAYLOAD)) - client = AgentScore(api_key=API_KEY) - result = client.assess(aip_token=AIP_TOKEN, aip_signature=AIP_SIGNATURE) - assert result["identity_method"] == "aip_token" - assert result["aip"]["issuer"] == "https://www.agentscore.com" - assert result["aip"]["subject"] == "user_2abc" - assert result["aip"]["trust_level"] == "human_present" - assert result["aip"]["agent_provider"] == "openai" - assert result["aip"]["pop_verified"] is True - - -@respx.mock -def test_assess_omits_aip_fields_when_not_provided(): - route = respx.post(f"{BASE_URL}/v1/assess").mock(return_value=httpx.Response(200, json=ASSESS_PAYLOAD)) - client = AgentScore(api_key=API_KEY) - client.assess(ADDRESS) - body = json.loads(route.calls.last.request.content) - assert "aip_token" not in body - assert "aip_signature" not in body - - -@pytest.mark.asyncio -@respx.mock -async def test_aassess_forwards_aip_token_and_signature_in_body(): - route = respx.post(f"{BASE_URL}/v1/assess").mock(return_value=httpx.Response(200, json=AIP_ASSESS_PAYLOAD)) - client = AgentScore(api_key=API_KEY) - await client.aassess(aip_token=AIP_TOKEN, aip_signature=AIP_SIGNATURE) - body = json.loads(route.calls.last.request.content) - assert body["aip_token"] == AIP_TOKEN - assert body["aip_signature"] == { - "method": "POST", - "authority": "merchant.example.com", - "path": "/premium/report", - "signature_input": 'sig1=("@method" "@authority" "@path");keyid="agent-cnf-key";alg="ed25519"', - "signature": "sig1=:dGVzdC1zaWduYXR1cmU=:", - } - assert "address" not in body - await client.aclose() - - -@pytest.mark.asyncio -@respx.mock -async def test_aassess_returns_aip_provenance(): - respx.post(f"{BASE_URL}/v1/assess").mock(return_value=httpx.Response(200, json=AIP_ASSESS_PAYLOAD)) - client = AgentScore(api_key=API_KEY) - result = await client.aassess(aip_token=AIP_TOKEN, aip_signature=AIP_SIGNATURE) - assert result["identity_method"] == "aip_token" - assert result["aip"]["pop_verified"] is True - assert result["aip"]["issuer"] == "https://www.agentscore.com" - await client.aclose() - - # --------------------------------------------------------------------------- # Async request handling # --------------------------------------------------------------------------- diff --git a/uv.lock b/uv.lock index af4b855..e9ae984 100644 --- a/uv.lock +++ b/uv.lock @@ -4,7 +4,7 @@ requires-python = ">=3.11" [[package]] name = "agentscore-py" -version = "2.7.1" +version = "2.8.0" source = { editable = "." } dependencies = [ { name = "httpx" },