@agent-score/commerce 1.0.3 → 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.
Files changed (52) hide show
  1. package/README.md +27 -0
  2. package/dist/challenge/index.js +58 -0
  3. package/dist/challenge/index.js.map +1 -1
  4. package/dist/challenge/index.mjs +65 -0
  5. package/dist/challenge/index.mjs.map +1 -1
  6. package/dist/core.d.mts +28 -1
  7. package/dist/core.d.ts +28 -1
  8. package/dist/core.js +237 -157
  9. package/dist/core.js.map +1 -1
  10. package/dist/core.mjs +246 -157
  11. package/dist/core.mjs.map +1 -1
  12. package/dist/discovery/index.js.map +1 -1
  13. package/dist/discovery/index.mjs.map +1 -1
  14. package/dist/identity/express.d.mts +18 -2
  15. package/dist/identity/express.d.ts +18 -2
  16. package/dist/identity/express.js +227 -164
  17. package/dist/identity/express.js.map +1 -1
  18. package/dist/identity/express.mjs +232 -164
  19. package/dist/identity/express.mjs.map +1 -1
  20. package/dist/identity/fastify.d.mts +17 -2
  21. package/dist/identity/fastify.d.ts +17 -2
  22. package/dist/identity/fastify.js +227 -164
  23. package/dist/identity/fastify.js.map +1 -1
  24. package/dist/identity/fastify.mjs +232 -164
  25. package/dist/identity/fastify.mjs.map +1 -1
  26. package/dist/identity/hono.d.mts +22 -2
  27. package/dist/identity/hono.d.ts +22 -2
  28. package/dist/identity/hono.js +227 -164
  29. package/dist/identity/hono.js.map +1 -1
  30. package/dist/identity/hono.mjs +232 -164
  31. package/dist/identity/hono.mjs.map +1 -1
  32. package/dist/identity/nextjs.d.mts +8 -1
  33. package/dist/identity/nextjs.d.ts +8 -1
  34. package/dist/identity/nextjs.js +213 -166
  35. package/dist/identity/nextjs.js.map +1 -1
  36. package/dist/identity/nextjs.mjs +220 -166
  37. package/dist/identity/nextjs.mjs.map +1 -1
  38. package/dist/identity/web.d.mts +15 -1
  39. package/dist/identity/web.d.ts +15 -1
  40. package/dist/identity/web.js +213 -166
  41. package/dist/identity/web.js.map +1 -1
  42. package/dist/identity/web.mjs +220 -166
  43. package/dist/identity/web.mjs.map +1 -1
  44. package/dist/index.js +120 -101
  45. package/dist/index.js.map +1 -1
  46. package/dist/index.mjs +127 -101
  47. package/dist/index.mjs.map +1 -1
  48. package/dist/payment/index.js.map +1 -1
  49. package/dist/payment/index.mjs.map +1 -1
  50. package/dist/stripe-multichain/index.js.map +1 -1
  51. package/dist/stripe-multichain/index.mjs.map +1 -1
  52. package/package.json +3 -3
@@ -29,6 +29,8 @@ __export(hono_exports, {
29
29
  denialReasonToBody: () => denialReasonToBody,
30
30
  extractPaymentSignerAddress: () => extractPaymentSignerAddress,
31
31
  getAgentScoreData: () => getAgentScoreData,
32
+ getGateDegradedState: () => getGateDegradedState,
33
+ getGateQuotaInfo: () => getGateQuotaInfo,
32
34
  isFixableDenial: () => isFixableDenial,
33
35
  readX402PaymentHeader: () => readX402PaymentHeader,
34
36
  verificationAgentInstructions: () => verificationAgentInstructions,
@@ -128,10 +130,10 @@ var WALLET_NOT_TRUSTED_INSTRUCTIONS = JSON.stringify({
128
130
  var PAYMENT_REQUIRED_INSTRUCTIONS = JSON.stringify({
129
131
  action: "contact_merchant",
130
132
  steps: [
131
- "The merchant's AgentScore tier does not include the assess feature, so agent identity cannot be evaluated. This is a merchant-side configuration gap \u2014 there is no agent-side recovery.",
132
- "Contact the merchant (their support channel \u2014 typically listed in /llms.txt or the OpenAPI servers metadata) and request they upgrade their AgentScore plan."
133
+ "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.",
134
+ "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."
133
135
  ],
134
- user_message: "This merchant's identity gate is misconfigured (AgentScore tier doesn't support assess). Contact the merchant \u2014 there's nothing to fix on the agent side."
136
+ user_message: "This merchant's identity gate is misconfigured. Contact the merchant \u2014 there's nothing to fix on the agent side."
135
137
  });
136
138
  var IDENTITY_VERIFICATION_REQUIRED_FALLBACK_INSTRUCTIONS = JSON.stringify({
137
139
  action: "deliver_verify_url_and_poll",
@@ -142,6 +144,24 @@ var IDENTITY_VERIFICATION_REQUIRED_FALLBACK_INSTRUCTIONS = JSON.stringify({
142
144
  ],
143
145
  user_message: "Identity verification is required. Visit verify_url, then poll poll_url for the operator token and retry."
144
146
  });
147
+ var API_ERROR_INSTRUCTIONS = JSON.stringify({
148
+ action: "retry_with_backoff",
149
+ steps: [
150
+ "Verification is temporarily unavailable. Retry the request after 5\u201330 seconds with exponential backoff.",
151
+ "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.",
152
+ "If the request continues to fail after 3+ retries (~60 seconds total), surface the error to the user with the merchant's support contact."
153
+ ],
154
+ user_message: "Verification is temporarily unavailable. Please try again in a moment \u2014 this is a transient issue, not a problem with your account."
155
+ });
156
+ var QUOTA_EXCEEDED_INSTRUCTIONS = JSON.stringify({
157
+ action: "contact_merchant",
158
+ steps: [
159
+ "AgentScore identity verification is unavailable for this merchant. This is a merchant-side issue and is NOT recoverable via retry.",
160
+ "Do not retry: the same 503 will be returned until the merchant resolves the issue on their side.",
161
+ "Surface to the user with the merchant's support contact. The merchant (not the agent) needs to act."
162
+ ],
163
+ user_message: "This merchant's identity verification is temporarily unavailable. Try again later, or contact the merchant directly."
164
+ });
145
165
  var TOKEN_EXPIRED_FALLBACK_INSTRUCTIONS = JSON.stringify({
146
166
  action: "deliver_verify_url_and_poll",
147
167
  steps: [
@@ -152,6 +172,7 @@ var TOKEN_EXPIRED_FALLBACK_INSTRUCTIONS = JSON.stringify({
152
172
  user_message: "Operator token is expired or revoked. A new verification session has been minted \u2014 visit verify_url to refresh."
153
173
  });
154
174
  var DEFAULT_AGENT_INSTRUCTIONS = {
175
+ api_error: API_ERROR_INSTRUCTIONS,
155
176
  wallet_not_trusted: WALLET_NOT_TRUSTED_INSTRUCTIONS,
156
177
  payment_required: PAYMENT_REQUIRED_INSTRUCTIONS,
157
178
  identity_verification_required: IDENTITY_VERIFICATION_REQUIRED_FALLBACK_INSTRUCTIONS,
@@ -162,7 +183,7 @@ var DEFAULT_MESSAGES = {
162
183
  identity_verification_required: "Identity verification is required to access this resource. Visit verify_url to complete KYC.",
163
184
  wallet_not_trusted: "The wallet does not meet the merchant compliance policy.",
164
185
  api_error: "AgentScore is unreachable. This is transient \u2014 retry in a few seconds.",
165
- payment_required: "AgentScore tier does not support assess. Contact support.",
186
+ payment_required: "Assess endpoint not enabled for this merchant. Contact support.",
166
187
  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.",
167
188
  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).",
168
189
  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.",
@@ -201,9 +222,6 @@ function denialReasonToBody(reason) {
201
222
  if (reason.expected_signer) body.expected_signer = reason.expected_signer;
202
223
  if (reason.actual_signer) body.actual_signer = reason.actual_signer;
203
224
  if (reason.linked_wallets && reason.linked_wallets.length > 0) body.linked_wallets = reason.linked_wallets;
204
- if (reason.code === "api_error" && !(reason.extra && reason.extra.next_steps)) {
205
- body.next_steps = { action: "retry", retry_after_seconds: 5 };
206
- }
207
225
  if (reason.extra) {
208
226
  for (const [key, value] of Object.entries(reason.extra)) {
209
227
  if (RESERVED_FIELDS.has(key)) {
@@ -216,6 +234,9 @@ function denialReasonToBody(reason) {
216
234
  return body;
217
235
  }
218
236
 
237
+ // src/core.ts
238
+ var import_sdk = require("@agent-score/sdk");
239
+
219
240
  // src/address.ts
220
241
  var SOLANA_BASE58_RE = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
221
242
  var isSolanaAddress = (address) => SOLANA_BASE58_RE.test(address) && !address.startsWith("0x");
@@ -344,9 +365,23 @@ function createAgentScoreCore(options) {
344
365
  } = options;
345
366
  const baseUrl = stripTrailingSlashes(rawBaseUrl);
346
367
  const agentMemoryHint = buildAgentMemoryHint();
347
- const defaultUa = `@agent-score/commerce@${"1.0.3"}`;
368
+ const defaultUa = `@agent-score/commerce@${"1.1.0"}`;
348
369
  const userAgentHeader = userAgent ? `${userAgent} (${defaultUa})` : defaultUa;
349
- const API_TIMEOUT_MS = 1e4;
370
+ const sdk = new import_sdk.AgentScore({ apiKey, baseUrl, userAgent: userAgentHeader });
371
+ const sessionSdkCache = /* @__PURE__ */ new Map();
372
+ function getSessionSdk(sessionApiKey, sessionBaseUrl) {
373
+ const key = `${sessionApiKey}|${sessionBaseUrl ?? ""}`;
374
+ let s = sessionSdkCache.get(key);
375
+ if (!s) {
376
+ s = new import_sdk.AgentScore({
377
+ apiKey: sessionApiKey,
378
+ baseUrl: sessionBaseUrl ?? baseUrl,
379
+ userAgent: userAgentHeader
380
+ });
381
+ sessionSdkCache.set(key, s);
382
+ }
383
+ return s;
384
+ }
350
385
  const cache = new TTLCache(cacheSeconds * 1e3);
351
386
  async function tryMintSessionDenial(ctx) {
352
387
  if (!createSessionOnMissing) return void 0;
@@ -363,20 +398,11 @@ function createAgentScoreCore(options) {
363
398
  console.warn("[gate] createSessionOnMissing.getSessionOptions hook failed:", err instanceof Error ? err.message : err);
364
399
  }
365
400
  }
366
- const sessionBaseUrl = stripTrailingSlashes(createSessionOnMissing.baseUrl ?? "https://api.agentscore.sh");
367
- const sessionRes = await fetch(`${sessionBaseUrl}/v1/sessions`, {
368
- method: "POST",
369
- headers: {
370
- "X-API-Key": createSessionOnMissing.apiKey,
371
- "Content-Type": "application/json",
372
- Accept: "application/json",
373
- "User-Agent": userAgentHeader
374
- },
375
- body: JSON.stringify(sessionBody),
376
- signal: AbortSignal.timeout(API_TIMEOUT_MS)
401
+ const sessionSdk = getSessionSdk(createSessionOnMissing.apiKey, createSessionOnMissing.baseUrl);
402
+ const data = await sessionSdk.createSession({
403
+ ...sessionBody.context !== void 0 ? { context: sessionBody.context } : {},
404
+ ...sessionBody.product_name !== void 0 ? { product_name: sessionBody.product_name } : {}
377
405
  });
378
- if (!sessionRes.ok) return void 0;
379
- const data = await sessionRes.json();
380
406
  if (typeof data.session_id !== "string" || typeof data.poll_secret !== "string" || typeof data.verify_url !== "string") {
381
407
  console.warn("[gate] /v1/sessions returned 200 without required fields \u2014 falling back to bare denial");
382
408
  return void 0;
@@ -440,7 +466,13 @@ function createAgentScoreCore(options) {
440
466
  const cached = cache.get(cacheKey);
441
467
  if (cached) {
442
468
  if (cached.allow) {
443
- return { kind: "allow", data: cached.raw };
469
+ const cachedRaw = cached.raw;
470
+ const cachedQuota = cachedRaw?.quota;
471
+ return {
472
+ kind: "allow",
473
+ data: cachedRaw,
474
+ ...cachedQuota !== void 0 && { quota: cachedQuota }
475
+ };
444
476
  }
445
477
  if (isFixableDenial(cached.reasons)) {
446
478
  const sessionReason = await tryMintSessionDenial(ctx);
@@ -457,130 +489,119 @@ function createAgentScoreCore(options) {
457
489
  }
458
490
  };
459
491
  }
492
+ const policy = {};
493
+ if (requireKyc != null) policy.require_kyc = requireKyc;
494
+ if (requireSanctionsClear != null) policy.require_sanctions_clear = requireSanctionsClear;
495
+ if (minAge != null) policy.min_age = minAge;
496
+ if (blockedJurisdictions != null) policy.blocked_jurisdictions = blockedJurisdictions;
497
+ if (allowedJurisdictions != null) policy.allowed_jurisdictions = allowedJurisdictions;
498
+ let data;
460
499
  try {
461
- const body = {};
462
- if (identity.address) body.address = identity.address;
463
- if (identity.operatorToken) body.operator_token = identity.operatorToken;
464
- if (gateChain) body.chain = gateChain;
465
- const policy = {};
466
- if (requireKyc != null) policy.require_kyc = requireKyc;
467
- if (requireSanctionsClear != null) policy.require_sanctions_clear = requireSanctionsClear;
468
- if (minAge != null) policy.min_age = minAge;
469
- if (blockedJurisdictions != null) policy.blocked_jurisdictions = blockedJurisdictions;
470
- if (allowedJurisdictions != null) policy.allowed_jurisdictions = allowedJurisdictions;
471
- if (Object.keys(policy).length > 0) body.policy = policy;
472
- const response = await fetch(`${baseUrl}/v1/assess`, {
473
- method: "POST",
474
- headers: {
475
- "X-API-Key": apiKey,
476
- "Content-Type": "application/json",
477
- Accept: "application/json",
478
- "User-Agent": userAgentHeader
479
- },
480
- body: JSON.stringify(body),
481
- signal: AbortSignal.timeout(API_TIMEOUT_MS)
482
- });
483
- if (response.status === 402) {
500
+ const opts = {
501
+ chain: gateChain,
502
+ ...Object.keys(policy).length > 0 ? { policy } : {}
503
+ };
504
+ const result = identity.address ? await sdk.assess(identity.address, { ...opts, operatorToken: identity.operatorToken }) : await sdk.assess(null, { ...opts, operatorToken: identity.operatorToken });
505
+ data = result;
506
+ } catch (err) {
507
+ if (err instanceof import_sdk.PaymentRequiredError) {
484
508
  if (failOpen) return { kind: "allow" };
485
509
  return { kind: "deny", reason: { code: "payment_required" } };
486
510
  }
487
- if (response.status === 401) {
488
- try {
489
- const errData = await response.clone().json();
490
- const code = errData?.error?.code;
491
- if (code === "token_expired") {
492
- return {
493
- kind: "deny",
494
- reason: {
495
- code,
496
- data: errData,
497
- ...typeof errData.verify_url === "string" ? { verify_url: errData.verify_url } : {},
498
- ...typeof errData.session_id === "string" ? { session_id: errData.session_id } : {},
499
- ...typeof errData.poll_secret === "string" ? { poll_secret: errData.poll_secret } : {},
500
- ...typeof errData.poll_url === "string" ? { poll_url: errData.poll_url } : {},
501
- ...errData.next_steps ? { agent_instructions: JSON.stringify(errData.next_steps) } : {},
502
- ...errData.agent_memory ? { agent_memory: errData.agent_memory } : {}
503
- }
504
- };
505
- }
506
- if (code === "invalid_credential") {
507
- return {
508
- kind: "deny",
509
- reason: {
510
- code: "invalid_credential",
511
- agent_instructions: INVALID_CREDENTIAL_INSTRUCTIONS,
512
- agent_memory: agentMemoryHint
513
- }
514
- };
515
- }
516
- if (code) {
517
- console.warn(`[gate] /v1/assess returned 401 ${code} \u2014 no specific handler, surfacing as api_error.`);
511
+ if (err instanceof import_sdk.TokenExpiredError) {
512
+ return {
513
+ kind: "deny",
514
+ reason: {
515
+ code: "token_expired",
516
+ data: err.details,
517
+ ...err.verifyUrl ? { verify_url: err.verifyUrl } : {},
518
+ ...err.sessionId ? { session_id: err.sessionId } : {},
519
+ ...err.pollSecret ? { poll_secret: err.pollSecret } : {},
520
+ ...err.pollUrl ? { poll_url: err.pollUrl } : {},
521
+ ...err.nextSteps ? { agent_instructions: JSON.stringify(err.nextSteps) } : {},
522
+ ...err.agentMemory ? { agent_memory: err.agentMemory } : {}
518
523
  }
519
- } catch (err) {
520
- console.warn("[gate] /v1/assess 401 body parse failed:", err instanceof Error ? err.message : err);
521
- }
524
+ };
522
525
  }
523
- if (response.status >= 400 && response.status < 500 && response.status !== 402) {
524
- try {
525
- const errData = await response.clone().json();
526
- const code = errData?.error?.code;
527
- if (code && code !== "token_expired" && code !== "invalid_credential") {
528
- console.warn(
529
- `[gate] /v1/assess returned ${response.status} ${code} \u2014 surfacing as api_error. Validate input shape before invoking the gate to avoid this.`
530
- );
526
+ if (err instanceof import_sdk.InvalidCredentialError) {
527
+ return {
528
+ kind: "deny",
529
+ reason: {
530
+ code: "invalid_credential",
531
+ agent_instructions: INVALID_CREDENTIAL_INSTRUCTIONS,
532
+ agent_memory: agentMemoryHint
531
533
  }
532
- } catch {
533
- }
534
+ };
534
535
  }
535
- if (!response.ok) {
536
- throw new Error(`AgentScore API returned ${response.status}`);
536
+ if (err instanceof import_sdk.QuotaExceededError) {
537
+ console.warn("[gate] /v1/assess returned 429 quota_exceeded");
538
+ if (failOpen) return { kind: "allow", degraded: true, infraReason: "quota_exceeded" };
539
+ return {
540
+ kind: "deny",
541
+ reason: { code: "api_error", agent_instructions: QUOTA_EXCEEDED_INSTRUCTIONS }
542
+ };
537
543
  }
538
- const data = await response.json();
539
- const decision = data.decision;
540
- const decisionReasons = data.decision_reasons ?? [];
541
- const allow = decision === "allow" || decision == null;
542
- cache.set(cacheKey, { allow, decision: decision ?? void 0, reasons: decisionReasons, raw: data });
543
- if (allow) {
544
- return { kind: "allow", data };
544
+ if (err instanceof import_sdk.TimeoutError) {
545
+ console.warn("[gate] /v1/assess timed out:", err.message);
546
+ if (failOpen) return { kind: "allow", degraded: true, infraReason: "network_timeout" };
547
+ return { kind: "deny", reason: { code: "api_error" } };
545
548
  }
546
- if (isFixableDenial(decisionReasons)) {
547
- const sessionReason = await tryMintSessionDenial(ctx);
548
- if (sessionReason) return { kind: "deny", reason: sessionReason };
549
+ const status = err?.status;
550
+ const errName = err instanceof Error ? err.name : "";
551
+ if (status === 429) {
552
+ console.warn("[gate] /v1/assess returned 429 (untyped \u2014 defensive)");
553
+ if (failOpen) return { kind: "allow", degraded: true, infraReason: "quota_exceeded" };
554
+ return {
555
+ kind: "deny",
556
+ reason: { code: "api_error", agent_instructions: QUOTA_EXCEEDED_INSTRUCTIONS }
557
+ };
558
+ }
559
+ if (errName === "TimeoutError" || errName === "AbortError") {
560
+ console.warn("[gate] /v1/assess timed out (by Error.name):", err instanceof Error ? err.message : err);
561
+ if (failOpen) return { kind: "allow", degraded: true, infraReason: "network_timeout" };
562
+ return { kind: "deny", reason: { code: "api_error" } };
549
563
  }
564
+ const errCode = err?.code;
565
+ const msg = err instanceof Error ? err.message : String(err);
566
+ const detail = errCode ? `${errCode}: ${msg}` : msg;
567
+ console.warn(`[gate] /v1/assess call failed \u2014 surfacing as api_error: ${detail}`);
568
+ if (failOpen) return { kind: "allow", degraded: true, infraReason: "api_error" };
569
+ return { kind: "deny", reason: { code: "api_error" } };
570
+ }
571
+ const decision = data.decision;
572
+ const decisionReasons = data.decision_reasons ?? [];
573
+ const allow = decision === "allow" || decision == null;
574
+ cache.set(cacheKey, { allow, decision: decision ?? void 0, reasons: decisionReasons, raw: data });
575
+ if (allow) {
576
+ const quota = data.quota;
550
577
  return {
551
- kind: "deny",
552
- reason: {
553
- code: "wallet_not_trusted",
554
- decision: decision ?? void 0,
555
- reasons: decisionReasons,
556
- verify_url: data.verify_url,
557
- data
558
- }
578
+ kind: "allow",
579
+ data,
580
+ ...quota !== void 0 && { quota }
559
581
  };
560
- } catch (err) {
561
- console.warn("[gate] /v1/assess call failed \u2014 surfacing as api_error:", err instanceof Error ? err.message : err);
562
- if (failOpen) return { kind: "allow" };
563
- return { kind: "deny", reason: { code: "api_error" } };
564
582
  }
583
+ if (isFixableDenial(decisionReasons)) {
584
+ const sessionReason = await tryMintSessionDenial(ctx);
585
+ if (sessionReason) return { kind: "deny", reason: sessionReason };
586
+ }
587
+ return {
588
+ kind: "deny",
589
+ reason: {
590
+ code: "wallet_not_trusted",
591
+ decision: decision ?? void 0,
592
+ reasons: decisionReasons,
593
+ verify_url: data.verify_url,
594
+ data
595
+ }
596
+ };
565
597
  }
566
598
  async function captureWallet2(options2) {
567
599
  try {
568
- const body = {
569
- operator_token: options2.operatorToken,
570
- wallet_address: options2.walletAddress,
571
- network: options2.network
572
- };
573
- if (options2.idempotencyKey) body.idempotency_key = options2.idempotencyKey;
574
- await fetch(`${baseUrl}/v1/credentials/wallets`, {
575
- method: "POST",
576
- headers: {
577
- "X-API-Key": apiKey,
578
- "Content-Type": "application/json",
579
- Accept: "application/json",
580
- "User-Agent": userAgentHeader
581
- },
582
- body: JSON.stringify(body),
583
- signal: AbortSignal.timeout(API_TIMEOUT_MS)
600
+ await sdk.associateWallet({
601
+ operatorToken: options2.operatorToken,
602
+ walletAddress: options2.walletAddress,
603
+ network: options2.network,
604
+ ...options2.idempotencyKey ? { idempotencyKey: options2.idempotencyKey } : {}
584
605
  });
585
606
  } catch (err) {
586
607
  console.warn("[agentscore-commerce] captureWallet failed:", err instanceof Error ? err.message : err);
@@ -605,19 +626,7 @@ function createAgentScoreCore(options) {
605
626
  return { ok: true, ...extractFromCached(resolveCached.raw) };
606
627
  }
607
628
  try {
608
- const response = await fetch(`${baseUrl}/v1/assess`, {
609
- method: "POST",
610
- headers: {
611
- "X-API-Key": apiKey,
612
- "Content-Type": "application/json",
613
- Accept: "application/json",
614
- "User-Agent": userAgentHeader
615
- },
616
- body: JSON.stringify({ address: walletAddress }),
617
- signal: AbortSignal.timeout(API_TIMEOUT_MS)
618
- });
619
- if (!response.ok) return { ok: false };
620
- const data = await response.json();
629
+ const data = await sdk.assess(walletAddress);
621
630
  cache.set(`resolve:${wallet}`, { allow: true, raw: data });
622
631
  return { ok: true, ...extractFromCached(data) };
623
632
  } catch (err) {
@@ -626,28 +635,37 @@ function createAgentScoreCore(options) {
626
635
  }
627
636
  }
628
637
  function reportSignerEvent(kind) {
629
- try {
630
- const pending = fetch(`${baseUrl}/v1/telemetry/signer-match`, {
631
- method: "POST",
632
- headers: {
633
- "X-API-Key": apiKey,
634
- "Content-Type": "application/json",
635
- Accept: "application/json",
636
- "User-Agent": userAgentHeader
637
- },
638
- body: JSON.stringify({ kind }),
639
- signal: AbortSignal.timeout(API_TIMEOUT_MS)
640
- });
641
- if (pending && typeof pending.catch === "function") {
642
- pending.catch((err) => {
643
- console.warn("[agentscore-commerce] signer-match telemetry failed:", err instanceof Error ? err.message : err);
644
- });
645
- }
646
- } catch {
638
+ void sdk.telemetrySignerMatch({ kind });
639
+ }
640
+ function projectSignerMatch(sm, claimedNorm, signerNorm) {
641
+ const kind = sm.kind;
642
+ if (kind === "pass") {
643
+ return {
644
+ kind: "pass",
645
+ claimedOperator: sm.claimed_operator ?? null,
646
+ signerOperator: sm.signer_operator ?? null
647
+ };
647
648
  }
649
+ if (kind === "wallet_auth_requires_wallet_signing") {
650
+ return {
651
+ kind: "wallet_auth_requires_wallet_signing",
652
+ claimedWallet: sm.claimed_wallet ?? claimedNorm,
653
+ agentInstructions: sm.agent_instructions ?? WALLET_AUTH_REQUIRES_WALLET_SIGNING_INSTRUCTIONS
654
+ };
655
+ }
656
+ const linked = sm.linked_wallets;
657
+ return {
658
+ kind: "wallet_signer_mismatch",
659
+ claimedOperator: sm.claimed_operator ?? null,
660
+ actualSignerOperator: sm.signer_operator ?? null,
661
+ expectedSigner: sm.expected_signer ?? claimedNorm,
662
+ actualSigner: sm.actual_signer ?? signerNorm,
663
+ linkedWallets: Array.isArray(linked) ? linked.filter((w) => typeof w === "string") : [],
664
+ agentInstructions: sm.agent_instructions ?? WALLET_SIGNER_MISMATCH_INSTRUCTIONS
665
+ };
648
666
  }
649
667
  async function verifyWalletSignerMatch2(options2) {
650
- const { claimedWallet, signer } = options2;
668
+ const { claimedWallet, signer, network } = options2;
651
669
  if (!signer) {
652
670
  reportSignerEvent("wallet_auth_requires_wallet_signing");
653
671
  return {
@@ -662,6 +680,35 @@ function createAgentScoreCore(options) {
662
680
  reportSignerEvent("pass");
663
681
  return { kind: "pass", claimedOperator: null, signerOperator: null };
664
682
  }
683
+ const cachedEntry = cache.get(claimedNorm);
684
+ const cachedMatch = cachedEntry?.signerMatchBySigner?.get(signerNorm);
685
+ if (cachedMatch) {
686
+ return projectSignerMatch(cachedMatch, claimedNorm, signerNorm);
687
+ }
688
+ const inferredNetwork = network ?? (signerNorm.startsWith("0x") ? "evm" : "solana");
689
+ let assessResponse;
690
+ try {
691
+ assessResponse = await sdk.assess(claimedNorm, {
692
+ resolveSigner: { address: signerNorm, network: inferredNetwork }
693
+ });
694
+ } catch (err) {
695
+ console.warn("[gate] verifyWalletSignerMatch assess failed:", err instanceof Error ? err.message : err);
696
+ reportSignerEvent("api_error");
697
+ return { kind: "api_error", claimedWallet: claimedNorm };
698
+ }
699
+ const signerMatch = assessResponse.signer_match;
700
+ if (signerMatch && typeof signerMatch === "object") {
701
+ if (cachedEntry) {
702
+ const map = cachedEntry.signerMatchBySigner ?? /* @__PURE__ */ new Map();
703
+ map.set(signerNorm, signerMatch);
704
+ cachedEntry.signerMatchBySigner = map;
705
+ } else {
706
+ const entry = { allow: true, raw: assessResponse };
707
+ entry.signerMatchBySigner = /* @__PURE__ */ new Map([[signerNorm, signerMatch]]);
708
+ cache.set(claimedNorm, entry);
709
+ }
710
+ return projectSignerMatch(signerMatch, claimedNorm, signerNorm);
711
+ }
665
712
  const [claimedResolve, signerResolve] = await Promise.all([
666
713
  resolveWalletToOperator(claimedNorm),
667
714
  resolveWalletToOperator(signerNorm)
@@ -683,8 +730,6 @@ function createAgentScoreCore(options) {
683
730
  actualSignerOperator: signerOperator,
684
731
  expectedSigner: claimedNorm,
685
732
  actualSigner: signerNorm,
686
- // Populated from /v1/assess.linked_wallets on the claimed wallet — the full set of
687
- // wallets the agent CAN sign with to satisfy the claim (same-operator rule).
688
733
  linkedWallets: claimedResolve.linkedWallets,
689
734
  agentInstructions: WALLET_SIGNER_MISMATCH_INSTRUCTIONS
690
735
  };
@@ -777,6 +822,14 @@ function agentscoreGate(options) {
777
822
  });
778
823
  const outcome = await core.evaluate(identity, c);
779
824
  if (outcome.kind === "allow") {
825
+ if (outcome.degraded || outcome.quota) {
826
+ const prev = c.get(GATE_STATE_KEY);
827
+ c.set(GATE_STATE_KEY, {
828
+ ...prev,
829
+ ...outcome.degraded && { degraded: true, infraReason: outcome.infraReason },
830
+ ...outcome.quota && { quota: outcome.quota }
831
+ });
832
+ }
780
833
  if (outcome.data) c.set(CONTEXT_KEY, outcome.data);
781
834
  await next();
782
835
  return;
@@ -787,6 +840,14 @@ function agentscoreGate(options) {
787
840
  function getAgentScoreData(c) {
788
841
  return c.get(CONTEXT_KEY);
789
842
  }
843
+ function getGateDegradedState(c) {
844
+ const state = c.get(GATE_STATE_KEY);
845
+ return { degraded: state?.degraded ?? false, infraReason: state?.infraReason };
846
+ }
847
+ function getGateQuotaInfo(c) {
848
+ const state = c.get(GATE_STATE_KEY);
849
+ return state?.quota;
850
+ }
790
851
  async function captureWallet(c, options) {
791
852
  const state = c.get(GATE_STATE_KEY);
792
853
  if (!state?.operatorToken) return;
@@ -820,6 +881,8 @@ async function verifyWalletSignerMatch(c, options) {
820
881
  denialReasonToBody,
821
882
  extractPaymentSignerAddress,
822
883
  getAgentScoreData,
884
+ getGateDegradedState,
885
+ getGateQuotaInfo,
823
886
  isFixableDenial,
824
887
  readX402PaymentHeader,
825
888
  verificationAgentInstructions,