@agent-score/commerce 1.0.2 → 1.1.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 +29 -2
- package/dist/challenge/index.js +58 -0
- package/dist/challenge/index.js.map +1 -1
- package/dist/challenge/index.mjs +65 -0
- package/dist/challenge/index.mjs.map +1 -1
- package/dist/core.d.mts +28 -1
- package/dist/core.d.ts +28 -1
- package/dist/core.js +237 -157
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +246 -157
- package/dist/core.mjs.map +1 -1
- package/dist/discovery/index.js.map +1 -1
- package/dist/discovery/index.mjs.map +1 -1
- package/dist/identity/express.d.mts +18 -2
- package/dist/identity/express.d.ts +18 -2
- package/dist/identity/express.js +227 -164
- package/dist/identity/express.js.map +1 -1
- package/dist/identity/express.mjs +232 -164
- package/dist/identity/express.mjs.map +1 -1
- package/dist/identity/fastify.d.mts +17 -2
- package/dist/identity/fastify.d.ts +17 -2
- package/dist/identity/fastify.js +227 -164
- package/dist/identity/fastify.js.map +1 -1
- package/dist/identity/fastify.mjs +232 -164
- package/dist/identity/fastify.mjs.map +1 -1
- package/dist/identity/hono.d.mts +22 -2
- package/dist/identity/hono.d.ts +22 -2
- package/dist/identity/hono.js +227 -164
- package/dist/identity/hono.js.map +1 -1
- package/dist/identity/hono.mjs +232 -164
- package/dist/identity/hono.mjs.map +1 -1
- package/dist/identity/nextjs.d.mts +8 -1
- package/dist/identity/nextjs.d.ts +8 -1
- package/dist/identity/nextjs.js +213 -166
- package/dist/identity/nextjs.js.map +1 -1
- package/dist/identity/nextjs.mjs +220 -166
- package/dist/identity/nextjs.mjs.map +1 -1
- package/dist/identity/web.d.mts +15 -1
- package/dist/identity/web.d.ts +15 -1
- package/dist/identity/web.js +213 -166
- package/dist/identity/web.js.map +1 -1
- package/dist/identity/web.mjs +220 -166
- package/dist/identity/web.mjs.map +1 -1
- package/dist/index.js +120 -101
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +127 -101
- package/dist/index.mjs.map +1 -1
- package/dist/payment/index.js.map +1 -1
- package/dist/payment/index.mjs.map +1 -1
- package/dist/stripe-multichain/index.js.map +1 -1
- package/dist/stripe-multichain/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/identity/nextjs.js
CHANGED
|
@@ -126,10 +126,10 @@ var WALLET_NOT_TRUSTED_INSTRUCTIONS = JSON.stringify({
|
|
|
126
126
|
var PAYMENT_REQUIRED_INSTRUCTIONS = JSON.stringify({
|
|
127
127
|
action: "contact_merchant",
|
|
128
128
|
steps: [
|
|
129
|
-
"The merchant's AgentScore
|
|
130
|
-
"Contact the merchant (their support channel \u2014 typically listed in /llms.txt or the OpenAPI servers metadata)
|
|
129
|
+
"The merchant's AgentScore account does not have the assess endpoint enabled, so agent identity cannot be evaluated. This is a merchant-side configuration gap \u2014 there is no agent-side recovery.",
|
|
130
|
+
"Contact the merchant (their support channel \u2014 typically listed in /llms.txt or the OpenAPI servers metadata) so they can resolve the configuration on their side."
|
|
131
131
|
],
|
|
132
|
-
user_message: "This merchant's identity gate is misconfigured
|
|
132
|
+
user_message: "This merchant's identity gate is misconfigured. Contact the merchant \u2014 there's nothing to fix on the agent side."
|
|
133
133
|
});
|
|
134
134
|
var IDENTITY_VERIFICATION_REQUIRED_FALLBACK_INSTRUCTIONS = JSON.stringify({
|
|
135
135
|
action: "deliver_verify_url_and_poll",
|
|
@@ -140,6 +140,24 @@ var IDENTITY_VERIFICATION_REQUIRED_FALLBACK_INSTRUCTIONS = JSON.stringify({
|
|
|
140
140
|
],
|
|
141
141
|
user_message: "Identity verification is required. Visit verify_url, then poll poll_url for the operator token and retry."
|
|
142
142
|
});
|
|
143
|
+
var API_ERROR_INSTRUCTIONS = JSON.stringify({
|
|
144
|
+
action: "retry_with_backoff",
|
|
145
|
+
steps: [
|
|
146
|
+
"Verification is temporarily unavailable. Retry the request after 5\u201330 seconds with exponential backoff.",
|
|
147
|
+
"This is NOT a compliance denial \u2014 the user does not need to re-verify their identity. Send the same identity headers (X-Wallet-Address or X-Operator-Token) on retry.",
|
|
148
|
+
"If the request continues to fail after 3+ retries (~60 seconds total), surface the error to the user with the merchant's support contact."
|
|
149
|
+
],
|
|
150
|
+
user_message: "Verification is temporarily unavailable. Please try again in a moment \u2014 this is a transient issue, not a problem with your account."
|
|
151
|
+
});
|
|
152
|
+
var QUOTA_EXCEEDED_INSTRUCTIONS = JSON.stringify({
|
|
153
|
+
action: "contact_merchant",
|
|
154
|
+
steps: [
|
|
155
|
+
"AgentScore identity verification is unavailable for this merchant. This is a merchant-side issue and is NOT recoverable via retry.",
|
|
156
|
+
"Do not retry: the same 503 will be returned until the merchant resolves the issue on their side.",
|
|
157
|
+
"Surface to the user with the merchant's support contact. The merchant (not the agent) needs to act."
|
|
158
|
+
],
|
|
159
|
+
user_message: "This merchant's identity verification is temporarily unavailable. Try again later, or contact the merchant directly."
|
|
160
|
+
});
|
|
143
161
|
var TOKEN_EXPIRED_FALLBACK_INSTRUCTIONS = JSON.stringify({
|
|
144
162
|
action: "deliver_verify_url_and_poll",
|
|
145
163
|
steps: [
|
|
@@ -150,6 +168,7 @@ var TOKEN_EXPIRED_FALLBACK_INSTRUCTIONS = JSON.stringify({
|
|
|
150
168
|
user_message: "Operator token is expired or revoked. A new verification session has been minted \u2014 visit verify_url to refresh."
|
|
151
169
|
});
|
|
152
170
|
var DEFAULT_AGENT_INSTRUCTIONS = {
|
|
171
|
+
api_error: API_ERROR_INSTRUCTIONS,
|
|
153
172
|
wallet_not_trusted: WALLET_NOT_TRUSTED_INSTRUCTIONS,
|
|
154
173
|
payment_required: PAYMENT_REQUIRED_INSTRUCTIONS,
|
|
155
174
|
identity_verification_required: IDENTITY_VERIFICATION_REQUIRED_FALLBACK_INSTRUCTIONS,
|
|
@@ -160,7 +179,7 @@ var DEFAULT_MESSAGES = {
|
|
|
160
179
|
identity_verification_required: "Identity verification is required to access this resource. Visit verify_url to complete KYC.",
|
|
161
180
|
wallet_not_trusted: "The wallet does not meet the merchant compliance policy.",
|
|
162
181
|
api_error: "AgentScore is unreachable. This is transient \u2014 retry in a few seconds.",
|
|
163
|
-
payment_required: "
|
|
182
|
+
payment_required: "Assess endpoint not enabled for this merchant. Contact support.",
|
|
164
183
|
wallet_signer_mismatch: "Payment signer does not match the wallet claimed via X-Wallet-Address. The signer and the claimed wallet must both resolve to the same AgentScore operator.",
|
|
165
184
|
wallet_auth_requires_wallet_signing: "X-Wallet-Address was sent with a rail that has no wallet signature (Stripe SPT / card). Switch to X-Operator-Token, or use a wallet-signing rail (Tempo MPP, x402).",
|
|
166
185
|
token_expired: "The operator token is expired or revoked. A fresh verification session has been minted \u2014 visit verify_url to mint a new token.",
|
|
@@ -199,9 +218,6 @@ function denialReasonToBody(reason) {
|
|
|
199
218
|
if (reason.expected_signer) body.expected_signer = reason.expected_signer;
|
|
200
219
|
if (reason.actual_signer) body.actual_signer = reason.actual_signer;
|
|
201
220
|
if (reason.linked_wallets && reason.linked_wallets.length > 0) body.linked_wallets = reason.linked_wallets;
|
|
202
|
-
if (reason.code === "api_error" && !(reason.extra && reason.extra.next_steps)) {
|
|
203
|
-
body.next_steps = { action: "retry", retry_after_seconds: 5 };
|
|
204
|
-
}
|
|
205
221
|
if (reason.extra) {
|
|
206
222
|
for (const [key, value] of Object.entries(reason.extra)) {
|
|
207
223
|
if (RESERVED_FIELDS.has(key)) {
|
|
@@ -214,6 +230,9 @@ function denialReasonToBody(reason) {
|
|
|
214
230
|
return body;
|
|
215
231
|
}
|
|
216
232
|
|
|
233
|
+
// src/core.ts
|
|
234
|
+
var import_sdk = require("@agent-score/sdk");
|
|
235
|
+
|
|
217
236
|
// src/address.ts
|
|
218
237
|
var SOLANA_BASE58_RE = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
219
238
|
var isSolanaAddress = (address) => SOLANA_BASE58_RE.test(address) && !address.startsWith("0x");
|
|
@@ -342,9 +361,23 @@ function createAgentScoreCore(options) {
|
|
|
342
361
|
} = options;
|
|
343
362
|
const baseUrl = stripTrailingSlashes(rawBaseUrl);
|
|
344
363
|
const agentMemoryHint = buildAgentMemoryHint();
|
|
345
|
-
const defaultUa = `@agent-score/commerce@${"1.0
|
|
364
|
+
const defaultUa = `@agent-score/commerce@${"1.1.0"}`;
|
|
346
365
|
const userAgentHeader = userAgent ? `${userAgent} (${defaultUa})` : defaultUa;
|
|
347
|
-
const
|
|
366
|
+
const sdk = new import_sdk.AgentScore({ apiKey, baseUrl, userAgent: userAgentHeader });
|
|
367
|
+
const sessionSdkCache = /* @__PURE__ */ new Map();
|
|
368
|
+
function getSessionSdk(sessionApiKey, sessionBaseUrl) {
|
|
369
|
+
const key = `${sessionApiKey}|${sessionBaseUrl ?? ""}`;
|
|
370
|
+
let s = sessionSdkCache.get(key);
|
|
371
|
+
if (!s) {
|
|
372
|
+
s = new import_sdk.AgentScore({
|
|
373
|
+
apiKey: sessionApiKey,
|
|
374
|
+
baseUrl: sessionBaseUrl ?? baseUrl,
|
|
375
|
+
userAgent: userAgentHeader
|
|
376
|
+
});
|
|
377
|
+
sessionSdkCache.set(key, s);
|
|
378
|
+
}
|
|
379
|
+
return s;
|
|
380
|
+
}
|
|
348
381
|
const cache = new TTLCache(cacheSeconds * 1e3);
|
|
349
382
|
async function tryMintSessionDenial(ctx) {
|
|
350
383
|
if (!createSessionOnMissing) return void 0;
|
|
@@ -361,20 +394,11 @@ function createAgentScoreCore(options) {
|
|
|
361
394
|
console.warn("[gate] createSessionOnMissing.getSessionOptions hook failed:", err instanceof Error ? err.message : err);
|
|
362
395
|
}
|
|
363
396
|
}
|
|
364
|
-
const
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
"X-API-Key": createSessionOnMissing.apiKey,
|
|
369
|
-
"Content-Type": "application/json",
|
|
370
|
-
Accept: "application/json",
|
|
371
|
-
"User-Agent": userAgentHeader
|
|
372
|
-
},
|
|
373
|
-
body: JSON.stringify(sessionBody),
|
|
374
|
-
signal: AbortSignal.timeout(API_TIMEOUT_MS)
|
|
397
|
+
const sessionSdk = getSessionSdk(createSessionOnMissing.apiKey, createSessionOnMissing.baseUrl);
|
|
398
|
+
const data = await sessionSdk.createSession({
|
|
399
|
+
...sessionBody.context !== void 0 ? { context: sessionBody.context } : {},
|
|
400
|
+
...sessionBody.product_name !== void 0 ? { product_name: sessionBody.product_name } : {}
|
|
375
401
|
});
|
|
376
|
-
if (!sessionRes.ok) return void 0;
|
|
377
|
-
const data = await sessionRes.json();
|
|
378
402
|
if (typeof data.session_id !== "string" || typeof data.poll_secret !== "string" || typeof data.verify_url !== "string") {
|
|
379
403
|
console.warn("[gate] /v1/sessions returned 200 without required fields \u2014 falling back to bare denial");
|
|
380
404
|
return void 0;
|
|
@@ -438,7 +462,13 @@ function createAgentScoreCore(options) {
|
|
|
438
462
|
const cached = cache.get(cacheKey);
|
|
439
463
|
if (cached) {
|
|
440
464
|
if (cached.allow) {
|
|
441
|
-
|
|
465
|
+
const cachedRaw = cached.raw;
|
|
466
|
+
const cachedQuota = cachedRaw?.quota;
|
|
467
|
+
return {
|
|
468
|
+
kind: "allow",
|
|
469
|
+
data: cachedRaw,
|
|
470
|
+
...cachedQuota !== void 0 && { quota: cachedQuota }
|
|
471
|
+
};
|
|
442
472
|
}
|
|
443
473
|
if (isFixableDenial(cached.reasons)) {
|
|
444
474
|
const sessionReason = await tryMintSessionDenial(ctx);
|
|
@@ -455,130 +485,119 @@ function createAgentScoreCore(options) {
|
|
|
455
485
|
}
|
|
456
486
|
};
|
|
457
487
|
}
|
|
488
|
+
const policy = {};
|
|
489
|
+
if (requireKyc != null) policy.require_kyc = requireKyc;
|
|
490
|
+
if (requireSanctionsClear != null) policy.require_sanctions_clear = requireSanctionsClear;
|
|
491
|
+
if (minAge != null) policy.min_age = minAge;
|
|
492
|
+
if (blockedJurisdictions != null) policy.blocked_jurisdictions = blockedJurisdictions;
|
|
493
|
+
if (allowedJurisdictions != null) policy.allowed_jurisdictions = allowedJurisdictions;
|
|
494
|
+
let data;
|
|
458
495
|
try {
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
if (
|
|
467
|
-
if (blockedJurisdictions != null) policy.blocked_jurisdictions = blockedJurisdictions;
|
|
468
|
-
if (allowedJurisdictions != null) policy.allowed_jurisdictions = allowedJurisdictions;
|
|
469
|
-
if (Object.keys(policy).length > 0) body.policy = policy;
|
|
470
|
-
const response = await fetch(`${baseUrl}/v1/assess`, {
|
|
471
|
-
method: "POST",
|
|
472
|
-
headers: {
|
|
473
|
-
"X-API-Key": apiKey,
|
|
474
|
-
"Content-Type": "application/json",
|
|
475
|
-
Accept: "application/json",
|
|
476
|
-
"User-Agent": userAgentHeader
|
|
477
|
-
},
|
|
478
|
-
body: JSON.stringify(body),
|
|
479
|
-
signal: AbortSignal.timeout(API_TIMEOUT_MS)
|
|
480
|
-
});
|
|
481
|
-
if (response.status === 402) {
|
|
496
|
+
const opts = {
|
|
497
|
+
chain: gateChain,
|
|
498
|
+
...Object.keys(policy).length > 0 ? { policy } : {}
|
|
499
|
+
};
|
|
500
|
+
const result = identity.address ? await sdk.assess(identity.address, { ...opts, operatorToken: identity.operatorToken }) : await sdk.assess(null, { ...opts, operatorToken: identity.operatorToken });
|
|
501
|
+
data = result;
|
|
502
|
+
} catch (err) {
|
|
503
|
+
if (err instanceof import_sdk.PaymentRequiredError) {
|
|
482
504
|
if (failOpen) return { kind: "allow" };
|
|
483
505
|
return { kind: "deny", reason: { code: "payment_required" } };
|
|
484
506
|
}
|
|
485
|
-
if (
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
...typeof errData.poll_secret === "string" ? { poll_secret: errData.poll_secret } : {},
|
|
498
|
-
...typeof errData.poll_url === "string" ? { poll_url: errData.poll_url } : {},
|
|
499
|
-
...errData.next_steps ? { agent_instructions: JSON.stringify(errData.next_steps) } : {},
|
|
500
|
-
...errData.agent_memory ? { agent_memory: errData.agent_memory } : {}
|
|
501
|
-
}
|
|
502
|
-
};
|
|
507
|
+
if (err instanceof import_sdk.TokenExpiredError) {
|
|
508
|
+
return {
|
|
509
|
+
kind: "deny",
|
|
510
|
+
reason: {
|
|
511
|
+
code: "token_expired",
|
|
512
|
+
data: err.details,
|
|
513
|
+
...err.verifyUrl ? { verify_url: err.verifyUrl } : {},
|
|
514
|
+
...err.sessionId ? { session_id: err.sessionId } : {},
|
|
515
|
+
...err.pollSecret ? { poll_secret: err.pollSecret } : {},
|
|
516
|
+
...err.pollUrl ? { poll_url: err.pollUrl } : {},
|
|
517
|
+
...err.nextSteps ? { agent_instructions: JSON.stringify(err.nextSteps) } : {},
|
|
518
|
+
...err.agentMemory ? { agent_memory: err.agentMemory } : {}
|
|
503
519
|
}
|
|
504
|
-
|
|
505
|
-
return {
|
|
506
|
-
kind: "deny",
|
|
507
|
-
reason: {
|
|
508
|
-
code: "invalid_credential",
|
|
509
|
-
agent_instructions: INVALID_CREDENTIAL_INSTRUCTIONS,
|
|
510
|
-
agent_memory: agentMemoryHint
|
|
511
|
-
}
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
if (code) {
|
|
515
|
-
console.warn(`[gate] /v1/assess returned 401 ${code} \u2014 no specific handler, surfacing as api_error.`);
|
|
516
|
-
}
|
|
517
|
-
} catch (err) {
|
|
518
|
-
console.warn("[gate] /v1/assess 401 body parse failed:", err instanceof Error ? err.message : err);
|
|
519
|
-
}
|
|
520
|
+
};
|
|
520
521
|
}
|
|
521
|
-
if (
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
);
|
|
522
|
+
if (err instanceof import_sdk.InvalidCredentialError) {
|
|
523
|
+
return {
|
|
524
|
+
kind: "deny",
|
|
525
|
+
reason: {
|
|
526
|
+
code: "invalid_credential",
|
|
527
|
+
agent_instructions: INVALID_CREDENTIAL_INSTRUCTIONS,
|
|
528
|
+
agent_memory: agentMemoryHint
|
|
529
529
|
}
|
|
530
|
-
}
|
|
531
|
-
}
|
|
530
|
+
};
|
|
532
531
|
}
|
|
533
|
-
if (
|
|
534
|
-
|
|
532
|
+
if (err instanceof import_sdk.QuotaExceededError) {
|
|
533
|
+
console.warn("[gate] /v1/assess returned 429 quota_exceeded");
|
|
534
|
+
if (failOpen) return { kind: "allow", degraded: true, infraReason: "quota_exceeded" };
|
|
535
|
+
return {
|
|
536
|
+
kind: "deny",
|
|
537
|
+
reason: { code: "api_error", agent_instructions: QUOTA_EXCEEDED_INSTRUCTIONS }
|
|
538
|
+
};
|
|
535
539
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
cache.set(cacheKey, { allow, decision: decision ?? void 0, reasons: decisionReasons, raw: data });
|
|
541
|
-
if (allow) {
|
|
542
|
-
return { kind: "allow", data };
|
|
540
|
+
if (err instanceof import_sdk.TimeoutError) {
|
|
541
|
+
console.warn("[gate] /v1/assess timed out:", err.message);
|
|
542
|
+
if (failOpen) return { kind: "allow", degraded: true, infraReason: "network_timeout" };
|
|
543
|
+
return { kind: "deny", reason: { code: "api_error" } };
|
|
543
544
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
545
|
+
const status = err?.status;
|
|
546
|
+
const errName = err instanceof Error ? err.name : "";
|
|
547
|
+
if (status === 429) {
|
|
548
|
+
console.warn("[gate] /v1/assess returned 429 (untyped \u2014 defensive)");
|
|
549
|
+
if (failOpen) return { kind: "allow", degraded: true, infraReason: "quota_exceeded" };
|
|
550
|
+
return {
|
|
551
|
+
kind: "deny",
|
|
552
|
+
reason: { code: "api_error", agent_instructions: QUOTA_EXCEEDED_INSTRUCTIONS }
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
if (errName === "TimeoutError" || errName === "AbortError") {
|
|
556
|
+
console.warn("[gate] /v1/assess timed out (by Error.name):", err instanceof Error ? err.message : err);
|
|
557
|
+
if (failOpen) return { kind: "allow", degraded: true, infraReason: "network_timeout" };
|
|
558
|
+
return { kind: "deny", reason: { code: "api_error" } };
|
|
547
559
|
}
|
|
560
|
+
const errCode = err?.code;
|
|
561
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
562
|
+
const detail = errCode ? `${errCode}: ${msg}` : msg;
|
|
563
|
+
console.warn(`[gate] /v1/assess call failed \u2014 surfacing as api_error: ${detail}`);
|
|
564
|
+
if (failOpen) return { kind: "allow", degraded: true, infraReason: "api_error" };
|
|
565
|
+
return { kind: "deny", reason: { code: "api_error" } };
|
|
566
|
+
}
|
|
567
|
+
const decision = data.decision;
|
|
568
|
+
const decisionReasons = data.decision_reasons ?? [];
|
|
569
|
+
const allow = decision === "allow" || decision == null;
|
|
570
|
+
cache.set(cacheKey, { allow, decision: decision ?? void 0, reasons: decisionReasons, raw: data });
|
|
571
|
+
if (allow) {
|
|
572
|
+
const quota = data.quota;
|
|
548
573
|
return {
|
|
549
|
-
kind: "
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
decision: decision ?? void 0,
|
|
553
|
-
reasons: decisionReasons,
|
|
554
|
-
verify_url: data.verify_url,
|
|
555
|
-
data
|
|
556
|
-
}
|
|
574
|
+
kind: "allow",
|
|
575
|
+
data,
|
|
576
|
+
...quota !== void 0 && { quota }
|
|
557
577
|
};
|
|
558
|
-
} catch (err) {
|
|
559
|
-
console.warn("[gate] /v1/assess call failed \u2014 surfacing as api_error:", err instanceof Error ? err.message : err);
|
|
560
|
-
if (failOpen) return { kind: "allow" };
|
|
561
|
-
return { kind: "deny", reason: { code: "api_error" } };
|
|
562
578
|
}
|
|
579
|
+
if (isFixableDenial(decisionReasons)) {
|
|
580
|
+
const sessionReason = await tryMintSessionDenial(ctx);
|
|
581
|
+
if (sessionReason) return { kind: "deny", reason: sessionReason };
|
|
582
|
+
}
|
|
583
|
+
return {
|
|
584
|
+
kind: "deny",
|
|
585
|
+
reason: {
|
|
586
|
+
code: "wallet_not_trusted",
|
|
587
|
+
decision: decision ?? void 0,
|
|
588
|
+
reasons: decisionReasons,
|
|
589
|
+
verify_url: data.verify_url,
|
|
590
|
+
data
|
|
591
|
+
}
|
|
592
|
+
};
|
|
563
593
|
}
|
|
564
594
|
async function captureWallet(options2) {
|
|
565
595
|
try {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
network: options2.network
|
|
570
|
-
|
|
571
|
-
if (options2.idempotencyKey) body.idempotency_key = options2.idempotencyKey;
|
|
572
|
-
await fetch(`${baseUrl}/v1/credentials/wallets`, {
|
|
573
|
-
method: "POST",
|
|
574
|
-
headers: {
|
|
575
|
-
"X-API-Key": apiKey,
|
|
576
|
-
"Content-Type": "application/json",
|
|
577
|
-
Accept: "application/json",
|
|
578
|
-
"User-Agent": userAgentHeader
|
|
579
|
-
},
|
|
580
|
-
body: JSON.stringify(body),
|
|
581
|
-
signal: AbortSignal.timeout(API_TIMEOUT_MS)
|
|
596
|
+
await sdk.associateWallet({
|
|
597
|
+
operatorToken: options2.operatorToken,
|
|
598
|
+
walletAddress: options2.walletAddress,
|
|
599
|
+
network: options2.network,
|
|
600
|
+
...options2.idempotencyKey ? { idempotencyKey: options2.idempotencyKey } : {}
|
|
582
601
|
});
|
|
583
602
|
} catch (err) {
|
|
584
603
|
console.warn("[agentscore-commerce] captureWallet failed:", err instanceof Error ? err.message : err);
|
|
@@ -603,19 +622,7 @@ function createAgentScoreCore(options) {
|
|
|
603
622
|
return { ok: true, ...extractFromCached(resolveCached.raw) };
|
|
604
623
|
}
|
|
605
624
|
try {
|
|
606
|
-
const
|
|
607
|
-
method: "POST",
|
|
608
|
-
headers: {
|
|
609
|
-
"X-API-Key": apiKey,
|
|
610
|
-
"Content-Type": "application/json",
|
|
611
|
-
Accept: "application/json",
|
|
612
|
-
"User-Agent": userAgentHeader
|
|
613
|
-
},
|
|
614
|
-
body: JSON.stringify({ address: walletAddress }),
|
|
615
|
-
signal: AbortSignal.timeout(API_TIMEOUT_MS)
|
|
616
|
-
});
|
|
617
|
-
if (!response.ok) return { ok: false };
|
|
618
|
-
const data = await response.json();
|
|
625
|
+
const data = await sdk.assess(walletAddress);
|
|
619
626
|
cache.set(`resolve:${wallet}`, { allow: true, raw: data });
|
|
620
627
|
return { ok: true, ...extractFromCached(data) };
|
|
621
628
|
} catch (err) {
|
|
@@ -624,28 +631,37 @@ function createAgentScoreCore(options) {
|
|
|
624
631
|
}
|
|
625
632
|
}
|
|
626
633
|
function reportSignerEvent(kind) {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
signal: AbortSignal.timeout(API_TIMEOUT_MS)
|
|
638
|
-
});
|
|
639
|
-
if (pending && typeof pending.catch === "function") {
|
|
640
|
-
pending.catch((err) => {
|
|
641
|
-
console.warn("[agentscore-commerce] signer-match telemetry failed:", err instanceof Error ? err.message : err);
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
} catch {
|
|
634
|
+
void sdk.telemetrySignerMatch({ kind });
|
|
635
|
+
}
|
|
636
|
+
function projectSignerMatch(sm, claimedNorm, signerNorm) {
|
|
637
|
+
const kind = sm.kind;
|
|
638
|
+
if (kind === "pass") {
|
|
639
|
+
return {
|
|
640
|
+
kind: "pass",
|
|
641
|
+
claimedOperator: sm.claimed_operator ?? null,
|
|
642
|
+
signerOperator: sm.signer_operator ?? null
|
|
643
|
+
};
|
|
645
644
|
}
|
|
645
|
+
if (kind === "wallet_auth_requires_wallet_signing") {
|
|
646
|
+
return {
|
|
647
|
+
kind: "wallet_auth_requires_wallet_signing",
|
|
648
|
+
claimedWallet: sm.claimed_wallet ?? claimedNorm,
|
|
649
|
+
agentInstructions: sm.agent_instructions ?? WALLET_AUTH_REQUIRES_WALLET_SIGNING_INSTRUCTIONS
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
const linked = sm.linked_wallets;
|
|
653
|
+
return {
|
|
654
|
+
kind: "wallet_signer_mismatch",
|
|
655
|
+
claimedOperator: sm.claimed_operator ?? null,
|
|
656
|
+
actualSignerOperator: sm.signer_operator ?? null,
|
|
657
|
+
expectedSigner: sm.expected_signer ?? claimedNorm,
|
|
658
|
+
actualSigner: sm.actual_signer ?? signerNorm,
|
|
659
|
+
linkedWallets: Array.isArray(linked) ? linked.filter((w) => typeof w === "string") : [],
|
|
660
|
+
agentInstructions: sm.agent_instructions ?? WALLET_SIGNER_MISMATCH_INSTRUCTIONS
|
|
661
|
+
};
|
|
646
662
|
}
|
|
647
663
|
async function verifyWalletSignerMatch(options2) {
|
|
648
|
-
const { claimedWallet, signer } = options2;
|
|
664
|
+
const { claimedWallet, signer, network } = options2;
|
|
649
665
|
if (!signer) {
|
|
650
666
|
reportSignerEvent("wallet_auth_requires_wallet_signing");
|
|
651
667
|
return {
|
|
@@ -660,6 +676,35 @@ function createAgentScoreCore(options) {
|
|
|
660
676
|
reportSignerEvent("pass");
|
|
661
677
|
return { kind: "pass", claimedOperator: null, signerOperator: null };
|
|
662
678
|
}
|
|
679
|
+
const cachedEntry = cache.get(claimedNorm);
|
|
680
|
+
const cachedMatch = cachedEntry?.signerMatchBySigner?.get(signerNorm);
|
|
681
|
+
if (cachedMatch) {
|
|
682
|
+
return projectSignerMatch(cachedMatch, claimedNorm, signerNorm);
|
|
683
|
+
}
|
|
684
|
+
const inferredNetwork = network ?? (signerNorm.startsWith("0x") ? "evm" : "solana");
|
|
685
|
+
let assessResponse;
|
|
686
|
+
try {
|
|
687
|
+
assessResponse = await sdk.assess(claimedNorm, {
|
|
688
|
+
resolveSigner: { address: signerNorm, network: inferredNetwork }
|
|
689
|
+
});
|
|
690
|
+
} catch (err) {
|
|
691
|
+
console.warn("[gate] verifyWalletSignerMatch assess failed:", err instanceof Error ? err.message : err);
|
|
692
|
+
reportSignerEvent("api_error");
|
|
693
|
+
return { kind: "api_error", claimedWallet: claimedNorm };
|
|
694
|
+
}
|
|
695
|
+
const signerMatch = assessResponse.signer_match;
|
|
696
|
+
if (signerMatch && typeof signerMatch === "object") {
|
|
697
|
+
if (cachedEntry) {
|
|
698
|
+
const map = cachedEntry.signerMatchBySigner ?? /* @__PURE__ */ new Map();
|
|
699
|
+
map.set(signerNorm, signerMatch);
|
|
700
|
+
cachedEntry.signerMatchBySigner = map;
|
|
701
|
+
} else {
|
|
702
|
+
const entry = { allow: true, raw: assessResponse };
|
|
703
|
+
entry.signerMatchBySigner = /* @__PURE__ */ new Map([[signerNorm, signerMatch]]);
|
|
704
|
+
cache.set(claimedNorm, entry);
|
|
705
|
+
}
|
|
706
|
+
return projectSignerMatch(signerMatch, claimedNorm, signerNorm);
|
|
707
|
+
}
|
|
663
708
|
const [claimedResolve, signerResolve] = await Promise.all([
|
|
664
709
|
resolveWalletToOperator(claimedNorm),
|
|
665
710
|
resolveWalletToOperator(signerNorm)
|
|
@@ -681,8 +726,6 @@ function createAgentScoreCore(options) {
|
|
|
681
726
|
actualSignerOperator: signerOperator,
|
|
682
727
|
expectedSigner: claimedNorm,
|
|
683
728
|
actualSigner: signerNorm,
|
|
684
|
-
// Populated from /v1/assess.linked_wallets on the claimed wallet — the full set of
|
|
685
|
-
// wallets the agent CAN sign with to satisfy the claim (same-operator rule).
|
|
686
729
|
linkedWallets: claimedResolve.linkedWallets,
|
|
687
730
|
agentInstructions: WALLET_SIGNER_MISMATCH_INSTRUCTIONS
|
|
688
731
|
};
|
|
@@ -784,7 +827,9 @@ function createAgentScoreGate(options) {
|
|
|
784
827
|
allowed: true,
|
|
785
828
|
data: outcome.data,
|
|
786
829
|
captureWallet,
|
|
787
|
-
verifyWalletSignerMatch: verifyWalletSignerMatchBound
|
|
830
|
+
verifyWalletSignerMatch: verifyWalletSignerMatchBound,
|
|
831
|
+
...outcome.degraded ? { degraded: true, infraReason: outcome.infraReason } : {},
|
|
832
|
+
...outcome.quota ? { quota: outcome.quota } : {}
|
|
788
833
|
};
|
|
789
834
|
}
|
|
790
835
|
const response = await onDenied(req, outcome.reason);
|
|
@@ -803,7 +848,9 @@ function withAgentScoreGate(options, handler) {
|
|
|
803
848
|
{
|
|
804
849
|
data: result.data,
|
|
805
850
|
captureWallet: result.captureWallet,
|
|
806
|
-
verifyWalletSignerMatch: result.verifyWalletSignerMatch
|
|
851
|
+
verifyWalletSignerMatch: result.verifyWalletSignerMatch,
|
|
852
|
+
...result.degraded ? { degraded: true, infraReason: result.infraReason } : {},
|
|
853
|
+
...result.quota ? { quota: result.quota } : {}
|
|
807
854
|
},
|
|
808
855
|
ctx
|
|
809
856
|
);
|