Skip to content

Commit 4243d9a

Browse files
authored
Remove the unused identity-token assess options (#106)
## Summary Removes the unused identity-token input from `assess` and `aassess`. The API no longer accepts it, so it is removed here: - its two parameters on both methods; - the request-body fields they set; - the signature-material and provenance TypedDicts; - the provenance block and `identity_method` value it produced on the response; - the two agent-memory fields that advertised it. Its tests go with it. Version 2.8.0: a minor, because nothing that works against the API today changes. Worked with Varun, who asked for the feature to be removed everywhere. ## Type of change - [ ] Bug fix (no breaking change) - [ ] New feature (no breaking change) - [ ] Breaking change (existing callers must update) - [x] Docs, tests, or internal maintenance only ## Public API Removes the two assess parameters, two exported TypedDicts, the response provenance field, and two `AgentMemoryHint` fields. Nobody uses them, and the API ignores them. ## Test plan ruff check, ruff format --check, ty, vulture and pytest (172 passed, 2 skipped), all green locally. ## Checklist - [x] Tests cover the new behavior, and the suite passes locally - [x] Lint, format, and type checks pass - [x] Docs and README examples updated if the public surface changed - [x] No secrets, credentials, or personal data in the diff or the tests
1 parent fa5f6b3 commit 4243d9a

6 files changed

Lines changed: 4 additions & 167 deletions

File tree

‎agentscore/__init__.py‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
AccountVerification,
1616
AgentMemoryHint,
1717
AgentMemoryIdentityPaths,
18-
AipProvenance,
19-
AipSignatureMaterial,
2018
AssessResponse,
2119
AssociateWalletResponse,
2220
CredentialCreateErrorNextSteps,
@@ -61,8 +59,6 @@
6159
"AgentMemoryIdentityPaths",
6260
"AgentScore",
6361
"AgentScoreError",
64-
"AipProvenance",
65-
"AipSignatureMaterial",
6662
"AssessResponse",
6763
"AssociateWalletResponse",
6864
"CredentialCreateErrorNextSteps",

‎agentscore/client.py‎

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ def _build_error_from_response(response: httpx.Response) -> AgentScoreError:
133133
from collections.abc import Awaitable, Callable
134134

135135
from agentscore.types import (
136-
AipSignatureMaterial,
137136
AssessResponse,
138137
AssociateWalletResponse,
139138
CredentialCreateResponse,
@@ -254,8 +253,6 @@ def assess(
254253
policy: DecisionPolicy | None = None,
255254
operator_token: str | None = None,
256255
signer: Signer | None = None,
257-
aip_token: str | None = None,
258-
aip_signature: AipSignatureMaterial | None = None,
259256
) -> AssessResponse:
260257
"""Assess a wallet or operator against a compliance policy.
261258
@@ -264,22 +261,12 @@ def assess(
264261
``signer`` opts into server-side wallet-signer-match: when supplied,
265262
the API resolves the signer wallet against the claimed ``address`` and emits
266263
a ``signer_match`` block on the response. See :class:`Signer`.
267-
268-
``aip_token`` (+ ``aip_signature``) supplies an AIP Agent Identity Token in place of
269-
``address`` / ``operator_token``: the API re-verifies the issuer signature, the RFC 9421
270-
proof-of-possession, and the attested claims, then evaluates policy against them.
271264
"""
272265
body: dict[str, Any] = {}
273266
if address:
274267
body["address"] = address
275268
if operator_token:
276269
body["operator_token"] = operator_token
277-
# AIP Agent Identity Token path: the API re-verifies the IdP signature + claims
278-
# server-side and evaluates policy against the attested identity.
279-
if aip_token:
280-
body["aip_token"] = aip_token
281-
if aip_signature is not None:
282-
body["aip_signature"] = dict(aip_signature)
283270
if chain:
284271
body["chain"] = chain
285272
if refresh is not None:
@@ -404,25 +391,18 @@ async def aassess(
404391
policy: DecisionPolicy | None = None,
405392
operator_token: str | None = None,
406393
signer: Signer | None = None,
407-
aip_token: str | None = None,
408-
aip_signature: AipSignatureMaterial | None = None,
409394
) -> AssessResponse:
410395
"""Assess a wallet or operator against a compliance policy.
411396
412397
``refresh`` is deprecated: the API ignores it, and every assess is evaluated live.
413398
414-
``signer`` opts into server-side wallet-signer-match; ``aip_token`` (+ ``aip_signature``)
415-
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`.
416400
"""
417401
body: dict[str, Any] = {}
418402
if address:
419403
body["address"] = address
420404
if operator_token:
421405
body["operator_token"] = operator_token
422-
if aip_token:
423-
body["aip_token"] = aip_token
424-
if aip_signature is not None:
425-
body["aip_signature"] = dict(aip_signature)
426406
if chain:
427407
body["chain"] = chain
428408
if refresh is not None:

‎agentscore/types.py‎

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -132,43 +132,10 @@ class SignerSanctionsUnavailable(TypedDict):
132132
SignerSanctions = SignerSanctionsClear | SignerSanctionsHit | SignerSanctionsUnavailable
133133

134134

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-
168135
class _AssessResponseRequired(TypedDict):
169136
decision: str | None
170137
decision_reasons: list[str]
171-
identity_method: Literal["wallet", "operator_token", "aip_token"]
138+
identity_method: Literal["wallet", "operator_token"]
172139

173140

174141
class PolicyExplanation(TypedDict, total=False):
@@ -223,8 +190,6 @@ class AssessResponse(_AssessResponseRequired, total=False):
223190
# Server-side OFAC SDN wallet-address verdict, returned only when the request supplied
224191
# ``signer``. Empty otherwise.
225192
signer_sanctions: NotRequired[SignerSanctions]
226-
# Issuer provenance, returned only when ``identity_method == "aip_token"``.
227-
aip: NotRequired[AipProvenance]
228193
# Quota state for this account, captured from response headers on the success path.
229194
# Use to monitor approach-to-cap proactively (warn at 80%, alert at 95%) before 429.
230195
quota: NotRequired[QuotaInfo]
@@ -446,7 +411,6 @@ class AssociateWalletResponse(TypedDict):
446411
class AgentMemoryIdentityPaths(TypedDict):
447412
wallet: str
448413
operator_token: str
449-
agent_identity: NotRequired[str]
450414

451415

452416
class AgentMemoryHint(TypedDict):
@@ -463,8 +427,6 @@ class AgentMemoryHint(TypedDict):
463427
identity_check_endpoint: str
464428
list_wallets_endpoint: NotRequired[str]
465429
identity_paths: AgentMemoryIdentityPaths
466-
# Issuer allowlist a merchant accepts for AIP Agent Identity Tokens, when advertised.
467-
aip_trusted_issuers: NotRequired[list[str]]
468430
bootstrap: str
469431
do_not_persist_in_memory: list[str]
470432
persist_in_credential_store: list[str]

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "agentscore-py"
7-
version = "2.7.1"
7+
version = "2.8.0"
88
description = "Python client for the AgentScore APIs"
99
readme = "README.md"
1010
license = "MIT"

‎tests/test_client.py‎

Lines changed: 0 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -353,107 +353,6 @@ def test_assess_signer_raises_token_expired_with_signer():
353353
assert body["signer"] == {"address": "0xs", "network": "evm"}
354354

355355

356-
# ---------------------------------------------------------------------------
357-
# assess: AIP Agent Identity Token (aip_token + aip_signature)
358-
# ---------------------------------------------------------------------------
359-
360-
AIP_TOKEN = "eyJhbGciOiJFZERTQSJ9.ait.payload"
361-
362-
AIP_SIGNATURE = {
363-
"method": "POST",
364-
"authority": "merchant.example.com",
365-
"path": "/premium/report",
366-
"signature_input": 'sig1=("@method" "@authority" "@path");keyid="agent-cnf-key";alg="ed25519"',
367-
"signature": "sig1=:dGVzdC1zaWduYXR1cmU=:",
368-
}
369-
370-
AIP_ASSESS_PAYLOAD = {
371-
**ASSESS_PAYLOAD,
372-
"identity_method": "aip_token",
373-
"aip": {
374-
"issuer": "https://www.agentscore.com",
375-
"subject": "user_2abc",
376-
"trust_level": "human_present",
377-
"agent_provider": "openai",
378-
"pop_verified": True,
379-
},
380-
}
381-
382-
383-
@respx.mock
384-
def test_assess_forwards_aip_token_and_signature_in_body():
385-
"""aip_token + all 5 RFC 9421 PoP fields of aip_signature land in the request body."""
386-
route = respx.post(f"{BASE_URL}/v1/assess").mock(return_value=httpx.Response(200, json=AIP_ASSESS_PAYLOAD))
387-
client = AgentScore(api_key=API_KEY)
388-
client.assess(aip_token=AIP_TOKEN, aip_signature=AIP_SIGNATURE)
389-
body = json.loads(route.calls.last.request.content)
390-
assert body["aip_token"] == AIP_TOKEN
391-
assert body["aip_signature"] == {
392-
"method": "POST",
393-
"authority": "merchant.example.com",
394-
"path": "/premium/report",
395-
"signature_input": 'sig1=("@method" "@authority" "@path");keyid="agent-cnf-key";alg="ed25519"',
396-
"signature": "sig1=:dGVzdC1zaWduYXR1cmU=:",
397-
}
398-
assert "address" not in body
399-
assert "operator_token" not in body
400-
401-
402-
@respx.mock
403-
def test_assess_returns_aip_provenance():
404-
"""The aip block (incl. pop_verified) and identity_method round-trip on the response."""
405-
respx.post(f"{BASE_URL}/v1/assess").mock(return_value=httpx.Response(200, json=AIP_ASSESS_PAYLOAD))
406-
client = AgentScore(api_key=API_KEY)
407-
result = client.assess(aip_token=AIP_TOKEN, aip_signature=AIP_SIGNATURE)
408-
assert result["identity_method"] == "aip_token"
409-
assert result["aip"]["issuer"] == "https://www.agentscore.com"
410-
assert result["aip"]["subject"] == "user_2abc"
411-
assert result["aip"]["trust_level"] == "human_present"
412-
assert result["aip"]["agent_provider"] == "openai"
413-
assert result["aip"]["pop_verified"] is True
414-
415-
416-
@respx.mock
417-
def test_assess_omits_aip_fields_when_not_provided():
418-
route = respx.post(f"{BASE_URL}/v1/assess").mock(return_value=httpx.Response(200, json=ASSESS_PAYLOAD))
419-
client = AgentScore(api_key=API_KEY)
420-
client.assess(ADDRESS)
421-
body = json.loads(route.calls.last.request.content)
422-
assert "aip_token" not in body
423-
assert "aip_signature" not in body
424-
425-
426-
@pytest.mark.asyncio
427-
@respx.mock
428-
async def test_aassess_forwards_aip_token_and_signature_in_body():
429-
route = respx.post(f"{BASE_URL}/v1/assess").mock(return_value=httpx.Response(200, json=AIP_ASSESS_PAYLOAD))
430-
client = AgentScore(api_key=API_KEY)
431-
await client.aassess(aip_token=AIP_TOKEN, aip_signature=AIP_SIGNATURE)
432-
body = json.loads(route.calls.last.request.content)
433-
assert body["aip_token"] == AIP_TOKEN
434-
assert body["aip_signature"] == {
435-
"method": "POST",
436-
"authority": "merchant.example.com",
437-
"path": "/premium/report",
438-
"signature_input": 'sig1=("@method" "@authority" "@path");keyid="agent-cnf-key";alg="ed25519"',
439-
"signature": "sig1=:dGVzdC1zaWduYXR1cmU=:",
440-
}
441-
assert "address" not in body
442-
await client.aclose()
443-
444-
445-
@pytest.mark.asyncio
446-
@respx.mock
447-
async def test_aassess_returns_aip_provenance():
448-
respx.post(f"{BASE_URL}/v1/assess").mock(return_value=httpx.Response(200, json=AIP_ASSESS_PAYLOAD))
449-
client = AgentScore(api_key=API_KEY)
450-
result = await client.aassess(aip_token=AIP_TOKEN, aip_signature=AIP_SIGNATURE)
451-
assert result["identity_method"] == "aip_token"
452-
assert result["aip"]["pop_verified"] is True
453-
assert result["aip"]["issuer"] == "https://www.agentscore.com"
454-
await client.aclose()
455-
456-
457356
# ---------------------------------------------------------------------------
458357
# Async request handling
459358
# ---------------------------------------------------------------------------

‎uv.lock‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)