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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@agent-score/sdk",
"version": "2.8.1",
"version": "2.9.0",
"description": "TypeScript client for the AgentScore APIs",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
Expand Down
7 changes: 0 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
import type {
AgentScoreConfig,
AgentScoreErrorBody,
AipSignatureMaterial,
AssessOptions,
AssessResponse,
AssociateWalletOptions,
Expand Down Expand Up @@ -63,16 +62,10 @@ export class AgentScore {

async assess(address: string, options?: AssessOptions): Promise<AssessResponse>;
async assess(address: null, options: AssessOptions & { operatorToken: string }): Promise<AssessResponse>;
async assess(address: null, options: AssessOptions & { aipToken: string; aipSignature: AipSignatureMaterial }): Promise<AssessResponse>;
async assess(address: string | null, options?: AssessOptions): Promise<AssessResponse> {
const body: Record<string, unknown> = {};
if (address) body.address = address;
if (options?.operatorToken) body.operator_token = options.operatorToken;
// AIP Agent Identity Token as the identity input. The API re-verifies the IdP
// signature + claims server-side and evaluates policy against the attested identity
// (alongside the existing wallet / operator_token paths).
if (options?.aipToken) body.aip_token = options.aipToken;
if (options?.aipSignature) body.aip_signature = options.aipSignature;
if (options?.chain) body.chain = options.chain;
if (options?.refresh !== undefined) body.refresh = options.refresh;
if (options?.policy) body.policy = options.policy;
Expand Down
70 changes: 2 additions & 68 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,29 +72,6 @@ export interface AssessRequest {
policy?: DecisionPolicy;
/** Optional server-side signer verdicts (wallet-binding + OFAC SDN). See {@link Signer}. */
signer?: Signer;
/** Optional AIP Agent Identity Token (a JWT) as the identity input, in place of
* `address` / `operator_token`. The API re-verifies the issuer signature + claims
* and evaluates policy against the token's attested identity. */
aip_token?: string;
/** RFC 9421 proof-of-possession material accompanying `aip_token`. Required by the API on the
* AIP path: without it the token is rejected (a stolen token cannot prove possession). */
aip_signature?: AipSignatureMaterial;
}

/** 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→merchant request itself. */
export interface AipSignatureMaterial {
/** HTTP method of the original agent→merchant request (`@method`). */
method: string;
/** Authority/host the agent signed (`@authority`). */
authority: string;
/** Request path the agent signed (`@path`). */
path: string;
/** Raw `Signature-Input` header value the agent sent. */
signature_input: string;
/** Raw `Signature` header value the agent sent. */
signature: string;
}

/** Server-side OFAC SDN wallet-address verdict. Emitted on `AssessResponse.signer_sanctions`
Expand Down Expand Up @@ -159,27 +136,10 @@ export interface QuotaInfo {
reset: string | null;
}

/** 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. */
export interface AipProvenance {
/** Canonical issuer URL of the AIT (e.g. `https://issuer.example`, `https://www.agentscore.com`). */
issuer: string;
/** The token's `sub`: the IdP's subject identifier for the verified human. */
subject: string;
/** Degree of human involvement the IdP asserted, when present. */
trust_level?: 'autonomous' | 'human_present' | 'human_confirmed';
/** Agent platform the token carried (informational unless the issuer is the platform IdP). */
agent_provider?: string;
/** 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?: boolean;
}

export interface AssessResponse {
decision: string | null;
decision_reasons: string[];
identity_method: 'wallet' | 'operator_token' | 'aip_token';
identity_method: 'wallet' | 'operator_token';
operator_verification?: OperatorVerification;
resolved_operator?: string | null;
/** Wallets linked to the same operator as the resolved identity. Populated on allow
Expand Down Expand Up @@ -209,8 +169,6 @@ export interface AssessResponse {
/** Server-side OFAC SDN wallet-address verdict, returned only when the request supplied
* `signer`. Empty otherwise. See {@link SignerSanctions}. */
signer_sanctions?: SignerSanctions;
/** Issuer provenance, returned only when `identity_method === 'aip_token'`. */
aip?: AipProvenance;
/** Quota state for this account, captured from response headers. Use it to monitor
* approach-to-cap proactively (e.g. warn at 80%, alert at 95%) before hitting a 429. */
quota?: QuotaInfo;
Expand Down Expand Up @@ -373,10 +331,6 @@ export interface WalletAuthRequiresSigningBody {
export interface AgentMemoryIdentityPaths {
wallet: string;
operator_token: string;
/** Present only when the merchant accepts AIP Agent Identity Tokens. Tells an agent holding
* an AIT from a trusted issuer to present it via an `Agent-Identity` header + RFC 9421
* signature instead of bootstrapping a fresh AgentScore credential. */
agent_identity?: string;
}

/**
Expand All @@ -395,31 +349,11 @@ export interface AgentMemoryHint {
identity_check_endpoint: string;
list_wallets_endpoint?: string;
identity_paths: AgentMemoryIdentityPaths;
/** Issuers whose AIP Agent Identity Tokens the merchant accepts. Present only when the
* merchant opted into AIP; pairs with `identity_paths.agent_identity`. */
aip_trusted_issuers?: string[];
bootstrap: string;
do_not_persist_in_memory: string[];
persist_in_credential_store: string[];
}

/** Proof-of-possession pairing for the AIP identity path: `aipToken` and `aipSignature` are
* only valid together. The API rejects an AIT presented without its RFC 9421 PoP material
* (HTTP 400), so the pairing is enforced at the type level: `{ aipToken }` alone (or
* `{ aipSignature }` alone) does not compile. */
export type AipAssessOptions =
| {
/** AIP Agent Identity Token (a JWT) as the identity input. Serializes to the
* request body's `aip_token`. The API re-verifies the issuer signature + claims and
* evaluates policy against the token's attested identity. Use the
* `assess(null, { aipToken, aipSignature })` overload when an AIT is the sole identity. */
aipToken: string;
/** RFC 9421 proof-of-possession material for the AIT. Serializes to the request body's
* `aip_signature`. Required by the API whenever `aipToken` is set. */
aipSignature: AipSignatureMaterial;
}
| { aipToken?: undefined; aipSignature?: undefined };

export type AssessOptions = {
chain?: string;
/** @deprecated The API ignores this field; every assess is evaluated live. */
Expand All @@ -430,7 +364,7 @@ export type AssessOptions = {
* assess calls + the wallet-sanctions check into the gate's primary assess call. The
* response then carries `signer_match` + `signer_sanctions` verdicts. See {@link Signer}. */
signer?: Signer;
} & AipAssessOptions;
};

export interface SessionCreateOptions {
context?: string;
Expand Down
38 changes: 0 additions & 38 deletions tests/assess-options.test-d.ts

This file was deleted.

86 changes: 0 additions & 86 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -768,92 +768,6 @@ describe('AgentScore.assess(): operatorToken', () => {
});
});

// ---------------------------------------------------------------------------
// Identity model: AIP Agent Identity Token (aipToken + aipSignature)
// ---------------------------------------------------------------------------

const AIP_TOKEN = 'eyJhbGciOiJFZERTQSJ9.ait.payload';

const 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=:',
};

describe('AgentScore.assess(): aipToken + aipSignature', () => {
afterEach(() => vi.restoreAllMocks());

it('sends aip_token and aip_signature (all 5 PoP fields) in the request body', async () => {
mockFetchOk({ ...ASSESS_RESPONSE, identity_method: 'aip_token' });
const client = new AgentScore({ apiKey: API_KEY });
await client.assess(null, { aipToken: AIP_TOKEN, aipSignature: AIP_SIGNATURE });
const call = (global.fetch as ReturnType<typeof vi.fn>).mock.calls[0];
const body = JSON.parse(call[1].body as string) as Record<string, unknown>;
expect(body.aip_token).toBe(AIP_TOKEN);
expect(body.aip_signature).toEqual({
method: 'POST',
authority: 'merchant.example.com',
path: '/premium/report',
signature_input: 'sig1=("@method" "@authority" "@path");keyid="agent-cnf-key";alg="ed25519"',
signature: 'sig1=:dGVzdC1zaWduYXR1cmU=:',
});
expect(body.address).toBeUndefined();
expect(body.operator_token).toBeUndefined();
});

it('sends aip_token alongside policy and signer when combined', async () => {
mockFetchOk({ ...ASSESS_RESPONSE, identity_method: 'aip_token' });
const client = new AgentScore({ apiKey: API_KEY });
await client.assess(null, {
aipToken: AIP_TOKEN,
aipSignature: AIP_SIGNATURE,
policy: { require_kyc: true },
signer: { address: '0xsigner', network: 'evm' },
});
const call = (global.fetch as ReturnType<typeof vi.fn>).mock.calls[0];
const body = JSON.parse(call[1].body as string) as Record<string, unknown>;
expect(body.aip_token).toBe(AIP_TOKEN);
expect(body.aip_signature).toEqual(AIP_SIGNATURE);
expect(body.policy).toEqual({ require_kyc: true });
expect(body.signer).toEqual({ address: '0xsigner', network: 'evm' });
});

it('omits aip_token and aip_signature when not provided', async () => {
mockFetchOk(ASSESS_RESPONSE);
const client = new AgentScore({ apiKey: API_KEY });
await client.assess(WALLET);
const call = (global.fetch as ReturnType<typeof vi.fn>).mock.calls[0];
const body = JSON.parse(call[1].body as string) as Record<string, unknown>;
expect(body).not.toHaveProperty('aip_token');
expect(body).not.toHaveProperty('aip_signature');
});

it('parses the aip provenance block (incl. pop_verified) and identity_method onto the response', async () => {
mockFetchOk({
...ASSESS_RESPONSE,
identity_method: 'aip_token',
aip: {
issuer: 'https://www.agentscore.com',
subject: 'user_2abc',
trust_level: 'human_present',
agent_provider: 'openai',
pop_verified: true,
},
});
const client = new AgentScore({ apiKey: API_KEY });
const result = await client.assess(null, { aipToken: AIP_TOKEN, aipSignature: AIP_SIGNATURE });
expect(result.identity_method).toBe('aip_token');
expect(result.aip).toBeDefined();
expect(result.aip!.issuer).toBe('https://www.agentscore.com');
expect(result.aip!.subject).toBe('user_2abc');
expect(result.aip!.trust_level).toBe('human_present');
expect(result.aip!.agent_provider).toBe('openai');
expect(result.aip!.pop_verified).toBe(true);
});
});

// ---------------------------------------------------------------------------
// Typed errors
// ---------------------------------------------------------------------------
Expand Down
Loading