@agent-score/commerce 2.3.0 → 2.5.0
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.
- package/README.md +6 -6
- package/dist/challenge/index.d.mts +9 -5
- package/dist/challenge/index.d.ts +9 -5
- package/dist/challenge/index.js +11 -4
- package/dist/challenge/index.js.map +1 -1
- package/dist/challenge/index.mjs +11 -4
- package/dist/challenge/index.mjs.map +1 -1
- package/dist/{checkout-ChyOi7aU.d.ts → checkout-McfNpZJf.d.ts} +80 -12
- package/dist/{checkout-Ceo1_rVJ.d.mts → checkout-o17dIxHi.d.mts} +80 -12
- package/dist/core.d.mts +27 -7
- package/dist/core.d.ts +27 -7
- package/dist/core.js +25 -11
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +25 -11
- package/dist/core.mjs.map +1 -1
- package/dist/discovery/index.d.mts +26 -10
- package/dist/discovery/index.d.ts +26 -10
- package/dist/discovery/index.js +58 -10
- package/dist/discovery/index.js.map +1 -1
- package/dist/discovery/index.mjs +58 -10
- package/dist/discovery/index.mjs.map +1 -1
- package/dist/gate-CWP10xPQ.d.mts +339 -0
- package/dist/gate-CWP10xPQ.d.ts +339 -0
- package/dist/identity/express.d.mts +26 -1
- package/dist/identity/express.d.ts +26 -1
- package/dist/identity/express.js +599 -25
- package/dist/identity/express.js.map +1 -1
- package/dist/identity/express.mjs +595 -24
- package/dist/identity/express.mjs.map +1 -1
- package/dist/identity/fastify.d.mts +12 -2
- package/dist/identity/fastify.d.ts +12 -2
- package/dist/identity/fastify.js +607 -25
- package/dist/identity/fastify.js.map +1 -1
- package/dist/identity/fastify.mjs +603 -24
- package/dist/identity/fastify.mjs.map +1 -1
- package/dist/identity/hono.d.mts +26 -1
- package/dist/identity/hono.d.ts +26 -1
- package/dist/identity/hono.js +588 -25
- package/dist/identity/hono.js.map +1 -1
- package/dist/identity/hono.mjs +584 -24
- package/dist/identity/hono.mjs.map +1 -1
- package/dist/identity/nextjs.d.mts +3 -0
- package/dist/identity/nextjs.d.ts +3 -0
- package/dist/identity/nextjs.js +593 -25
- package/dist/identity/nextjs.js.map +1 -1
- package/dist/identity/nextjs.mjs +589 -24
- package/dist/identity/nextjs.mjs.map +1 -1
- package/dist/identity/policy.js.map +1 -1
- package/dist/identity/policy.mjs.map +1 -1
- package/dist/identity/web.d.mts +24 -1
- package/dist/identity/web.d.ts +24 -1
- package/dist/identity/web.js +593 -25
- package/dist/identity/web.js.map +1 -1
- package/dist/identity/web.mjs +589 -24
- package/dist/identity/web.mjs.map +1 -1
- package/dist/index.d.mts +129 -14
- package/dist/index.d.ts +129 -14
- package/dist/index.js +978 -99
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +956 -99
- package/dist/index.mjs.map +1 -1
- package/dist/{default_rails-DtR_E9N9.d.ts → network_kind-BIJM2peR.d.ts} +22 -8
- package/dist/{default_rails-K25PtWrL.d.mts → network_kind-C0EMkdzz.d.mts} +22 -8
- package/dist/payment/index.d.mts +16 -26
- package/dist/payment/index.d.ts +16 -26
- package/dist/payment/index.js +62 -57
- package/dist/payment/index.js.map +1 -1
- package/dist/payment/index.mjs +62 -57
- package/dist/payment/index.mjs.map +1 -1
- package/dist/{pricing-B3-aKxSz.d.ts → pricing-CytRwhC2.d.ts} +1 -1
- package/dist/{pricing-BReyZiqN.d.mts → pricing-KHDqMLd7.d.mts} +1 -1
- package/dist/{rail_spec-B1239jPp.d.mts → rail_spec-BFZmW9RN.d.mts} +3 -4
- package/dist/{rail_spec-B1239jPp.d.ts → rail_spec-BFZmW9RN.d.ts} +3 -4
- package/dist/stripe-multichain/index.d.mts +2 -9
- package/dist/stripe-multichain/index.d.ts +2 -9
- package/dist/stripe-multichain/index.js.map +1 -1
- package/dist/stripe-multichain/index.mjs.map +1 -1
- package/dist/{wwwauthenticate-D_FMnPgU.d.mts → wwwauthenticate-CVaGUMjU.d.mts} +8 -6
- package/dist/{wwwauthenticate-D_FMnPgU.d.ts → wwwauthenticate-CVaGUMjU.d.ts} +8 -6
- package/package.json +14 -13
package/dist/index.mjs
CHANGED
|
@@ -329,19 +329,19 @@ function stableStringify(value) {
|
|
|
329
329
|
}
|
|
330
330
|
if (!Number.isInteger(value)) {
|
|
331
331
|
throw new Error(
|
|
332
|
-
`UCP profile canonicalization rejects non-integer Number ${value}. Use a decimal string (e.g. "9.99") for monetary or fractional fields
|
|
332
|
+
`UCP profile canonicalization rejects non-integer Number ${value}. Use a decimal string (e.g. "9.99") for monetary or fractional fields so the signed canonical bytes stay stable for any verifier.`
|
|
333
333
|
);
|
|
334
334
|
}
|
|
335
335
|
if (!Number.isSafeInteger(value)) {
|
|
336
336
|
throw new Error(
|
|
337
|
-
`stableStringify: integer ${value} exceeds Number.MAX_SAFE_INTEGER. For values >2^53, use a decimal string to
|
|
337
|
+
`stableStringify: integer ${value} exceeds Number.MAX_SAFE_INTEGER. For values >2^53, use a decimal string to keep the signed bytes stable across implementations.`
|
|
338
338
|
);
|
|
339
339
|
}
|
|
340
340
|
}
|
|
341
341
|
if (typeof value === "string") {
|
|
342
342
|
if (value.includes("\u2028") || value.includes("\u2029")) {
|
|
343
343
|
throw new Error(
|
|
344
|
-
"stableStringify: strings containing U+2028 (LINE SEPARATOR) or U+2029 (PARAGRAPH SEPARATOR) are not allowed;
|
|
344
|
+
"stableStringify: strings containing U+2028 (LINE SEPARATOR) or U+2029 (PARAGRAPH SEPARATOR) are not allowed; stable canonical bytes require neither be present (some JSON encoders escape them, others emit them raw)."
|
|
345
345
|
);
|
|
346
346
|
}
|
|
347
347
|
return JSON.stringify(value);
|
|
@@ -361,7 +361,7 @@ function stableStringify(value) {
|
|
|
361
361
|
for (const k of keys) {
|
|
362
362
|
if (k.includes("\u2028") || k.includes("\u2029")) {
|
|
363
363
|
throw new Error(
|
|
364
|
-
"stableStringify: object keys containing U+2028 (LINE SEPARATOR) or U+2029 (PARAGRAPH SEPARATOR) are not allowed;
|
|
364
|
+
"stableStringify: object keys containing U+2028 (LINE SEPARATOR) or U+2029 (PARAGRAPH SEPARATOR) are not allowed; stable canonical bytes require neither be present (some JSON encoders escape them, others emit them raw)."
|
|
365
365
|
);
|
|
366
366
|
}
|
|
367
367
|
}
|
|
@@ -581,12 +581,12 @@ async function buildEnvSigningKey(opts) {
|
|
|
581
581
|
);
|
|
582
582
|
}
|
|
583
583
|
const canonicalPrivateJwk = detectedAlg === "EdDSA" ? { kty: jwkDict.kty, crv: jwkDict.crv, x: jwkDict.x, d: jwkDict.d } : { kty: jwkDict.kty, crv: jwkDict.crv, x: jwkDict.x, y: jwkDict.y, d: jwkDict.d };
|
|
584
|
-
const { importJWK } = await import("jose");
|
|
584
|
+
const { importJWK: importJWK3 } = await import("jose");
|
|
585
585
|
const { createPublicKey } = await import("crypto");
|
|
586
586
|
let privateKey;
|
|
587
587
|
let publicNodeKey;
|
|
588
588
|
try {
|
|
589
|
-
privateKey = await
|
|
589
|
+
privateKey = await importJWK3(
|
|
590
590
|
canonicalPrivateJwk,
|
|
591
591
|
detectedAlg
|
|
592
592
|
);
|
|
@@ -7949,13 +7949,13 @@ var init_schemas = __esm({
|
|
|
7949
7949
|
}
|
|
7950
7950
|
return propValues;
|
|
7951
7951
|
});
|
|
7952
|
-
const
|
|
7952
|
+
const isObject3 = isObject;
|
|
7953
7953
|
const catchall2 = def.catchall;
|
|
7954
7954
|
let value;
|
|
7955
7955
|
inst._zod.parse = (payload, ctx) => {
|
|
7956
7956
|
value ?? (value = _normalized.value);
|
|
7957
7957
|
const input = payload.value;
|
|
7958
|
-
if (!
|
|
7958
|
+
if (!isObject3(input)) {
|
|
7959
7959
|
payload.issues.push({
|
|
7960
7960
|
expected: "object",
|
|
7961
7961
|
code: "invalid_type",
|
|
@@ -8082,7 +8082,7 @@ var init_schemas = __esm({
|
|
|
8082
8082
|
return (payload, ctx) => fn(shape, payload, ctx);
|
|
8083
8083
|
};
|
|
8084
8084
|
let fastpass;
|
|
8085
|
-
const
|
|
8085
|
+
const isObject3 = isObject;
|
|
8086
8086
|
const jit = !globalConfig.jitless;
|
|
8087
8087
|
const allowsEval2 = allowsEval;
|
|
8088
8088
|
const fastEnabled = jit && allowsEval2.value;
|
|
@@ -8091,7 +8091,7 @@ var init_schemas = __esm({
|
|
|
8091
8091
|
inst._zod.parse = (payload, ctx) => {
|
|
8092
8092
|
value ?? (value = _normalized.value);
|
|
8093
8093
|
const input = payload.value;
|
|
8094
|
-
if (!
|
|
8094
|
+
if (!isObject3(input)) {
|
|
8095
8095
|
payload.issues.push({
|
|
8096
8096
|
expected: "object",
|
|
8097
8097
|
code: "invalid_type",
|
|
@@ -20328,6 +20328,53 @@ var init_networks = __esm({
|
|
|
20328
20328
|
}
|
|
20329
20329
|
});
|
|
20330
20330
|
|
|
20331
|
+
// src/payment/amounts.ts
|
|
20332
|
+
function usdToAtomic(usd, opts) {
|
|
20333
|
+
const { decimals } = opts;
|
|
20334
|
+
if (!Number.isInteger(decimals) || decimals < 0) {
|
|
20335
|
+
throw new RangeError(`decimals must be a non-negative integer, got ${decimals}`);
|
|
20336
|
+
}
|
|
20337
|
+
if (typeof usd === "number") {
|
|
20338
|
+
if (!Number.isFinite(usd)) {
|
|
20339
|
+
throw new RangeError(`usd must be finite, got ${usd}`);
|
|
20340
|
+
}
|
|
20341
|
+
if (usd < 0) {
|
|
20342
|
+
throw new RangeError(`usd must be non-negative, got ${usd}`);
|
|
20343
|
+
}
|
|
20344
|
+
}
|
|
20345
|
+
const s = (typeof usd === "number" ? usd.toString() : usd).trim();
|
|
20346
|
+
if (s.startsWith("-")) {
|
|
20347
|
+
throw new RangeError(`usd must be non-negative, got ${s}`);
|
|
20348
|
+
}
|
|
20349
|
+
if (s === "NaN" || s === "Infinity") {
|
|
20350
|
+
throw new RangeError(`usd must be finite, got ${s}`);
|
|
20351
|
+
}
|
|
20352
|
+
const match = /^(\d*)(?:\.(\d*))?$/.exec(s);
|
|
20353
|
+
if (!match || match[1] === "" && (match[2] === void 0 || match[2] === "")) {
|
|
20354
|
+
throw new SyntaxError(`invalid usd value: ${JSON.stringify(usd)}`);
|
|
20355
|
+
}
|
|
20356
|
+
const intPart = match[1] || "0";
|
|
20357
|
+
const fracPart = match[2] ?? "";
|
|
20358
|
+
if (fracPart.length <= decimals) {
|
|
20359
|
+
return BigInt(intPart + fracPart.padEnd(decimals, "0"));
|
|
20360
|
+
}
|
|
20361
|
+
const kept = fracPart.slice(0, decimals);
|
|
20362
|
+
const roundDigit = fracPart[decimals];
|
|
20363
|
+
let result = BigInt(intPart + kept);
|
|
20364
|
+
if (roundDigit >= "5") {
|
|
20365
|
+
result += 1n;
|
|
20366
|
+
}
|
|
20367
|
+
return result;
|
|
20368
|
+
}
|
|
20369
|
+
function formatUsdCents(cents, decimals = 2) {
|
|
20370
|
+
return (cents / 100).toFixed(decimals);
|
|
20371
|
+
}
|
|
20372
|
+
var init_amounts = __esm({
|
|
20373
|
+
"src/payment/amounts.ts"() {
|
|
20374
|
+
"use strict";
|
|
20375
|
+
}
|
|
20376
|
+
});
|
|
20377
|
+
|
|
20331
20378
|
// src/payment/rails.ts
|
|
20332
20379
|
function lookupRail(name) {
|
|
20333
20380
|
return rails[name];
|
|
@@ -20427,8 +20474,7 @@ function buildPaymentRequestBlob({
|
|
|
20427
20474
|
const decimalsResolved = decimals ?? railDef?.decimals ?? 6;
|
|
20428
20475
|
const currencyResolved = currency ?? railDef?.currency ?? "usd";
|
|
20429
20476
|
const chainIdResolved = chainId ?? railDef?.chainId;
|
|
20430
|
-
const
|
|
20431
|
-
const amountRaw = BigInt(Math.round(amountNum * 10 ** decimalsResolved)).toString();
|
|
20477
|
+
const amountRaw = usdToAtomic(amountUsd, { decimals: decimalsResolved }).toString();
|
|
20432
20478
|
const blob = { amount: amountRaw, currency: currencyResolved, decimals: decimalsResolved };
|
|
20433
20479
|
if (recipient) blob.recipient = recipient;
|
|
20434
20480
|
const methodDetails = {};
|
|
@@ -20455,6 +20501,7 @@ function paymentDirective({
|
|
|
20455
20501
|
var init_directive = __esm({
|
|
20456
20502
|
"src/payment/directive.ts"() {
|
|
20457
20503
|
"use strict";
|
|
20504
|
+
init_amounts();
|
|
20458
20505
|
init_rails();
|
|
20459
20506
|
}
|
|
20460
20507
|
});
|
|
@@ -20804,6 +20851,7 @@ var init_well_known = __esm({
|
|
|
20804
20851
|
});
|
|
20805
20852
|
|
|
20806
20853
|
// src/core.ts
|
|
20854
|
+
import { createHash } from "crypto";
|
|
20807
20855
|
import {
|
|
20808
20856
|
AgentScore,
|
|
20809
20857
|
InvalidCredentialError,
|
|
@@ -21126,8 +21174,9 @@ var INVALID_CREDENTIAL_INSTRUCTIONS = JSON.stringify({
|
|
|
21126
21174
|
],
|
|
21127
21175
|
user_message: "The operator_token is not recognized. Use a different stored token, or restart the verification session flow to mint a new one."
|
|
21128
21176
|
});
|
|
21129
|
-
function buildAgentMemoryHint() {
|
|
21177
|
+
function buildAgentMemoryHint(aipTrustedIssuers) {
|
|
21130
21178
|
const api = CANONICAL_AGENTSCORE_API;
|
|
21179
|
+
const acceptsAip = aipTrustedIssuers !== void 0 && aipTrustedIssuers.length > 0;
|
|
21131
21180
|
return {
|
|
21132
21181
|
save_for_future_agentscore_gates: true,
|
|
21133
21182
|
pattern_summary: "AgentScore is a cross-merchant identity substrate for agent commerce. Any endpoint that returns 403 missing_identity / identity_verification_required or 402 challenges referencing AgentScore uses the same identity system. One KYC works across all AgentScore merchants \u2014 no re-verification per site. Save this pattern (and the endpoints below) to persistent memory so future AgentScore encounters skip the cold-start session flow.",
|
|
@@ -21135,8 +21184,12 @@ function buildAgentMemoryHint() {
|
|
|
21135
21184
|
identity_check_endpoint: `${api}/v1/credentials`,
|
|
21136
21185
|
identity_paths: {
|
|
21137
21186
|
wallet: "If paying from a crypto wallet you have used before on any AgentScore merchant, send `X-Wallet-Address: 0x...`. Shortest path; no token lifecycle to manage.",
|
|
21138
|
-
operator_token: "For any rail (Stripe SPT, card, or a wallet you have not linked yet), send `X-Operator-Token: opc_...`. Reusable across merchants until the token expires."
|
|
21187
|
+
operator_token: "For any rail (Stripe SPT, card, or a wallet you have not linked yet), send `X-Operator-Token: opc_...`. Reusable across merchants until the token expires.",
|
|
21188
|
+
...acceptsAip && {
|
|
21189
|
+
agent_identity: 'If you hold an AIP Agent Identity Token from a trusted issuer (see aip_trusted_issuers), present it: send the JWT in an `Agent-Identity` header plus an RFC 9421 HTTP Message Signature (Signature-Input + Signature over @method @authority @path agent-identity, tag="agent-identity") signed with the token-bound cnf key. The merchant verifies it and applies its policy \u2014 no separate AgentScore credential needed.'
|
|
21190
|
+
}
|
|
21139
21191
|
},
|
|
21192
|
+
...acceptsAip && { aip_trusted_issuers: aipTrustedIssuers },
|
|
21140
21193
|
bootstrap: "If you have neither a linked wallet nor a valid operator_token, follow the session/verify flow in the per-request `agent_instructions` block. This happens at most once per agent identity \u2014 after first verification, the operator_token and any subsequently used wallet are reusable everywhere.",
|
|
21141
21194
|
do_not_persist_in_memory: ["operator_token", "poll_secret"],
|
|
21142
21195
|
persist_in_credential_store: ["operator_token"]
|
|
@@ -21158,11 +21211,12 @@ function createAgentScoreCore(options) {
|
|
|
21158
21211
|
baseUrl: rawBaseUrl = "https://api.agentscore.sh",
|
|
21159
21212
|
chain: gateChain,
|
|
21160
21213
|
userAgent,
|
|
21161
|
-
createSessionOnMissing
|
|
21214
|
+
createSessionOnMissing,
|
|
21215
|
+
aipTrustedIssuers
|
|
21162
21216
|
} = options;
|
|
21163
21217
|
const baseUrl = stripTrailingSlashes(rawBaseUrl);
|
|
21164
|
-
const agentMemoryHint = buildAgentMemoryHint();
|
|
21165
|
-
const defaultUa = `@agent-score/commerce@${"2.
|
|
21218
|
+
const agentMemoryHint = buildAgentMemoryHint(aipTrustedIssuers);
|
|
21219
|
+
const defaultUa = `@agent-score/commerce@${"2.5.0"}`;
|
|
21166
21220
|
const userAgentHeader = userAgent ? `${userAgent} (${defaultUa})` : defaultUa;
|
|
21167
21221
|
const sdk = new AgentScore({ apiKey, baseUrl, userAgent: userAgentHeader });
|
|
21168
21222
|
const sessionSdkCache = /* @__PURE__ */ new Map();
|
|
@@ -21180,6 +21234,7 @@ function createAgentScoreCore(options) {
|
|
|
21180
21234
|
return s;
|
|
21181
21235
|
}
|
|
21182
21236
|
const cache = new TTLCache(cacheSeconds * 1e3);
|
|
21237
|
+
const lastSignerRaw = /* @__PURE__ */ new Map();
|
|
21183
21238
|
async function tryMintSessionDenial(ctx) {
|
|
21184
21239
|
if (!createSessionOnMissing) return void 0;
|
|
21185
21240
|
try {
|
|
@@ -21237,13 +21292,18 @@ function createAgentScoreCore(options) {
|
|
|
21237
21292
|
}
|
|
21238
21293
|
}
|
|
21239
21294
|
async function evaluate(identity, ctx, signer) {
|
|
21240
|
-
if (!identity || !identity.address && !identity.operatorToken) {
|
|
21295
|
+
if (!identity || !identity.address?.trim() && !identity.operatorToken?.trim() && !identity.aipToken?.trim()) {
|
|
21241
21296
|
if (failOpen) return { kind: "allow" };
|
|
21242
21297
|
const sessionReason = await tryMintSessionDenial(ctx);
|
|
21243
21298
|
if (sessionReason) return { kind: "deny", reason: sessionReason };
|
|
21299
|
+
const acceptsAip = aipTrustedIssuers !== void 0 && aipTrustedIssuers.length > 0;
|
|
21300
|
+
const aipStep = acceptsAip ? [
|
|
21301
|
+
`If you hold an AIP Agent Identity Token from a trusted issuer (${aipTrustedIssuers.join(", ")}): present it \u2014 send the JWT in an Agent-Identity header plus an RFC 9421 HTTP Message Signature (Signature-Input + Signature over @method @authority @path agent-identity, tag="agent-identity") signed with the token-bound cnf key. This satisfies identity in one round trip without an AgentScore credential.`
|
|
21302
|
+
] : [];
|
|
21244
21303
|
const missingIdentityInstructions = JSON.stringify({
|
|
21245
21304
|
action: "probe_identity_then_session",
|
|
21246
21305
|
steps: [
|
|
21306
|
+
...aipStep,
|
|
21247
21307
|
"If you have a wallet AND this merchant accepts a wallet-signing rail (Tempo MPP or x402 \u2014 see openapi.json x-payment-info): send X-Wallet-Address and retry. If the wallet is already linked to an AgentScore operator (via prior SIWE claim or prior captureWallet), this passes in one round trip. If the wallet is unlinked or the account has no KYC, the 403 will include a verify_url \u2014 share it with the user to claim the wallet + complete KYC, then retry.",
|
|
21248
21308
|
"If step 1 is denied or you already have a stored operator_token (valid + not expired): send X-Operator-Token: opc_... and retry.",
|
|
21249
21309
|
"If neither applies: retry with NO identity header. Merchants that auto-create verification sessions (most AgentScore merchants do) return verify_url + session_id + poll_secret in the 403 body \u2014 share verify_url with the user, then poll poll_url every 5s with the X-Poll-Secret header until status=verified (the poll returns a one-time operator_token). If the retry returns the same bare 403, this merchant does not support self-service session bootstrapping \u2014 direct the user to https://agentscore.sh/sign-up to create an AgentScore identity and mint an operator_token from their dashboard (https://agentscore.sh/dashboard/verify). The user hands the opc_... to you, and you retry with X-Operator-Token."
|
|
@@ -21259,7 +21319,7 @@ function createAgentScoreCore(options) {
|
|
|
21259
21319
|
}
|
|
21260
21320
|
};
|
|
21261
21321
|
}
|
|
21262
|
-
const cacheKey2 = identity.operatorToken?.toLowerCase() ?? (identity.address ? normalizeAddress(identity.address) : "");
|
|
21322
|
+
const cacheKey2 = identity.aipToken ? `aip:${createHash("sha256").update(identity.aipToken).digest("hex")}` : identity.operatorToken?.toLowerCase() ?? (identity.address ? normalizeAddress(identity.address) : "");
|
|
21263
21323
|
const cached2 = cache.get(cacheKey2);
|
|
21264
21324
|
if (cached2) {
|
|
21265
21325
|
if (cached2.allow) {
|
|
@@ -21304,7 +21364,7 @@ function createAgentScoreCore(options) {
|
|
|
21304
21364
|
// regardless of policy.require_sanctions_clear (which gates the separate NAME screen).
|
|
21305
21365
|
...signer && { signer: { address: signer.address, network: signer.network } }
|
|
21306
21366
|
};
|
|
21307
|
-
const result = identity.address ? await sdk.assess(identity.address, { ...opts, operatorToken: identity.operatorToken }) : await sdk.assess(null, { ...opts, operatorToken: identity.operatorToken });
|
|
21367
|
+
const result = identity.aipToken ? await sdk.assess(null, { ...opts, aipToken: identity.aipToken }) : identity.address ? await sdk.assess(identity.address, { ...opts, operatorToken: identity.operatorToken }) : await sdk.assess(null, { ...opts, operatorToken: identity.operatorToken });
|
|
21308
21368
|
data = result;
|
|
21309
21369
|
} catch (err) {
|
|
21310
21370
|
if (err instanceof PaymentRequiredError) {
|
|
@@ -21375,6 +21435,9 @@ function createAgentScoreCore(options) {
|
|
|
21375
21435
|
const decisionReasons = data.decision_reasons ?? [];
|
|
21376
21436
|
const allow = decision === "allow" || decision == null;
|
|
21377
21437
|
cache.set(cacheKey2, { allow, decision: decision ?? void 0, reasons: decisionReasons, raw: data });
|
|
21438
|
+
if (identity.address !== void 0 && identity.operatorToken === void 0 && identity.aipToken === void 0 && (data.signer_match !== void 0 || data.signer_sanctions !== void 0)) {
|
|
21439
|
+
lastSignerRaw.set(normalizeAddress(identity.address), data);
|
|
21440
|
+
}
|
|
21378
21441
|
if (allow) {
|
|
21379
21442
|
const quota = data.quota;
|
|
21380
21443
|
return {
|
|
@@ -21439,9 +21502,7 @@ function createAgentScoreCore(options) {
|
|
|
21439
21502
|
}
|
|
21440
21503
|
function getSignerVerdict(claimedAddress) {
|
|
21441
21504
|
const claimedNorm = normalizeAddress(claimedAddress);
|
|
21442
|
-
const
|
|
21443
|
-
if (!cached2) return void 0;
|
|
21444
|
-
const raw = cached2.raw;
|
|
21505
|
+
const raw = lastSignerRaw.get(claimedNorm) ?? cache.get(claimedNorm)?.raw;
|
|
21445
21506
|
if (!raw) return void 0;
|
|
21446
21507
|
const rawMatch = raw.signer_match;
|
|
21447
21508
|
const rawSanctions = raw.signer_sanctions;
|
|
@@ -21502,6 +21563,18 @@ async function extractSolanaSignerFromCredential(credential) {
|
|
|
21502
21563
|
}
|
|
21503
21564
|
}
|
|
21504
21565
|
async function extractPaymentSigner(request, x402PaymentHeader) {
|
|
21566
|
+
if (x402PaymentHeader) {
|
|
21567
|
+
try {
|
|
21568
|
+
const decoded = atob(x402PaymentHeader);
|
|
21569
|
+
const parsed = JSON.parse(decoded);
|
|
21570
|
+
const from9 = parsed?.payload?.authorization?.from;
|
|
21571
|
+
if (typeof from9 === "string" && /^0x[0-9a-fA-F]{40}$/.test(from9)) {
|
|
21572
|
+
return { address: from9.toLowerCase(), network: "evm" };
|
|
21573
|
+
}
|
|
21574
|
+
} catch (err) {
|
|
21575
|
+
console.warn("[gate] x402 signer extraction failed:", err instanceof Error ? err.message : err);
|
|
21576
|
+
}
|
|
21577
|
+
}
|
|
21505
21578
|
const authHeader = request.headers.get("authorization");
|
|
21506
21579
|
if (authHeader) {
|
|
21507
21580
|
try {
|
|
@@ -21521,18 +21594,6 @@ async function extractPaymentSigner(request, x402PaymentHeader) {
|
|
|
21521
21594
|
console.warn("[gate] MPP signer extraction failed:", err instanceof Error ? err.message : err);
|
|
21522
21595
|
}
|
|
21523
21596
|
}
|
|
21524
|
-
if (x402PaymentHeader) {
|
|
21525
|
-
try {
|
|
21526
|
-
const decoded = atob(x402PaymentHeader);
|
|
21527
|
-
const parsed = JSON.parse(decoded);
|
|
21528
|
-
const from9 = parsed?.payload?.authorization?.from;
|
|
21529
|
-
if (typeof from9 === "string" && /^0x[0-9a-fA-F]{40}$/.test(from9)) {
|
|
21530
|
-
return { address: from9.toLowerCase(), network: "evm" };
|
|
21531
|
-
}
|
|
21532
|
-
} catch (err) {
|
|
21533
|
-
console.warn("[gate] x402 signer extraction failed:", err instanceof Error ? err.message : err);
|
|
21534
|
-
}
|
|
21535
|
-
}
|
|
21536
21597
|
return null;
|
|
21537
21598
|
}
|
|
21538
21599
|
async function extractPaymentSignerFromAuth(authHeader, x402PaymentHeader) {
|
|
@@ -21704,7 +21765,7 @@ function validateShippingAgainstPolicy(opts) {
|
|
|
21704
21765
|
}
|
|
21705
21766
|
|
|
21706
21767
|
// src/identity/tokens.ts
|
|
21707
|
-
import { createHash } from "crypto";
|
|
21768
|
+
import { createHash as createHash2 } from "crypto";
|
|
21708
21769
|
|
|
21709
21770
|
// src/payment/payment_header.ts
|
|
21710
21771
|
function toTitleCase(name) {
|
|
@@ -21740,7 +21801,7 @@ function hasMppxHeader(input) {
|
|
|
21740
21801
|
|
|
21741
21802
|
// src/identity/tokens.ts
|
|
21742
21803
|
function hashOperatorToken(plaintext) {
|
|
21743
|
-
return
|
|
21804
|
+
return createHash2("sha256").update(plaintext, "utf8").digest("hex");
|
|
21744
21805
|
}
|
|
21745
21806
|
function extractOwnerScope(input) {
|
|
21746
21807
|
const headers = asHeaders(input);
|
|
@@ -21793,6 +21854,710 @@ function warnMissingApiKeyOnce(label) {
|
|
|
21793
21854
|
);
|
|
21794
21855
|
}
|
|
21795
21856
|
|
|
21857
|
+
// src/aip/verify.ts
|
|
21858
|
+
import { decodeJwt, decodeProtectedHeader, importJWK as importJWK2, jwtVerify } from "jose";
|
|
21859
|
+
|
|
21860
|
+
// src/aip/http-signature.ts
|
|
21861
|
+
import { calculateJwkThumbprint, importJWK } from "jose";
|
|
21862
|
+
var { subtle } = globalThis.crypto;
|
|
21863
|
+
var b64ToBytes = (b64) => {
|
|
21864
|
+
const bin = atob(b64);
|
|
21865
|
+
const out = new Uint8Array(bin.length);
|
|
21866
|
+
for (let i = 0; i < bin.length; i++) {
|
|
21867
|
+
out[i] = bin.charCodeAt(i);
|
|
21868
|
+
}
|
|
21869
|
+
return out;
|
|
21870
|
+
};
|
|
21871
|
+
var bytesToB64 = (bytes) => {
|
|
21872
|
+
let bin = "";
|
|
21873
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
21874
|
+
bin += String.fromCharCode(bytes[i]);
|
|
21875
|
+
}
|
|
21876
|
+
return btoa(bin);
|
|
21877
|
+
};
|
|
21878
|
+
var AIP_COVERED_COMPONENTS = ["@method", "@authority", "@path", "agent-identity"];
|
|
21879
|
+
var AIP_SIGNATURE_TAG = "agent-identity";
|
|
21880
|
+
var DEFAULT_MAX_SKEW_SECONDS = 60;
|
|
21881
|
+
var normalizeAuthority = (authority) => {
|
|
21882
|
+
const lower = authority.trim().toLowerCase();
|
|
21883
|
+
const colon = lower.lastIndexOf(":");
|
|
21884
|
+
if (colon === -1) {
|
|
21885
|
+
return lower;
|
|
21886
|
+
}
|
|
21887
|
+
if (lower.includes("]") && colon < lower.indexOf("]")) {
|
|
21888
|
+
return lower;
|
|
21889
|
+
}
|
|
21890
|
+
const host = lower.slice(0, colon);
|
|
21891
|
+
const port = lower.slice(colon + 1);
|
|
21892
|
+
if (port === "80" || port === "443") {
|
|
21893
|
+
return host;
|
|
21894
|
+
}
|
|
21895
|
+
return lower;
|
|
21896
|
+
};
|
|
21897
|
+
var componentValue = (name, input) => {
|
|
21898
|
+
switch (name) {
|
|
21899
|
+
case "@method":
|
|
21900
|
+
return input.method.toUpperCase();
|
|
21901
|
+
case "@authority":
|
|
21902
|
+
return normalizeAuthority(input.authority);
|
|
21903
|
+
case "@path":
|
|
21904
|
+
return input.path;
|
|
21905
|
+
case "agent-identity":
|
|
21906
|
+
return input.agentIdentity.trim();
|
|
21907
|
+
default:
|
|
21908
|
+
return input.extra?.[name] ?? null;
|
|
21909
|
+
}
|
|
21910
|
+
};
|
|
21911
|
+
var serializeComponentList = (components) => `(${components.map((c) => `"${c}"`).join(" ")})`;
|
|
21912
|
+
var serializeParams = (p) => {
|
|
21913
|
+
const parts = [];
|
|
21914
|
+
if (p.created !== void 0) {
|
|
21915
|
+
parts.push(`created=${p.created}`);
|
|
21916
|
+
}
|
|
21917
|
+
if (p.expires !== void 0) {
|
|
21918
|
+
parts.push(`expires=${p.expires}`);
|
|
21919
|
+
}
|
|
21920
|
+
if (p.keyid !== void 0) {
|
|
21921
|
+
parts.push(`keyid="${p.keyid}"`);
|
|
21922
|
+
}
|
|
21923
|
+
if (p.alg !== void 0) {
|
|
21924
|
+
parts.push(`alg="${p.alg}"`);
|
|
21925
|
+
}
|
|
21926
|
+
if (p.tag !== void 0) {
|
|
21927
|
+
parts.push(`tag="${p.tag}"`);
|
|
21928
|
+
}
|
|
21929
|
+
return parts.map((s) => `;${s}`).join("");
|
|
21930
|
+
};
|
|
21931
|
+
var buildSignatureBase = (params, input) => {
|
|
21932
|
+
const lines = [];
|
|
21933
|
+
for (const name of params.components) {
|
|
21934
|
+
const value = componentValue(name, input);
|
|
21935
|
+
if (value === null) {
|
|
21936
|
+
throw new MissingComponentError(name);
|
|
21937
|
+
}
|
|
21938
|
+
lines.push(`"${name}": ${value}`);
|
|
21939
|
+
}
|
|
21940
|
+
const paramsValue = serializeComponentList(params.components) + serializeParams(params);
|
|
21941
|
+
lines.push(`"@signature-params": ${paramsValue}`);
|
|
21942
|
+
return lines.join("\n");
|
|
21943
|
+
};
|
|
21944
|
+
var MissingComponentError = class extends Error {
|
|
21945
|
+
constructor(component) {
|
|
21946
|
+
super(`signature base missing covered component: ${component}`);
|
|
21947
|
+
this.component = component;
|
|
21948
|
+
this.name = "MissingComponentError";
|
|
21949
|
+
}
|
|
21950
|
+
component;
|
|
21951
|
+
};
|
|
21952
|
+
var parseSignatureInput = (header, tag = AIP_SIGNATURE_TAG) => {
|
|
21953
|
+
const members = splitDictionary(header);
|
|
21954
|
+
if (members.length === 0) {
|
|
21955
|
+
return null;
|
|
21956
|
+
}
|
|
21957
|
+
const parsed = members.map((m) => {
|
|
21958
|
+
const params = parseInnerListMember(m.value);
|
|
21959
|
+
return params ? { label: m.label, params } : null;
|
|
21960
|
+
}).filter((x) => x !== null);
|
|
21961
|
+
if (parsed.length === 0) {
|
|
21962
|
+
return null;
|
|
21963
|
+
}
|
|
21964
|
+
const tagged = parsed.find((p) => p.params.tag === tag);
|
|
21965
|
+
if (tagged) {
|
|
21966
|
+
return tagged;
|
|
21967
|
+
}
|
|
21968
|
+
if (parsed.length === 1 && parsed[0].params.tag === void 0) {
|
|
21969
|
+
return parsed[0];
|
|
21970
|
+
}
|
|
21971
|
+
return null;
|
|
21972
|
+
};
|
|
21973
|
+
var parseSignatureValue = (header, label) => {
|
|
21974
|
+
const members = splitDictionary(header);
|
|
21975
|
+
const member = members.find((m) => m.label === label);
|
|
21976
|
+
if (!member) {
|
|
21977
|
+
return null;
|
|
21978
|
+
}
|
|
21979
|
+
const v = member.value.trim();
|
|
21980
|
+
if (!v.startsWith(":") || !v.endsWith(":") || v.length < 2) {
|
|
21981
|
+
return null;
|
|
21982
|
+
}
|
|
21983
|
+
const b64 = v.slice(1, -1);
|
|
21984
|
+
try {
|
|
21985
|
+
return b64ToBytes(b64);
|
|
21986
|
+
} catch {
|
|
21987
|
+
return null;
|
|
21988
|
+
}
|
|
21989
|
+
};
|
|
21990
|
+
var splitDictionary = (header) => {
|
|
21991
|
+
const out = [];
|
|
21992
|
+
let depth = 0;
|
|
21993
|
+
let inBytes = false;
|
|
21994
|
+
let inString = false;
|
|
21995
|
+
let current = "";
|
|
21996
|
+
for (let i = 0; i < header.length; i++) {
|
|
21997
|
+
const ch = header[i];
|
|
21998
|
+
if (inString) {
|
|
21999
|
+
current += ch;
|
|
22000
|
+
if (ch === '"' && header[i - 1] !== "\\") {
|
|
22001
|
+
inString = false;
|
|
22002
|
+
}
|
|
22003
|
+
continue;
|
|
22004
|
+
}
|
|
22005
|
+
if (ch === '"') {
|
|
22006
|
+
inString = true;
|
|
22007
|
+
current += ch;
|
|
22008
|
+
continue;
|
|
22009
|
+
}
|
|
22010
|
+
if (ch === ":") {
|
|
22011
|
+
inBytes = !inBytes;
|
|
22012
|
+
current += ch;
|
|
22013
|
+
continue;
|
|
22014
|
+
}
|
|
22015
|
+
if (!inBytes && ch === "(") {
|
|
22016
|
+
depth++;
|
|
22017
|
+
current += ch;
|
|
22018
|
+
continue;
|
|
22019
|
+
}
|
|
22020
|
+
if (!inBytes && ch === ")") {
|
|
22021
|
+
depth = Math.max(0, depth - 1);
|
|
22022
|
+
current += ch;
|
|
22023
|
+
continue;
|
|
22024
|
+
}
|
|
22025
|
+
if (!inBytes && depth === 0 && ch === ",") {
|
|
22026
|
+
pushMember(out, current);
|
|
22027
|
+
current = "";
|
|
22028
|
+
continue;
|
|
22029
|
+
}
|
|
22030
|
+
current += ch;
|
|
22031
|
+
}
|
|
22032
|
+
pushMember(out, current);
|
|
22033
|
+
return out;
|
|
22034
|
+
};
|
|
22035
|
+
var pushMember = (out, raw) => {
|
|
22036
|
+
const trimmed = raw.trim();
|
|
22037
|
+
if (!trimmed) {
|
|
22038
|
+
return;
|
|
22039
|
+
}
|
|
22040
|
+
const eq = trimmed.indexOf("=");
|
|
22041
|
+
if (eq === -1) {
|
|
22042
|
+
return;
|
|
22043
|
+
}
|
|
22044
|
+
const label = trimmed.slice(0, eq).trim();
|
|
22045
|
+
const value = trimmed.slice(eq + 1).trim();
|
|
22046
|
+
if (label) {
|
|
22047
|
+
out.push({ label, value });
|
|
22048
|
+
}
|
|
22049
|
+
};
|
|
22050
|
+
var parseInnerListMember = (value) => {
|
|
22051
|
+
const open = value.indexOf("(");
|
|
22052
|
+
const close = value.indexOf(")", open + 1);
|
|
22053
|
+
if (open === -1 || close === -1) {
|
|
22054
|
+
return null;
|
|
22055
|
+
}
|
|
22056
|
+
const listBody = value.slice(open + 1, close).trim();
|
|
22057
|
+
const components = listBody.length === 0 ? [] : (listBody.match(/"[^"]*"/g) ?? []).map((s) => s.slice(1, -1));
|
|
22058
|
+
const params = { components };
|
|
22059
|
+
const paramStr = value.slice(close + 1);
|
|
22060
|
+
const re = /;\s*([a-zA-Z][a-zA-Z0-9_-]*)\s*=\s*("(?:[^"\\]|\\.)*"|-?\d+)/g;
|
|
22061
|
+
let match;
|
|
22062
|
+
while ((match = re.exec(paramStr)) !== null) {
|
|
22063
|
+
const key = match[1];
|
|
22064
|
+
const raw = match[2];
|
|
22065
|
+
const val = raw.startsWith('"') ? raw.slice(1, -1) : Number(raw);
|
|
22066
|
+
if (key === "created") {
|
|
22067
|
+
params.created = val;
|
|
22068
|
+
} else if (key === "expires") {
|
|
22069
|
+
params.expires = val;
|
|
22070
|
+
} else if (key === "keyid") {
|
|
22071
|
+
params.keyid = val;
|
|
22072
|
+
} else if (key === "tag") {
|
|
22073
|
+
params.tag = val;
|
|
22074
|
+
} else if (key === "alg") {
|
|
22075
|
+
params.alg = val;
|
|
22076
|
+
}
|
|
22077
|
+
}
|
|
22078
|
+
return params;
|
|
22079
|
+
};
|
|
22080
|
+
var verifyMessageSignature = async (input) => {
|
|
22081
|
+
const selected = parseSignatureInput(input.signatureInput);
|
|
22082
|
+
if (!selected) {
|
|
22083
|
+
return { ok: false, reason: "no_aip_signature" };
|
|
22084
|
+
}
|
|
22085
|
+
const { label, params } = selected;
|
|
22086
|
+
if (params.alg !== void 0 && !["ed25519", "eddsa"].includes(params.alg.toLowerCase())) {
|
|
22087
|
+
return { ok: false, reason: "unsupported_alg" };
|
|
22088
|
+
}
|
|
22089
|
+
for (const required3 of AIP_COVERED_COMPONENTS) {
|
|
22090
|
+
if (!params.components.includes(required3)) {
|
|
22091
|
+
return { ok: false, reason: "missing_covered_component" };
|
|
22092
|
+
}
|
|
22093
|
+
}
|
|
22094
|
+
const now = input.now ?? Math.floor(Date.now() / 1e3);
|
|
22095
|
+
const skew = input.maxSkewSeconds ?? DEFAULT_MAX_SKEW_SECONDS;
|
|
22096
|
+
if (params.created !== void 0 && params.created > now + skew) {
|
|
22097
|
+
return { ok: false, reason: "created_in_future" };
|
|
22098
|
+
}
|
|
22099
|
+
if (params.expires !== void 0 && params.expires < now - skew) {
|
|
22100
|
+
return { ok: false, reason: "expired" };
|
|
22101
|
+
}
|
|
22102
|
+
if (!params.keyid) {
|
|
22103
|
+
return { ok: false, reason: "missing_keyid" };
|
|
22104
|
+
}
|
|
22105
|
+
const cnf = input.cnfJwk;
|
|
22106
|
+
if (cnf.kty !== "OKP" || cnf.crv !== "Ed25519" || typeof cnf.x !== "string" || cnf.x.length === 0) {
|
|
22107
|
+
return { ok: false, reason: "unsupported_cnf_key" };
|
|
22108
|
+
}
|
|
22109
|
+
let thumbprint;
|
|
22110
|
+
try {
|
|
22111
|
+
thumbprint = await calculateJwkThumbprint(input.cnfJwk, "sha256");
|
|
22112
|
+
} catch {
|
|
22113
|
+
return { ok: false, reason: "unsupported_cnf_key" };
|
|
22114
|
+
}
|
|
22115
|
+
if (params.keyid !== thumbprint) {
|
|
22116
|
+
return { ok: false, reason: "keyid_mismatch" };
|
|
22117
|
+
}
|
|
22118
|
+
const sig = parseSignatureValue(input.signature, label);
|
|
22119
|
+
if (!sig) {
|
|
22120
|
+
return { ok: false, reason: "malformed_signature" };
|
|
22121
|
+
}
|
|
22122
|
+
let base;
|
|
22123
|
+
try {
|
|
22124
|
+
base = buildSignatureBase(params, {
|
|
22125
|
+
method: input.method,
|
|
22126
|
+
authority: input.authority,
|
|
22127
|
+
path: input.path,
|
|
22128
|
+
agentIdentity: input.agentIdentity,
|
|
22129
|
+
extra: input.extraComponents
|
|
22130
|
+
});
|
|
22131
|
+
} catch (err) {
|
|
22132
|
+
if (err instanceof MissingComponentError) {
|
|
22133
|
+
return { ok: false, reason: "missing_covered_component" };
|
|
22134
|
+
}
|
|
22135
|
+
throw err;
|
|
22136
|
+
}
|
|
22137
|
+
let valid;
|
|
22138
|
+
try {
|
|
22139
|
+
const key = await importJWK(input.cnfJwk, "EdDSA");
|
|
22140
|
+
if (!(key instanceof CryptoKey)) {
|
|
22141
|
+
return { ok: false, reason: "signature_invalid" };
|
|
22142
|
+
}
|
|
22143
|
+
valid = await subtle.verify(
|
|
22144
|
+
{ name: "Ed25519" },
|
|
22145
|
+
key,
|
|
22146
|
+
sig,
|
|
22147
|
+
new TextEncoder().encode(base)
|
|
22148
|
+
);
|
|
22149
|
+
} catch {
|
|
22150
|
+
return { ok: false, reason: "signature_invalid" };
|
|
22151
|
+
}
|
|
22152
|
+
return valid ? { ok: true, params } : { ok: false, reason: "signature_invalid" };
|
|
22153
|
+
};
|
|
22154
|
+
var signMessage = async (input) => {
|
|
22155
|
+
const label = input.label ?? "ait";
|
|
22156
|
+
const components = input.components ?? [...AIP_COVERED_COMPONENTS];
|
|
22157
|
+
const created = input.created ?? Math.floor(Date.now() / 1e3);
|
|
22158
|
+
const keyid = await calculateJwkThumbprint(input.publicJwk, "sha256");
|
|
22159
|
+
const params = {
|
|
22160
|
+
components,
|
|
22161
|
+
created,
|
|
22162
|
+
expires: input.expires,
|
|
22163
|
+
keyid,
|
|
22164
|
+
tag: AIP_SIGNATURE_TAG
|
|
22165
|
+
};
|
|
22166
|
+
const base = buildSignatureBase(params, {
|
|
22167
|
+
method: input.method,
|
|
22168
|
+
authority: input.authority,
|
|
22169
|
+
path: input.path,
|
|
22170
|
+
agentIdentity: input.agentIdentity,
|
|
22171
|
+
extra: input.extraComponents
|
|
22172
|
+
});
|
|
22173
|
+
const key = await importJWK(input.privateJwk, "EdDSA");
|
|
22174
|
+
if (!(key instanceof CryptoKey)) {
|
|
22175
|
+
throw new Error("signMessage: expected an Ed25519 private CryptoKey");
|
|
22176
|
+
}
|
|
22177
|
+
const sigBytes = await subtle.sign(
|
|
22178
|
+
"Ed25519",
|
|
22179
|
+
key,
|
|
22180
|
+
new TextEncoder().encode(base)
|
|
22181
|
+
);
|
|
22182
|
+
const b64 = bytesToB64(new Uint8Array(sigBytes));
|
|
22183
|
+
const signatureInput = `${label}=${serializeComponentList(components)}${serializeParams(params)}`;
|
|
22184
|
+
const signature2 = `${label}=:${b64}:`;
|
|
22185
|
+
return { signatureInput, signature: signature2 };
|
|
22186
|
+
};
|
|
22187
|
+
|
|
22188
|
+
// src/aip/types.ts
|
|
22189
|
+
var isObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
22190
|
+
var isNonEmptyString = (v) => typeof v === "string" && v.length > 0;
|
|
22191
|
+
var isAitShape = (payload) => isObject2(payload) && isObject2(payload.cnf) && isObject2(payload.agent);
|
|
22192
|
+
var validateAitPayload = (payload) => {
|
|
22193
|
+
if (!isObject2(payload)) {
|
|
22194
|
+
return { ok: false, reason: "not_an_object" };
|
|
22195
|
+
}
|
|
22196
|
+
if (!isNonEmptyString(payload.aip_version)) {
|
|
22197
|
+
return { ok: false, reason: "missing_aip_version" };
|
|
22198
|
+
}
|
|
22199
|
+
if (!isNonEmptyString(payload.iss)) {
|
|
22200
|
+
return { ok: false, reason: "missing_iss" };
|
|
22201
|
+
}
|
|
22202
|
+
if (!isNonEmptyString(payload.sub)) {
|
|
22203
|
+
return { ok: false, reason: "missing_sub" };
|
|
22204
|
+
}
|
|
22205
|
+
if (typeof payload.iat !== "number") {
|
|
22206
|
+
return { ok: false, reason: "missing_iat" };
|
|
22207
|
+
}
|
|
22208
|
+
if (typeof payload.exp !== "number") {
|
|
22209
|
+
return { ok: false, reason: "missing_exp" };
|
|
22210
|
+
}
|
|
22211
|
+
if (!isObject2(payload.cnf) || !isObject2(payload.cnf.jwk)) {
|
|
22212
|
+
return { ok: false, reason: "missing_cnf" };
|
|
22213
|
+
}
|
|
22214
|
+
if (!isObject2(payload.agent) || !isNonEmptyString(payload.agent.provider)) {
|
|
22215
|
+
return { ok: false, reason: "missing_agent_provider" };
|
|
22216
|
+
}
|
|
22217
|
+
if (payload.trust_level === "human_confirmed") {
|
|
22218
|
+
const auth = payload.auth;
|
|
22219
|
+
const amr = isObject2(auth) ? auth.amr : void 0;
|
|
22220
|
+
if (!Array.isArray(amr) || amr.length === 0) {
|
|
22221
|
+
return { ok: false, reason: "human_confirmed_without_amr" };
|
|
22222
|
+
}
|
|
22223
|
+
}
|
|
22224
|
+
return { ok: true, payload };
|
|
22225
|
+
};
|
|
22226
|
+
|
|
22227
|
+
// src/aip/verify.ts
|
|
22228
|
+
var AGENT_IDENTITY_HEADER = "agent-identity";
|
|
22229
|
+
var verifyAit = async (ctx, opts) => {
|
|
22230
|
+
if (ctx.agentIdentityHeaders.length === 0) {
|
|
22231
|
+
return { ok: false, reason: "no_token" };
|
|
22232
|
+
}
|
|
22233
|
+
if (!ctx.signatureInput || !ctx.signature) {
|
|
22234
|
+
return { ok: false, reason: "pop_signature_missing" };
|
|
22235
|
+
}
|
|
22236
|
+
let lastFailure = "malformed_token";
|
|
22237
|
+
for (const raw of ctx.agentIdentityHeaders) {
|
|
22238
|
+
const token = stripBearer(raw);
|
|
22239
|
+
let header;
|
|
22240
|
+
let payload;
|
|
22241
|
+
try {
|
|
22242
|
+
header = decodeProtectedHeader(token);
|
|
22243
|
+
payload = decodeJwt(token);
|
|
22244
|
+
} catch {
|
|
22245
|
+
lastFailure = "malformed_token";
|
|
22246
|
+
continue;
|
|
22247
|
+
}
|
|
22248
|
+
if (header.alg === void 0 || header.alg.toLowerCase() === "none") {
|
|
22249
|
+
lastFailure = "malformed_token";
|
|
22250
|
+
continue;
|
|
22251
|
+
}
|
|
22252
|
+
if (!isAitShape(payload)) {
|
|
22253
|
+
lastFailure = "malformed_token";
|
|
22254
|
+
continue;
|
|
22255
|
+
}
|
|
22256
|
+
const validated = validateAitPayload(payload);
|
|
22257
|
+
if (!validated.ok) {
|
|
22258
|
+
lastFailure = "invalid_claims";
|
|
22259
|
+
continue;
|
|
22260
|
+
}
|
|
22261
|
+
const claims = validated.payload;
|
|
22262
|
+
const keyLookup = await opts.jwks.getKey(claims.iss, header.kid);
|
|
22263
|
+
if (!keyLookup.ok) {
|
|
22264
|
+
lastFailure = keyLookup.reason === "untrusted_issuer" || keyLookup.reason === "insecure_issuer" ? "untrusted_issuer" : "key_unavailable";
|
|
22265
|
+
continue;
|
|
22266
|
+
}
|
|
22267
|
+
const jwtClockTolerance = opts.maxSkewSeconds ?? 60;
|
|
22268
|
+
try {
|
|
22269
|
+
const idpKey = await importJWK2(keyLookup.key, normalizeAlg(header.alg));
|
|
22270
|
+
await jwtVerify(token, idpKey, {
|
|
22271
|
+
// Pin the signature algorithm allowlist (RFC 8725 §3.1) — also rejects `alg:none`. Without
|
|
22272
|
+
// this, jose accepts whatever alg the resolved JWK supports, so a trusted IdP publishing a
|
|
22273
|
+
// non-Ed25519 (e.g. RSA/EC) `use:sig` key would let an attacker present an RS256/ES256
|
|
22274
|
+
// token that verifies. Matches the server-side allowlist in core/api aip-verify.
|
|
22275
|
+
algorithms: AIT_SIGNING_ALGS,
|
|
22276
|
+
clockTolerance: jwtClockTolerance,
|
|
22277
|
+
currentDate: opts.now !== void 0 ? new Date(opts.now * 1e3) : void 0
|
|
22278
|
+
});
|
|
22279
|
+
} catch (err) {
|
|
22280
|
+
lastFailure = isExpiry(err) ? "expired_token" : "idp_signature_invalid";
|
|
22281
|
+
continue;
|
|
22282
|
+
}
|
|
22283
|
+
const nowSec = opts.now ?? Math.floor(Date.now() / 1e3);
|
|
22284
|
+
if (claims.iat > nowSec + jwtClockTolerance) {
|
|
22285
|
+
lastFailure = "expired_token";
|
|
22286
|
+
continue;
|
|
22287
|
+
}
|
|
22288
|
+
const popResult = await verifyMessageSignature({
|
|
22289
|
+
method: ctx.method,
|
|
22290
|
+
authority: ctx.authority,
|
|
22291
|
+
path: ctx.path,
|
|
22292
|
+
agentIdentity: raw,
|
|
22293
|
+
signatureInput: ctx.signatureInput,
|
|
22294
|
+
signature: ctx.signature,
|
|
22295
|
+
cnfJwk: claims.cnf.jwk,
|
|
22296
|
+
now: opts.now,
|
|
22297
|
+
maxSkewSeconds: opts.maxSkewSeconds
|
|
22298
|
+
});
|
|
22299
|
+
if (!popResult.ok) {
|
|
22300
|
+
lastFailure = "pop_signature_invalid";
|
|
22301
|
+
continue;
|
|
22302
|
+
}
|
|
22303
|
+
return { ok: true, ait: { payload: claims, iss: claims.iss, cnfJwk: claims.cnf.jwk, token } };
|
|
22304
|
+
}
|
|
22305
|
+
return { ok: false, reason: lastFailure };
|
|
22306
|
+
};
|
|
22307
|
+
var stripBearer = (value) => {
|
|
22308
|
+
const trimmed = value.trim();
|
|
22309
|
+
return /^bearer\s+/i.test(trimmed) ? trimmed.replace(/^bearer\s+/i, "") : trimmed;
|
|
22310
|
+
};
|
|
22311
|
+
var AIT_SIGNING_ALGS = ["EdDSA", "ES256"];
|
|
22312
|
+
var normalizeAlg = (alg) => alg.toLowerCase() === "eddsa" ? "EdDSA" : alg;
|
|
22313
|
+
var isExpiry = (err) => typeof err === "object" && err !== null && err.code === "ERR_JWT_EXPIRED";
|
|
22314
|
+
|
|
22315
|
+
// src/aip/request.ts
|
|
22316
|
+
var readAgentIdentityHeaders = (headers) => {
|
|
22317
|
+
const raw = headers.get(AGENT_IDENTITY_HEADER);
|
|
22318
|
+
if (!raw) {
|
|
22319
|
+
return [];
|
|
22320
|
+
}
|
|
22321
|
+
return raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
22322
|
+
};
|
|
22323
|
+
var deriveAuthority = (req, url2) => req.headers.get("host") ?? url2.host;
|
|
22324
|
+
var buildVerifyContextFromRequest = (req) => {
|
|
22325
|
+
const url2 = new URL(req.url);
|
|
22326
|
+
return {
|
|
22327
|
+
method: req.method,
|
|
22328
|
+
authority: deriveAuthority(req, url2),
|
|
22329
|
+
path: url2.pathname,
|
|
22330
|
+
agentIdentityHeaders: readAgentIdentityHeaders(req.headers),
|
|
22331
|
+
signatureInput: req.headers.get("signature-input"),
|
|
22332
|
+
signature: req.headers.get("signature")
|
|
22333
|
+
};
|
|
22334
|
+
};
|
|
22335
|
+
var hasAgentIdentityHeader = (req) => readAgentIdentityHeaders(req.headers).length > 0;
|
|
22336
|
+
var readNodeHeader = (headers, name) => {
|
|
22337
|
+
const v = headers[name] ?? headers[name.toLowerCase()];
|
|
22338
|
+
if (v === void 0) {
|
|
22339
|
+
return void 0;
|
|
22340
|
+
}
|
|
22341
|
+
return Array.isArray(v) ? v.join(", ") : v;
|
|
22342
|
+
};
|
|
22343
|
+
var hasAgentIdentityHeaderNode = (headers) => {
|
|
22344
|
+
const raw = readNodeHeader(headers, "agent-identity");
|
|
22345
|
+
return raw !== void 0 && raw.split(",").some((s) => s.trim().length > 0);
|
|
22346
|
+
};
|
|
22347
|
+
var buildVerifyContextFromParts = (parts) => {
|
|
22348
|
+
const agentIdentityRaw = readNodeHeader(parts.headers, "agent-identity");
|
|
22349
|
+
const agentIdentityHeaders = agentIdentityRaw ? agentIdentityRaw.split(",").map((s) => s.trim()).filter((s) => s.length > 0) : [];
|
|
22350
|
+
const host = parts.authority ?? readNodeHeader(parts.headers, "host") ?? "";
|
|
22351
|
+
let path;
|
|
22352
|
+
try {
|
|
22353
|
+
path = new URL(parts.url, host ? `http://${host}` : "http://localhost").pathname;
|
|
22354
|
+
} catch {
|
|
22355
|
+
const q = parts.url.indexOf("?");
|
|
22356
|
+
path = q === -1 ? parts.url : parts.url.slice(0, q);
|
|
22357
|
+
}
|
|
22358
|
+
return {
|
|
22359
|
+
method: parts.method,
|
|
22360
|
+
authority: host,
|
|
22361
|
+
path,
|
|
22362
|
+
agentIdentityHeaders,
|
|
22363
|
+
signatureInput: readNodeHeader(parts.headers, "signature-input") ?? null,
|
|
22364
|
+
signature: readNodeHeader(parts.headers, "signature") ?? null
|
|
22365
|
+
};
|
|
22366
|
+
};
|
|
22367
|
+
|
|
22368
|
+
// src/aip/gate.ts
|
|
22369
|
+
var verifyFromContext = async (ctx, opts) => {
|
|
22370
|
+
const result = await verifyAit(ctx, { jwks: opts.jwks, now: opts.now, maxSkewSeconds: opts.maxSkewSeconds });
|
|
22371
|
+
return result.ok ? { ok: true, ait: result.ait } : { ok: false, failure: result.reason };
|
|
22372
|
+
};
|
|
22373
|
+
var verifyAitRequest = (req, opts) => verifyFromContext(buildVerifyContextFromRequest(req), opts);
|
|
22374
|
+
var verifyAitParts = (parts, opts) => verifyFromContext(buildVerifyContextFromParts(parts), opts);
|
|
22375
|
+
var aipErrorCode = (failure) => {
|
|
22376
|
+
switch (failure) {
|
|
22377
|
+
case "no_token":
|
|
22378
|
+
case "pop_signature_missing":
|
|
22379
|
+
return "agent_identity_required";
|
|
22380
|
+
case "untrusted_issuer":
|
|
22381
|
+
return "untrusted_issuer";
|
|
22382
|
+
case "expired_token":
|
|
22383
|
+
return "expired_token";
|
|
22384
|
+
case "invalid_claims":
|
|
22385
|
+
return "insufficient_claims";
|
|
22386
|
+
case "key_unavailable":
|
|
22387
|
+
return "idp_unavailable";
|
|
22388
|
+
case "malformed_token":
|
|
22389
|
+
case "idp_signature_invalid":
|
|
22390
|
+
case "pop_signature_invalid":
|
|
22391
|
+
return "invalid_signature";
|
|
22392
|
+
default:
|
|
22393
|
+
return "invalid_signature";
|
|
22394
|
+
}
|
|
22395
|
+
};
|
|
22396
|
+
var aipErrorStatus = (failure) => {
|
|
22397
|
+
switch (failure) {
|
|
22398
|
+
case "key_unavailable":
|
|
22399
|
+
return 503;
|
|
22400
|
+
case "untrusted_issuer":
|
|
22401
|
+
case "invalid_claims":
|
|
22402
|
+
return 403;
|
|
22403
|
+
default:
|
|
22404
|
+
return 401;
|
|
22405
|
+
}
|
|
22406
|
+
};
|
|
22407
|
+
var aipErrorDetail = (failure) => {
|
|
22408
|
+
switch (failure) {
|
|
22409
|
+
case "no_token":
|
|
22410
|
+
return "No Agent-Identity token was presented.";
|
|
22411
|
+
case "pop_signature_missing":
|
|
22412
|
+
return "The request is missing the RFC 9421 HTTP Message Signature that proves possession of the token-bound key.";
|
|
22413
|
+
case "untrusted_issuer":
|
|
22414
|
+
return "The token's issuer is not in this service's trusted-issuer list.";
|
|
22415
|
+
case "expired_token":
|
|
22416
|
+
return "The Agent Identity Token has expired.";
|
|
22417
|
+
case "invalid_claims":
|
|
22418
|
+
return "The token is missing required claims for this endpoint.";
|
|
22419
|
+
case "malformed_token":
|
|
22420
|
+
return "The Agent-Identity header could not be parsed as an Agent Identity Token.";
|
|
22421
|
+
case "idp_signature_invalid":
|
|
22422
|
+
return "The identity provider's signature on the token failed verification.";
|
|
22423
|
+
case "pop_signature_invalid":
|
|
22424
|
+
return "The request signature did not match the key bound to the token.";
|
|
22425
|
+
case "key_unavailable":
|
|
22426
|
+
return "The identity provider's signing key could not be resolved.";
|
|
22427
|
+
default:
|
|
22428
|
+
return "Token verification failed.";
|
|
22429
|
+
}
|
|
22430
|
+
};
|
|
22431
|
+
var buildAipErrorBody = (failure) => {
|
|
22432
|
+
const code = aipErrorCode(failure);
|
|
22433
|
+
return {
|
|
22434
|
+
type: `urn:aip:error:${code}`,
|
|
22435
|
+
title: code.replace(/_/g, " "),
|
|
22436
|
+
status: aipErrorStatus(failure),
|
|
22437
|
+
detail: aipErrorDetail(failure)
|
|
22438
|
+
};
|
|
22439
|
+
};
|
|
22440
|
+
|
|
22441
|
+
// src/aip/jwks.ts
|
|
22442
|
+
var JWKS_WELL_KNOWN_PATH = "/.well-known/agent-identity/jwks.json";
|
|
22443
|
+
var HARD_MAX_CACHE_SECONDS = 86400;
|
|
22444
|
+
var DEFAULT_CACHE_SECONDS = 300;
|
|
22445
|
+
var AGENTSCORE_CANONICAL_ISSUER = "https://agentscore.sh";
|
|
22446
|
+
var canonicalizeIssuer = (iss) => {
|
|
22447
|
+
let url2;
|
|
22448
|
+
try {
|
|
22449
|
+
url2 = new URL(iss.trim());
|
|
22450
|
+
} catch {
|
|
22451
|
+
return null;
|
|
22452
|
+
}
|
|
22453
|
+
const scheme = url2.protocol.toLowerCase();
|
|
22454
|
+
const host = url2.hostname.toLowerCase();
|
|
22455
|
+
const isDefaultPort = url2.port === "" || scheme === "https:" && url2.port === "443" || scheme === "http:" && url2.port === "80";
|
|
22456
|
+
const portPart = isDefaultPort ? "" : `:${url2.port}`;
|
|
22457
|
+
const path = url2.pathname === "/" ? "" : url2.pathname.replace(/\/$/, "");
|
|
22458
|
+
return `${scheme}//${host}${portPart}${path}`;
|
|
22459
|
+
};
|
|
22460
|
+
var resolveCacheSeconds = (cacheControl) => {
|
|
22461
|
+
if (!cacheControl) {
|
|
22462
|
+
return DEFAULT_CACHE_SECONDS;
|
|
22463
|
+
}
|
|
22464
|
+
if (/\bno-store\b/i.test(cacheControl) || /\bno-cache\b/i.test(cacheControl)) {
|
|
22465
|
+
return DEFAULT_CACHE_SECONDS;
|
|
22466
|
+
}
|
|
22467
|
+
const m = /\bmax-age\s*=\s*(\d+)/i.exec(cacheControl);
|
|
22468
|
+
if (!m) {
|
|
22469
|
+
return DEFAULT_CACHE_SECONDS;
|
|
22470
|
+
}
|
|
22471
|
+
const advertised = Number(m[1]);
|
|
22472
|
+
if (!Number.isFinite(advertised) || advertised <= 0) {
|
|
22473
|
+
return DEFAULT_CACHE_SECONDS;
|
|
22474
|
+
}
|
|
22475
|
+
return Math.min(advertised, HARD_MAX_CACHE_SECONDS);
|
|
22476
|
+
};
|
|
22477
|
+
var signingKeys = (keys) => keys.filter((k) => k.use === void 0 || k.use === "sig");
|
|
22478
|
+
var JwksCache = class {
|
|
22479
|
+
trusted;
|
|
22480
|
+
fetchImpl;
|
|
22481
|
+
now;
|
|
22482
|
+
userAgent;
|
|
22483
|
+
cache = /* @__PURE__ */ new Map();
|
|
22484
|
+
constructor(opts) {
|
|
22485
|
+
this.trusted = new Set(
|
|
22486
|
+
[AGENTSCORE_CANONICAL_ISSUER, ...opts.trustedIssuers ?? []].map(canonicalizeIssuer).filter((s) => s !== null)
|
|
22487
|
+
);
|
|
22488
|
+
this.fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
22489
|
+
this.now = opts.now ?? Date.now;
|
|
22490
|
+
this.userAgent = opts.userAgent ?? "@agentscore/commerce";
|
|
22491
|
+
}
|
|
22492
|
+
/** Is this issuer on the canonicalized trust list? */
|
|
22493
|
+
isTrusted(iss) {
|
|
22494
|
+
const canon = canonicalizeIssuer(iss);
|
|
22495
|
+
return canon !== null && this.trusted.has(canon);
|
|
22496
|
+
}
|
|
22497
|
+
/**
|
|
22498
|
+
* Resolve the signing key for `(iss, kid)`. Enforces trust + HTTPS, serves from cache when
|
|
22499
|
+
* fresh, and refetches once on a kid-miss before giving up.
|
|
22500
|
+
*/
|
|
22501
|
+
async getKey(iss, kid) {
|
|
22502
|
+
const canon = canonicalizeIssuer(iss);
|
|
22503
|
+
if (canon === null || !this.trusted.has(canon)) {
|
|
22504
|
+
return { ok: false, reason: "untrusted_issuer" };
|
|
22505
|
+
}
|
|
22506
|
+
if (!canon.startsWith("https://")) {
|
|
22507
|
+
return { ok: false, reason: "insecure_issuer" };
|
|
22508
|
+
}
|
|
22509
|
+
const cached2 = this.cache.get(canon);
|
|
22510
|
+
if (cached2 && this.now() < cached2.expiresAt) {
|
|
22511
|
+
const hit2 = this.select(cached2.keys, kid);
|
|
22512
|
+
if (hit2) {
|
|
22513
|
+
return { ok: true, key: hit2 };
|
|
22514
|
+
}
|
|
22515
|
+
}
|
|
22516
|
+
const refreshed = await this.refresh(canon);
|
|
22517
|
+
if (!refreshed.ok) {
|
|
22518
|
+
return refreshed;
|
|
22519
|
+
}
|
|
22520
|
+
const hit = this.select(refreshed.keys, kid);
|
|
22521
|
+
return hit ? { ok: true, key: hit } : { ok: false, reason: "key_not_found" };
|
|
22522
|
+
}
|
|
22523
|
+
select(keys, kid) {
|
|
22524
|
+
const candidates = signingKeys(keys);
|
|
22525
|
+
if (kid !== void 0) {
|
|
22526
|
+
const byKid = candidates.find((k) => k.kid === kid);
|
|
22527
|
+
if (byKid) {
|
|
22528
|
+
return byKid;
|
|
22529
|
+
}
|
|
22530
|
+
return void 0;
|
|
22531
|
+
}
|
|
22532
|
+
return candidates.length === 1 ? candidates[0] : void 0;
|
|
22533
|
+
}
|
|
22534
|
+
async refresh(canonIssuer) {
|
|
22535
|
+
const url2 = `${canonIssuer}${JWKS_WELL_KNOWN_PATH}`;
|
|
22536
|
+
let res;
|
|
22537
|
+
try {
|
|
22538
|
+
res = await this.fetchImpl(url2, { headers: { "User-Agent": this.userAgent, Accept: "application/jwk-set+json, application/json" } });
|
|
22539
|
+
} catch {
|
|
22540
|
+
return { ok: false, reason: "fetch_failed" };
|
|
22541
|
+
}
|
|
22542
|
+
if (!res.ok) {
|
|
22543
|
+
return { ok: false, reason: "fetch_failed" };
|
|
22544
|
+
}
|
|
22545
|
+
let body;
|
|
22546
|
+
try {
|
|
22547
|
+
body = await res.json();
|
|
22548
|
+
} catch {
|
|
22549
|
+
return { ok: false, reason: "malformed_jwks" };
|
|
22550
|
+
}
|
|
22551
|
+
if (typeof body !== "object" || body === null || !Array.isArray(body.keys)) {
|
|
22552
|
+
return { ok: false, reason: "malformed_jwks" };
|
|
22553
|
+
}
|
|
22554
|
+
const keys = body.keys;
|
|
22555
|
+
const ttlSeconds = resolveCacheSeconds(res.headers.get("cache-control"));
|
|
22556
|
+
this.cache.set(canonIssuer, { keys, expiresAt: this.now() + ttlSeconds * 1e3 });
|
|
22557
|
+
return { ok: true, keys };
|
|
22558
|
+
}
|
|
22559
|
+
};
|
|
22560
|
+
|
|
21796
22561
|
// src/payment/rail_spec.ts
|
|
21797
22562
|
init_usdc();
|
|
21798
22563
|
async function resolveRecipient(r) {
|
|
@@ -21945,10 +22710,11 @@ function buildAgentInstructions({
|
|
|
21945
22710
|
|
|
21946
22711
|
// src/challenge/agent_memory.ts
|
|
21947
22712
|
function firstEncounterAgentMemory({
|
|
21948
|
-
firstEncounter
|
|
22713
|
+
firstEncounter,
|
|
22714
|
+
aipTrustedIssuers
|
|
21949
22715
|
}) {
|
|
21950
22716
|
if (!firstEncounter) return void 0;
|
|
21951
|
-
return buildAgentMemoryHint();
|
|
22717
|
+
return buildAgentMemoryHint(aipTrustedIssuers);
|
|
21952
22718
|
}
|
|
21953
22719
|
|
|
21954
22720
|
// src/challenge/body.ts
|
|
@@ -22980,6 +23746,30 @@ function pickRail(rails2, key) {
|
|
|
22980
23746
|
const spec = rails2[key];
|
|
22981
23747
|
return spec === void 0 ? void 0 : spec;
|
|
22982
23748
|
}
|
|
23749
|
+
function buildAipTrustedIssuers(externalIssuers) {
|
|
23750
|
+
const out = [AGENTSCORE_CANONICAL_ISSUER, ...externalIssuers ?? []];
|
|
23751
|
+
const seen = /* @__PURE__ */ new Set();
|
|
23752
|
+
const deduped = [];
|
|
23753
|
+
for (const iss of out) {
|
|
23754
|
+
const key = canonicalizeIssuer(iss) ?? iss;
|
|
23755
|
+
if (!seen.has(key)) {
|
|
23756
|
+
seen.add(key);
|
|
23757
|
+
deduped.push(iss);
|
|
23758
|
+
}
|
|
23759
|
+
}
|
|
23760
|
+
return deduped;
|
|
23761
|
+
}
|
|
23762
|
+
function aipTrustedIssuerSet(cfg) {
|
|
23763
|
+
return buildAipTrustedIssuers(cfg.trustedIssuers);
|
|
23764
|
+
}
|
|
23765
|
+
function resolveIssuerPolicy(issuerPolicies, iss) {
|
|
23766
|
+
const target = canonicalizeIssuer(iss);
|
|
23767
|
+
if (target === null) return void 0;
|
|
23768
|
+
for (const [key, policy] of Object.entries(issuerPolicies)) {
|
|
23769
|
+
if (canonicalizeIssuer(key) === target) return policy;
|
|
23770
|
+
}
|
|
23771
|
+
return void 0;
|
|
23772
|
+
}
|
|
22983
23773
|
var Checkout = class {
|
|
22984
23774
|
rails;
|
|
22985
23775
|
url;
|
|
@@ -22997,6 +23787,15 @@ var Checkout = class {
|
|
|
22997
23787
|
discoveryExtensions;
|
|
22998
23788
|
discoveryProbe;
|
|
22999
23789
|
_x402ServerGetter;
|
|
23790
|
+
/** Lazily-built JWKS cache for AIP verification, shared across requests so issuer keys
|
|
23791
|
+
* are fetched once and cached (per the verifier's hard 24h cap). Built on first AIT. */
|
|
23792
|
+
aipJwks;
|
|
23793
|
+
getAipJwks(cfg) {
|
|
23794
|
+
if (this.aipJwks === void 0) {
|
|
23795
|
+
this.aipJwks = new JwksCache(cfg.trustedIssuers !== void 0 ? { trustedIssuers: cfg.trustedIssuers } : {});
|
|
23796
|
+
}
|
|
23797
|
+
return this.aipJwks;
|
|
23798
|
+
}
|
|
23000
23799
|
/**
|
|
23001
23800
|
* True when the merchant has configured an identity-bearing policy flag —
|
|
23002
23801
|
* `require_kyc`, `require_sanctions_clear` (name screening on the KYC
|
|
@@ -23284,7 +24083,64 @@ var Checkout = class {
|
|
|
23284
24083
|
}
|
|
23285
24084
|
return result;
|
|
23286
24085
|
}
|
|
24086
|
+
const headers = normalizeHeadersToLowercase(ctx.request.headers);
|
|
24087
|
+
const walletAddress = headers["x-wallet-address"];
|
|
24088
|
+
const operatorToken = headers["x-operator-token"];
|
|
24089
|
+
let aipToken;
|
|
24090
|
+
let aipIssuer;
|
|
24091
|
+
if (gate.aip !== void 0 && hasAgentIdentityHeaderNode(headers)) {
|
|
24092
|
+
const aipResult = await verifyAitParts(
|
|
24093
|
+
{
|
|
24094
|
+
method: ctx.request.method,
|
|
24095
|
+
url: ctx.request.url,
|
|
24096
|
+
headers,
|
|
24097
|
+
...gate.aip.authority !== void 0 && { authority: gate.aip.authority }
|
|
24098
|
+
},
|
|
24099
|
+
{
|
|
24100
|
+
jwks: this.getAipJwks(gate.aip),
|
|
24101
|
+
...gate.aip.maxSkewSeconds !== void 0 && { maxSkewSeconds: gate.aip.maxSkewSeconds }
|
|
24102
|
+
}
|
|
24103
|
+
);
|
|
24104
|
+
if (!aipResult.ok) {
|
|
24105
|
+
const body2 = buildAipErrorBody(aipResult.failure);
|
|
24106
|
+
return {
|
|
24107
|
+
status: body2.status,
|
|
24108
|
+
body: body2,
|
|
24109
|
+
headers: {
|
|
24110
|
+
"content-type": "application/problem+json",
|
|
24111
|
+
// 503 = the IdP's JWKS was unreachable (transient infra, not a bad token). Hint a
|
|
24112
|
+
// short backoff so agents retry rather than uselessly re-signing.
|
|
24113
|
+
...body2.status === 503 && { "retry-after": "5" }
|
|
24114
|
+
}
|
|
24115
|
+
};
|
|
24116
|
+
}
|
|
24117
|
+
aipToken = aipResult.ait.token;
|
|
24118
|
+
aipIssuer = aipResult.ait.iss;
|
|
24119
|
+
}
|
|
24120
|
+
const issuerPolicy = aipIssuer !== void 0 && gate.aip?.issuerPolicies !== void 0 ? resolveIssuerPolicy(gate.aip.issuerPolicies, aipIssuer) : void 0;
|
|
24121
|
+
const effPolicy = issuerPolicy ?? {
|
|
24122
|
+
...gate.requireKyc !== void 0 && { requireKyc: gate.requireKyc },
|
|
24123
|
+
...gate.requireSanctionsClear !== void 0 && { requireSanctionsClear: gate.requireSanctionsClear },
|
|
24124
|
+
...gate.minAge !== void 0 && { minAge: gate.minAge },
|
|
24125
|
+
...gate.blockedJurisdictions !== void 0 && { blockedJurisdictions: gate.blockedJurisdictions },
|
|
24126
|
+
...gate.allowedJurisdictions !== void 0 && { allowedJurisdictions: gate.allowedJurisdictions }
|
|
24127
|
+
};
|
|
23287
24128
|
if (gate.apiKey === void 0) {
|
|
24129
|
+
if (aipToken !== void 0) {
|
|
24130
|
+
const hasPolicy = effPolicy.requireKyc || effPolicy.requireSanctionsClear || effPolicy.minAge != null || effPolicy.blockedJurisdictions !== void 0 || effPolicy.allowedJurisdictions !== void 0;
|
|
24131
|
+
if (hasPolicy) {
|
|
24132
|
+
return {
|
|
24133
|
+
status: 403,
|
|
24134
|
+
body: {
|
|
24135
|
+
error: {
|
|
24136
|
+
code: "aip_policy_requires_api_key",
|
|
24137
|
+
message: "This gate declares compliance policy (KYC / age / sanctions / jurisdiction) but has no AgentScore apiKey, so the Agent Identity Token\u2019s claims cannot be evaluated. Configure gate.apiKey to enable policy enforcement on AITs."
|
|
24138
|
+
}
|
|
24139
|
+
}
|
|
24140
|
+
};
|
|
24141
|
+
}
|
|
24142
|
+
return null;
|
|
24143
|
+
}
|
|
23288
24144
|
return this.runWalletSanctionsOnly(ctx);
|
|
23289
24145
|
}
|
|
23290
24146
|
let policyOverride;
|
|
@@ -23296,19 +24152,25 @@ var Checkout = class {
|
|
|
23296
24152
|
apiKey: gate.apiKey,
|
|
23297
24153
|
...gate.baseUrl !== void 0 && { baseUrl: gate.baseUrl },
|
|
23298
24154
|
...gate.userAgent !== void 0 && { userAgent: gate.userAgent },
|
|
23299
|
-
|
|
23300
|
-
|
|
23301
|
-
|
|
23302
|
-
|
|
23303
|
-
...
|
|
24155
|
+
// Compliance fields come from effPolicy — the per-issuer override for the verified AIT's
|
|
24156
|
+
// issuer when one is configured, else the gate defaults (see effPolicy above). A whole-
|
|
24157
|
+
// policy replacement: an issuer override of `{ requireKyc, minAge }` sends ONLY those,
|
|
24158
|
+
// so sanctions / jurisdiction are not enforced for that issuer.
|
|
24159
|
+
...effPolicy.requireKyc !== void 0 && { requireKyc: effPolicy.requireKyc },
|
|
24160
|
+
...effPolicy.requireSanctionsClear !== void 0 && { requireSanctionsClear: effPolicy.requireSanctionsClear },
|
|
24161
|
+
...effPolicy.minAge !== void 0 && { minAge: effPolicy.minAge },
|
|
24162
|
+
...effPolicy.blockedJurisdictions !== void 0 && { blockedJurisdictions: effPolicy.blockedJurisdictions },
|
|
24163
|
+
...effPolicy.allowedJurisdictions !== void 0 && { allowedJurisdictions: effPolicy.allowedJurisdictions },
|
|
23304
24164
|
...gate.failOpen !== void 0 && { failOpen: gate.failOpen },
|
|
23305
24165
|
...gate.cacheSeconds !== void 0 && { cacheSeconds: gate.cacheSeconds },
|
|
23306
24166
|
...gate.chain !== void 0 && { chain: gate.chain },
|
|
24167
|
+
// Surface AIP acceptance in the missing-identity recovery instructions + agent_memory
|
|
24168
|
+
// hint so agents holding an AIT learn they can present it instead of bootstrapping.
|
|
24169
|
+
...gate.aip !== void 0 && { aipTrustedIssuers: aipTrustedIssuerSet(gate.aip) },
|
|
23307
24170
|
// Auto-default `createSessionOnMissing` from gate config when the merchant
|
|
23308
|
-
// didn't supply one
|
|
23309
|
-
//
|
|
23310
|
-
//
|
|
23311
|
-
// pre-minting an order_id) supply their own config instead.
|
|
24171
|
+
// didn't supply one, so every gated route gets the bootstrap session-mint UX
|
|
24172
|
+
// out of the box. Merchants who need custom session context or onBeforeSession
|
|
24173
|
+
// side effects (goods merchants pre-minting an order_id) supply their own config.
|
|
23312
24174
|
createSessionOnMissing: gate.createSessionOnMissing ?? {
|
|
23313
24175
|
apiKey: gate.apiKey,
|
|
23314
24176
|
...gate.baseUrl !== void 0 && { baseUrl: gate.baseUrl },
|
|
@@ -23318,10 +24180,7 @@ var Checkout = class {
|
|
|
23318
24180
|
...policyOverride ?? {}
|
|
23319
24181
|
};
|
|
23320
24182
|
const core = createAgentScoreCore(coreOpts);
|
|
23321
|
-
const
|
|
23322
|
-
const walletAddress = headers["x-wallet-address"];
|
|
23323
|
-
const operatorToken = headers["x-operator-token"];
|
|
23324
|
-
const identity = walletAddress !== void 0 || operatorToken !== void 0 ? {
|
|
24183
|
+
const identity = aipToken !== void 0 ? { aipToken } : walletAddress !== void 0 || operatorToken !== void 0 ? {
|
|
23325
24184
|
...walletAddress !== void 0 && { address: walletAddress },
|
|
23326
24185
|
...operatorToken !== void 0 && { operatorToken }
|
|
23327
24186
|
} : void 0;
|
|
@@ -23340,7 +24199,7 @@ var Checkout = class {
|
|
|
23340
24199
|
});
|
|
23341
24200
|
};
|
|
23342
24201
|
}
|
|
23343
|
-
if (walletAddress !== void 0) {
|
|
24202
|
+
if (aipToken === void 0 && walletAddress !== void 0) {
|
|
23344
24203
|
const verdict = core.getSignerVerdict(walletAddress);
|
|
23345
24204
|
const sm = verdict?.signer_match;
|
|
23346
24205
|
if (sm && sm.kind !== "pass") {
|
|
@@ -23685,8 +24544,13 @@ var Checkout = class {
|
|
|
23685
24544
|
retryBody: ctx.request.body,
|
|
23686
24545
|
// Merchants without an identity-bearing gate get a clean 402: no
|
|
23687
24546
|
// AgentScore-identity bootstrap describing a verification flow they
|
|
23688
|
-
// don't run. Wallet OFAC (the always-on default) doesn't need it.
|
|
23689
|
-
|
|
24547
|
+
// don't run. Wallet OFAC (the always-on default) doesn't need it. When the
|
|
24548
|
+
// merchant accepts AIP, advertise the agent_identity path too (AgentScore's
|
|
24549
|
+
// own issuer is always trusted, so this fires even with no external issuers).
|
|
24550
|
+
agentMemory: firstEncounterAgentMemory({
|
|
24551
|
+
firstEncounter: this.hasIdentityGate(),
|
|
24552
|
+
...this.gate?.aip !== void 0 && { aipTrustedIssuers: aipTrustedIssuerSet(this.gate.aip) }
|
|
24553
|
+
}),
|
|
23690
24554
|
...ctx.pricing.product ? { product: ctx.pricing.product } : {},
|
|
23691
24555
|
...ctx.pricing.bodyExtras ? { extra: ctx.pricing.bodyExtras } : {},
|
|
23692
24556
|
...x402Accepts.length > 0 ? {
|
|
@@ -24045,47 +24909,8 @@ init_wwwauthenticate();
|
|
|
24045
24909
|
init_directive();
|
|
24046
24910
|
init_wwwauthenticate();
|
|
24047
24911
|
|
|
24048
|
-
// src/payment/
|
|
24049
|
-
|
|
24050
|
-
const { decimals } = opts;
|
|
24051
|
-
if (!Number.isInteger(decimals) || decimals < 0) {
|
|
24052
|
-
throw new RangeError(`decimals must be a non-negative integer, got ${decimals}`);
|
|
24053
|
-
}
|
|
24054
|
-
if (typeof usd === "number") {
|
|
24055
|
-
if (!Number.isFinite(usd)) {
|
|
24056
|
-
throw new RangeError(`usd must be finite, got ${usd}`);
|
|
24057
|
-
}
|
|
24058
|
-
if (usd < 0) {
|
|
24059
|
-
throw new RangeError(`usd must be non-negative, got ${usd}`);
|
|
24060
|
-
}
|
|
24061
|
-
}
|
|
24062
|
-
const s = (typeof usd === "number" ? usd.toString() : usd).trim();
|
|
24063
|
-
if (s.startsWith("-")) {
|
|
24064
|
-
throw new RangeError(`usd must be non-negative, got ${s}`);
|
|
24065
|
-
}
|
|
24066
|
-
if (s === "NaN" || s === "Infinity") {
|
|
24067
|
-
throw new RangeError(`usd must be finite, got ${s}`);
|
|
24068
|
-
}
|
|
24069
|
-
const match = /^(\d*)(?:\.(\d*))?$/.exec(s);
|
|
24070
|
-
if (!match || match[1] === "" && (match[2] === void 0 || match[2] === "")) {
|
|
24071
|
-
throw new SyntaxError(`invalid usd value: ${JSON.stringify(usd)}`);
|
|
24072
|
-
}
|
|
24073
|
-
const intPart = match[1] || "0";
|
|
24074
|
-
const fracPart = match[2] ?? "";
|
|
24075
|
-
if (fracPart.length <= decimals) {
|
|
24076
|
-
return BigInt(intPart + fracPart.padEnd(decimals, "0"));
|
|
24077
|
-
}
|
|
24078
|
-
const kept = fracPart.slice(0, decimals);
|
|
24079
|
-
const roundDigit = fracPart[decimals];
|
|
24080
|
-
let result = BigInt(intPart + kept);
|
|
24081
|
-
if (roundDigit >= "5") {
|
|
24082
|
-
result += 1n;
|
|
24083
|
-
}
|
|
24084
|
-
return result;
|
|
24085
|
-
}
|
|
24086
|
-
function formatUsdCents(cents, decimals = 2) {
|
|
24087
|
-
return (cents / 100).toFixed(decimals);
|
|
24088
|
-
}
|
|
24912
|
+
// src/payment/index.ts
|
|
24913
|
+
init_amounts();
|
|
24089
24914
|
|
|
24090
24915
|
// src/payment/solana.ts
|
|
24091
24916
|
async function loadSolanaFeePayer(opts) {
|
|
@@ -24109,6 +24934,7 @@ async function loadSolanaFeePayer(opts) {
|
|
|
24109
24934
|
}
|
|
24110
24935
|
|
|
24111
24936
|
// src/payment/compose_rails.ts
|
|
24937
|
+
init_amounts();
|
|
24112
24938
|
init_usdc();
|
|
24113
24939
|
var warnedStripeBelowMinimum = false;
|
|
24114
24940
|
function buildMppxComposeRails(opts) {
|
|
@@ -24153,7 +24979,13 @@ init_usdc();
|
|
|
24153
24979
|
function buildDefaultCheckoutRails(opts) {
|
|
24154
24980
|
const out = {};
|
|
24155
24981
|
if (opts.tempo) {
|
|
24156
|
-
|
|
24982
|
+
const merged = { recipient: "", ...RAIL_SPEC_DEFAULTS.tempo, ...opts.tempo };
|
|
24983
|
+
if (merged.testnet) {
|
|
24984
|
+
if (opts.tempo.network === void 0) merged.network = "tempo-testnet";
|
|
24985
|
+
if (opts.tempo.chainId === void 0) merged.chainId = 42431;
|
|
24986
|
+
if (opts.tempo.token === void 0) merged.token = USDC.tempo.testnet.address;
|
|
24987
|
+
}
|
|
24988
|
+
out.tempo = merged;
|
|
24157
24989
|
}
|
|
24158
24990
|
if (opts.x402Base) {
|
|
24159
24991
|
const merged = { recipient: "", ...RAIL_SPEC_DEFAULTS.x402Base, ...opts.x402Base };
|
|
@@ -24208,8 +25040,11 @@ function buildSuccessNextSteps(opts) {
|
|
|
24208
25040
|
// src/discovery/index.ts
|
|
24209
25041
|
init_well_known();
|
|
24210
25042
|
|
|
25043
|
+
// src/checkout_compute_first.ts
|
|
25044
|
+
init_amounts();
|
|
25045
|
+
|
|
24211
25046
|
// src/quote_cache.ts
|
|
24212
|
-
import { createHash as
|
|
25047
|
+
import { createHash as createHash3 } from "crypto";
|
|
24213
25048
|
|
|
24214
25049
|
// src/_redis.ts
|
|
24215
25050
|
async function tryCreateRedis(opts) {
|
|
@@ -24262,7 +25097,7 @@ function createQuoteCache(opts = {}) {
|
|
|
24262
25097
|
return {
|
|
24263
25098
|
bodyHashKey(prefix, body) {
|
|
24264
25099
|
const canonical = JSON.stringify(canonicalize2(body));
|
|
24265
|
-
const hash3 =
|
|
25100
|
+
const hash3 = createHash3("sha256").update(`${prefix}::${canonical}`).digest("hex").slice(0, 24);
|
|
24266
25101
|
return `${prefix}::${hash3}`;
|
|
24267
25102
|
},
|
|
24268
25103
|
async read(key) {
|
|
@@ -24776,15 +25611,26 @@ function defaultReadOnlyOnDenied(reason) {
|
|
|
24776
25611
|
export {
|
|
24777
25612
|
A2A_DEFAULT_TRANSPORT,
|
|
24778
25613
|
A2A_PROTOCOL_VERSION,
|
|
25614
|
+
AGENTSCORE_CANONICAL_ISSUER,
|
|
24779
25615
|
AGENTSCORE_UCP_CAPABILITY,
|
|
25616
|
+
AGENT_IDENTITY_HEADER,
|
|
25617
|
+
AIP_COVERED_COMPONENTS,
|
|
25618
|
+
AIP_SIGNATURE_TAG,
|
|
24780
25619
|
Checkout,
|
|
24781
25620
|
CheckoutValidationError,
|
|
24782
25621
|
FIXABLE_DENIAL_REASONS,
|
|
25622
|
+
HARD_MAX_CACHE_SECONDS,
|
|
25623
|
+
JWKS_WELL_KNOWN_PATH,
|
|
25624
|
+
JwksCache,
|
|
24783
25625
|
UCPSigningKey,
|
|
24784
25626
|
UCPVerificationError,
|
|
24785
25627
|
UCP_A2A_EXTENSION_URI,
|
|
25628
|
+
aipErrorCode,
|
|
25629
|
+
aipErrorStatus,
|
|
24786
25630
|
buildA2AAgentCard,
|
|
24787
25631
|
buildAgentMemoryHint,
|
|
25632
|
+
buildAipErrorBody,
|
|
25633
|
+
buildAipTrustedIssuers,
|
|
24788
25634
|
buildContactSupportNextSteps,
|
|
24789
25635
|
buildDefaultCheckoutRails,
|
|
24790
25636
|
buildGateFromPolicy,
|
|
@@ -24793,6 +25639,8 @@ export {
|
|
|
24793
25639
|
buildSignerMismatchBody,
|
|
24794
25640
|
buildUCPProfile,
|
|
24795
25641
|
buildVerificationRequiredBody,
|
|
25642
|
+
buildVerifyContextFromRequest,
|
|
25643
|
+
canonicalizeIssuer,
|
|
24796
25644
|
computeFirstCheckout,
|
|
24797
25645
|
createDefaultOnDenied,
|
|
24798
25646
|
createQuoteCache,
|
|
@@ -24806,11 +25654,15 @@ export {
|
|
|
24806
25654
|
formatUsdCents,
|
|
24807
25655
|
generateUCPSigningKey,
|
|
24808
25656
|
getIdentityStatus,
|
|
25657
|
+
hasAgentIdentityHeader,
|
|
24809
25658
|
hasMppxHeader,
|
|
24810
25659
|
hasPaymentHeader,
|
|
24811
25660
|
hasX402Header,
|
|
24812
25661
|
hashOperatorToken,
|
|
25662
|
+
isAitShape,
|
|
25663
|
+
isEvmNetwork,
|
|
24813
25664
|
isFixableDenial,
|
|
25665
|
+
isSolanaNetwork,
|
|
24814
25666
|
loadSolanaFeePayer,
|
|
24815
25667
|
loadUCPSigningKeyFromEnv,
|
|
24816
25668
|
makeMppxComposeHook,
|
|
@@ -24820,9 +25672,11 @@ export {
|
|
|
24820
25672
|
runGateWithEnforcement,
|
|
24821
25673
|
shippingCountryAllowed,
|
|
24822
25674
|
shippingStateAllowed,
|
|
25675
|
+
signMessage,
|
|
24823
25676
|
signUCPProfile,
|
|
24824
25677
|
stripeSptPaymentHandler,
|
|
24825
25678
|
ucpA2AExtension,
|
|
25679
|
+
validateAitPayload,
|
|
24826
25680
|
validateShippingAgainstPolicy,
|
|
24827
25681
|
validationEnvelope,
|
|
24828
25682
|
validationResponseExpress,
|
|
@@ -24831,6 +25685,9 @@ export {
|
|
|
24831
25685
|
validationResponseNextjs,
|
|
24832
25686
|
validationResponseWeb,
|
|
24833
25687
|
verificationAgentInstructions,
|
|
25688
|
+
verifyAit,
|
|
25689
|
+
verifyAitRequest,
|
|
25690
|
+
verifyMessageSignature,
|
|
24834
25691
|
verifyUCPProfile,
|
|
24835
25692
|
x402PaymentHandler
|
|
24836
25693
|
};
|