@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.
- package/README.md +27 -0
- 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/core.d.mts
CHANGED
|
@@ -161,18 +161,45 @@ interface AgentScoreData {
|
|
|
161
161
|
}>;
|
|
162
162
|
} | null;
|
|
163
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Reason a failOpen allow short-circuited an evaluate call due to AgentScore-side
|
|
166
|
+
* infrastructure issues. Surfaced on `EvaluateOutcome` so merchants can log/alert when
|
|
167
|
+
* their gate is running in degraded mode (compliance not actually enforced this request).
|
|
168
|
+
*
|
|
169
|
+
* - `quota_exceeded` — AgentScore returned 429
|
|
170
|
+
* - `api_error` — AgentScore returned 5xx or non-2xx that isn't 429
|
|
171
|
+
* - `network_timeout` — request to /v1/assess timed out or failed at the network layer
|
|
172
|
+
*/
|
|
173
|
+
type FailOpenInfraReason = 'quota_exceeded' | 'api_error' | 'network_timeout';
|
|
174
|
+
/** Per-account assess quota observability, captured from `X-Quota-*` response headers
|
|
175
|
+
* on the success path. Mirrors the SDK's `QuotaInfo` shape — re-exported from gate state
|
|
176
|
+
* so merchants can monitor approach-to-cap proactively (warn at 80%, alert at 95%). */
|
|
177
|
+
interface GateQuotaInfo {
|
|
178
|
+
limit: number | null;
|
|
179
|
+
used: number | null;
|
|
180
|
+
/** ISO-8601 timestamp, or the literal string `"never"` for unlimited tiers. */
|
|
181
|
+
reset: string | null;
|
|
182
|
+
}
|
|
164
183
|
/**
|
|
165
184
|
* Outcome from `AgentScoreCore.evaluate()`. Adapters map this to framework-specific responses.
|
|
166
185
|
*
|
|
167
186
|
* - `{ kind: 'allow', data }` — the request passed the policy. `data` is present on a normal
|
|
168
187
|
* allow; `undefined` when fail-open short-circuited (identity missing, API unreachable,
|
|
169
188
|
* timeout, or 402 paid-tier required).
|
|
189
|
+
* - When `failOpen: true` and the allow was the result of an AgentScore-side infrastructure
|
|
190
|
+
* failure (429/5xx/timeout), the result also carries `degraded: true` + `infraReason` so
|
|
191
|
+
* merchants can alert/log without parsing console output.
|
|
192
|
+
* - `quota` propagates the SDK's per-request quota observability when the API emits the
|
|
193
|
+
* `X-Quota-*` headers. Optional; absent on Enterprise / unlimited tiers.
|
|
170
194
|
* - `{ kind: 'deny', reason }` — the request was denied. Adapters should render a 403 with the
|
|
171
195
|
* reason, or invoke the caller's custom denial handler.
|
|
172
196
|
*/
|
|
173
197
|
type EvaluateOutcome = {
|
|
174
198
|
kind: 'allow';
|
|
175
199
|
data?: AgentScoreData;
|
|
200
|
+
degraded?: boolean;
|
|
201
|
+
infraReason?: FailOpenInfraReason;
|
|
202
|
+
quota?: GateQuotaInfo;
|
|
176
203
|
} | {
|
|
177
204
|
kind: 'deny';
|
|
178
205
|
reason: DenialReason;
|
|
@@ -249,4 +276,4 @@ interface AgentScoreCore {
|
|
|
249
276
|
declare function buildAgentMemoryHint(): AgentMemoryHint;
|
|
250
277
|
declare function createAgentScoreCore(options: AgentScoreCoreOptions): AgentScoreCore;
|
|
251
278
|
|
|
252
|
-
export { type AgentIdentity, type AgentMemoryHint, type AgentScoreCore, type AgentScoreCoreOptions, type AgentScoreData, type CaptureWalletOptions, type CreateSessionOnMissing, type DenialCode, type DenialReason, type EvaluateOutcome, type SessionMetadata, type VerifyWalletSignerMatchOptions, type VerifyWalletSignerResult, buildAgentMemoryHint, createAgentScoreCore };
|
|
279
|
+
export { type AgentIdentity, type AgentMemoryHint, type AgentScoreCore, type AgentScoreCoreOptions, type AgentScoreData, type CaptureWalletOptions, type CreateSessionOnMissing, type DenialCode, type DenialReason, type EvaluateOutcome, type FailOpenInfraReason, type GateQuotaInfo, type SessionMetadata, type VerifyWalletSignerMatchOptions, type VerifyWalletSignerResult, buildAgentMemoryHint, createAgentScoreCore };
|
package/dist/core.d.ts
CHANGED
|
@@ -161,18 +161,45 @@ interface AgentScoreData {
|
|
|
161
161
|
}>;
|
|
162
162
|
} | null;
|
|
163
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Reason a failOpen allow short-circuited an evaluate call due to AgentScore-side
|
|
166
|
+
* infrastructure issues. Surfaced on `EvaluateOutcome` so merchants can log/alert when
|
|
167
|
+
* their gate is running in degraded mode (compliance not actually enforced this request).
|
|
168
|
+
*
|
|
169
|
+
* - `quota_exceeded` — AgentScore returned 429
|
|
170
|
+
* - `api_error` — AgentScore returned 5xx or non-2xx that isn't 429
|
|
171
|
+
* - `network_timeout` — request to /v1/assess timed out or failed at the network layer
|
|
172
|
+
*/
|
|
173
|
+
type FailOpenInfraReason = 'quota_exceeded' | 'api_error' | 'network_timeout';
|
|
174
|
+
/** Per-account assess quota observability, captured from `X-Quota-*` response headers
|
|
175
|
+
* on the success path. Mirrors the SDK's `QuotaInfo` shape — re-exported from gate state
|
|
176
|
+
* so merchants can monitor approach-to-cap proactively (warn at 80%, alert at 95%). */
|
|
177
|
+
interface GateQuotaInfo {
|
|
178
|
+
limit: number | null;
|
|
179
|
+
used: number | null;
|
|
180
|
+
/** ISO-8601 timestamp, or the literal string `"never"` for unlimited tiers. */
|
|
181
|
+
reset: string | null;
|
|
182
|
+
}
|
|
164
183
|
/**
|
|
165
184
|
* Outcome from `AgentScoreCore.evaluate()`. Adapters map this to framework-specific responses.
|
|
166
185
|
*
|
|
167
186
|
* - `{ kind: 'allow', data }` — the request passed the policy. `data` is present on a normal
|
|
168
187
|
* allow; `undefined` when fail-open short-circuited (identity missing, API unreachable,
|
|
169
188
|
* timeout, or 402 paid-tier required).
|
|
189
|
+
* - When `failOpen: true` and the allow was the result of an AgentScore-side infrastructure
|
|
190
|
+
* failure (429/5xx/timeout), the result also carries `degraded: true` + `infraReason` so
|
|
191
|
+
* merchants can alert/log without parsing console output.
|
|
192
|
+
* - `quota` propagates the SDK's per-request quota observability when the API emits the
|
|
193
|
+
* `X-Quota-*` headers. Optional; absent on Enterprise / unlimited tiers.
|
|
170
194
|
* - `{ kind: 'deny', reason }` — the request was denied. Adapters should render a 403 with the
|
|
171
195
|
* reason, or invoke the caller's custom denial handler.
|
|
172
196
|
*/
|
|
173
197
|
type EvaluateOutcome = {
|
|
174
198
|
kind: 'allow';
|
|
175
199
|
data?: AgentScoreData;
|
|
200
|
+
degraded?: boolean;
|
|
201
|
+
infraReason?: FailOpenInfraReason;
|
|
202
|
+
quota?: GateQuotaInfo;
|
|
176
203
|
} | {
|
|
177
204
|
kind: 'deny';
|
|
178
205
|
reason: DenialReason;
|
|
@@ -249,4 +276,4 @@ interface AgentScoreCore {
|
|
|
249
276
|
declare function buildAgentMemoryHint(): AgentMemoryHint;
|
|
250
277
|
declare function createAgentScoreCore(options: AgentScoreCoreOptions): AgentScoreCore;
|
|
251
278
|
|
|
252
|
-
export { type AgentIdentity, type AgentMemoryHint, type AgentScoreCore, type AgentScoreCoreOptions, type AgentScoreData, type CaptureWalletOptions, type CreateSessionOnMissing, type DenialCode, type DenialReason, type EvaluateOutcome, type SessionMetadata, type VerifyWalletSignerMatchOptions, type VerifyWalletSignerResult, buildAgentMemoryHint, createAgentScoreCore };
|
|
279
|
+
export { type AgentIdentity, type AgentMemoryHint, type AgentScoreCore, type AgentScoreCoreOptions, type AgentScoreData, type CaptureWalletOptions, type CreateSessionOnMissing, type DenialCode, type DenialReason, type EvaluateOutcome, type FailOpenInfraReason, type GateQuotaInfo, type SessionMetadata, type VerifyWalletSignerMatchOptions, type VerifyWalletSignerResult, buildAgentMemoryHint, createAgentScoreCore };
|
package/dist/core.js
CHANGED
|
@@ -24,6 +24,7 @@ __export(core_exports, {
|
|
|
24
24
|
createAgentScoreCore: () => createAgentScoreCore
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(core_exports);
|
|
27
|
+
var import_sdk = require("@agent-score/sdk");
|
|
27
28
|
|
|
28
29
|
// src/_denial.ts
|
|
29
30
|
var FIXABLE_DENIAL_REASONS = /* @__PURE__ */ new Set([
|
|
@@ -36,6 +37,61 @@ function isFixableDenial(reasons) {
|
|
|
36
37
|
return reasons.every((r) => FIXABLE_DENIAL_REASONS.has(r));
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
// src/_response.ts
|
|
41
|
+
var WALLET_NOT_TRUSTED_INSTRUCTIONS = JSON.stringify({
|
|
42
|
+
action: "contact_support",
|
|
43
|
+
steps: [
|
|
44
|
+
"The wallet's operator failed an UNFIXABLE compliance check (sanctions, age, or jurisdiction). `reasons` lists which: `sanctions_flagged` / `age_insufficient` / `jurisdiction_restricted`. KYC re-verification won't change the outcome \u2014 the policy denial is structural.",
|
|
45
|
+
"Surface the denial to the user with the merchant's support contact. Do not retry the same merchant request; do not hand the user a verify_url (verification won't fix this code path).",
|
|
46
|
+
"Fixable compliance reasons (`kyc_required`, `kyc_pending`, `kyc_failed`) do NOT land on this code \u2014 the gate auto-mints a verification session for those and returns `identity_verification_required` with poll endpoints, same shape as `missing_identity`. `jurisdiction_restricted` IS in the unfixable bucket because the API only emits it after KYC is verified (the user's KYC'd country is in the blocked list \u2014 re-doing KYC won't change the country)."
|
|
47
|
+
],
|
|
48
|
+
user_message: "This purchase is denied by the merchant's compliance policy and cannot be resolved by re-verifying. Contact the merchant's support if you believe this is in error."
|
|
49
|
+
});
|
|
50
|
+
var PAYMENT_REQUIRED_INSTRUCTIONS = JSON.stringify({
|
|
51
|
+
action: "contact_merchant",
|
|
52
|
+
steps: [
|
|
53
|
+
"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.",
|
|
54
|
+
"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."
|
|
55
|
+
],
|
|
56
|
+
user_message: "This merchant's identity gate is misconfigured. Contact the merchant \u2014 there's nothing to fix on the agent side."
|
|
57
|
+
});
|
|
58
|
+
var IDENTITY_VERIFICATION_REQUIRED_FALLBACK_INSTRUCTIONS = JSON.stringify({
|
|
59
|
+
action: "deliver_verify_url_and_poll",
|
|
60
|
+
steps: [
|
|
61
|
+
"Share verify_url with the user \u2014 they complete identity verification on AgentScore.",
|
|
62
|
+
"If session_id + poll_secret are present in the body, poll poll_url every 5 seconds with header `X-Poll-Secret: <poll_secret>` until status=verified. The poll returns a one-time operator_token.",
|
|
63
|
+
"Retry the original request with header `X-Operator-Token: <opc_...>`."
|
|
64
|
+
],
|
|
65
|
+
user_message: "Identity verification is required. Visit verify_url, then poll poll_url for the operator token and retry."
|
|
66
|
+
});
|
|
67
|
+
var API_ERROR_INSTRUCTIONS = JSON.stringify({
|
|
68
|
+
action: "retry_with_backoff",
|
|
69
|
+
steps: [
|
|
70
|
+
"Verification is temporarily unavailable. Retry the request after 5\u201330 seconds with exponential backoff.",
|
|
71
|
+
"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.",
|
|
72
|
+
"If the request continues to fail after 3+ retries (~60 seconds total), surface the error to the user with the merchant's support contact."
|
|
73
|
+
],
|
|
74
|
+
user_message: "Verification is temporarily unavailable. Please try again in a moment \u2014 this is a transient issue, not a problem with your account."
|
|
75
|
+
});
|
|
76
|
+
var QUOTA_EXCEEDED_INSTRUCTIONS = JSON.stringify({
|
|
77
|
+
action: "contact_merchant",
|
|
78
|
+
steps: [
|
|
79
|
+
"AgentScore identity verification is unavailable for this merchant. This is a merchant-side issue and is NOT recoverable via retry.",
|
|
80
|
+
"Do not retry: the same 503 will be returned until the merchant resolves the issue on their side.",
|
|
81
|
+
"Surface to the user with the merchant's support contact. The merchant (not the agent) needs to act."
|
|
82
|
+
],
|
|
83
|
+
user_message: "This merchant's identity verification is temporarily unavailable. Try again later, or contact the merchant directly."
|
|
84
|
+
});
|
|
85
|
+
var TOKEN_EXPIRED_FALLBACK_INSTRUCTIONS = JSON.stringify({
|
|
86
|
+
action: "deliver_verify_url_and_poll",
|
|
87
|
+
steps: [
|
|
88
|
+
"The operator token is expired or revoked. AgentScore auto-mints a fresh verification session \u2014 complete it to receive a new opc_...",
|
|
89
|
+
"Share verify_url with the user, then poll poll_url every 5 seconds with header `X-Poll-Secret: <poll_secret>` until status=verified. The poll returns a fresh one-time operator_token.",
|
|
90
|
+
"Retry the original request with header `X-Operator-Token: <new_opc_...>`."
|
|
91
|
+
],
|
|
92
|
+
user_message: "Operator token is expired or revoked. A new verification session has been minted \u2014 visit verify_url to refresh."
|
|
93
|
+
});
|
|
94
|
+
|
|
39
95
|
// src/address.ts
|
|
40
96
|
var SOLANA_BASE58_RE = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
41
97
|
var isSolanaAddress = (address) => SOLANA_BASE58_RE.test(address) && !address.startsWith("0x");
|
|
@@ -164,9 +220,23 @@ function createAgentScoreCore(options) {
|
|
|
164
220
|
} = options;
|
|
165
221
|
const baseUrl = stripTrailingSlashes(rawBaseUrl);
|
|
166
222
|
const agentMemoryHint = buildAgentMemoryHint();
|
|
167
|
-
const defaultUa = `@agent-score/commerce@${"1.0
|
|
223
|
+
const defaultUa = `@agent-score/commerce@${"1.1.0"}`;
|
|
168
224
|
const userAgentHeader = userAgent ? `${userAgent} (${defaultUa})` : defaultUa;
|
|
169
|
-
const
|
|
225
|
+
const sdk = new import_sdk.AgentScore({ apiKey, baseUrl, userAgent: userAgentHeader });
|
|
226
|
+
const sessionSdkCache = /* @__PURE__ */ new Map();
|
|
227
|
+
function getSessionSdk(sessionApiKey, sessionBaseUrl) {
|
|
228
|
+
const key = `${sessionApiKey}|${sessionBaseUrl ?? ""}`;
|
|
229
|
+
let s = sessionSdkCache.get(key);
|
|
230
|
+
if (!s) {
|
|
231
|
+
s = new import_sdk.AgentScore({
|
|
232
|
+
apiKey: sessionApiKey,
|
|
233
|
+
baseUrl: sessionBaseUrl ?? baseUrl,
|
|
234
|
+
userAgent: userAgentHeader
|
|
235
|
+
});
|
|
236
|
+
sessionSdkCache.set(key, s);
|
|
237
|
+
}
|
|
238
|
+
return s;
|
|
239
|
+
}
|
|
170
240
|
const cache = new TTLCache(cacheSeconds * 1e3);
|
|
171
241
|
async function tryMintSessionDenial(ctx) {
|
|
172
242
|
if (!createSessionOnMissing) return void 0;
|
|
@@ -183,20 +253,11 @@ function createAgentScoreCore(options) {
|
|
|
183
253
|
console.warn("[gate] createSessionOnMissing.getSessionOptions hook failed:", err instanceof Error ? err.message : err);
|
|
184
254
|
}
|
|
185
255
|
}
|
|
186
|
-
const
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
"X-API-Key": createSessionOnMissing.apiKey,
|
|
191
|
-
"Content-Type": "application/json",
|
|
192
|
-
Accept: "application/json",
|
|
193
|
-
"User-Agent": userAgentHeader
|
|
194
|
-
},
|
|
195
|
-
body: JSON.stringify(sessionBody),
|
|
196
|
-
signal: AbortSignal.timeout(API_TIMEOUT_MS)
|
|
256
|
+
const sessionSdk = getSessionSdk(createSessionOnMissing.apiKey, createSessionOnMissing.baseUrl);
|
|
257
|
+
const data = await sessionSdk.createSession({
|
|
258
|
+
...sessionBody.context !== void 0 ? { context: sessionBody.context } : {},
|
|
259
|
+
...sessionBody.product_name !== void 0 ? { product_name: sessionBody.product_name } : {}
|
|
197
260
|
});
|
|
198
|
-
if (!sessionRes.ok) return void 0;
|
|
199
|
-
const data = await sessionRes.json();
|
|
200
261
|
if (typeof data.session_id !== "string" || typeof data.poll_secret !== "string" || typeof data.verify_url !== "string") {
|
|
201
262
|
console.warn("[gate] /v1/sessions returned 200 without required fields \u2014 falling back to bare denial");
|
|
202
263
|
return void 0;
|
|
@@ -260,7 +321,13 @@ function createAgentScoreCore(options) {
|
|
|
260
321
|
const cached = cache.get(cacheKey);
|
|
261
322
|
if (cached) {
|
|
262
323
|
if (cached.allow) {
|
|
263
|
-
|
|
324
|
+
const cachedRaw = cached.raw;
|
|
325
|
+
const cachedQuota = cachedRaw?.quota;
|
|
326
|
+
return {
|
|
327
|
+
kind: "allow",
|
|
328
|
+
data: cachedRaw,
|
|
329
|
+
...cachedQuota !== void 0 && { quota: cachedQuota }
|
|
330
|
+
};
|
|
264
331
|
}
|
|
265
332
|
if (isFixableDenial(cached.reasons)) {
|
|
266
333
|
const sessionReason = await tryMintSessionDenial(ctx);
|
|
@@ -277,130 +344,119 @@ function createAgentScoreCore(options) {
|
|
|
277
344
|
}
|
|
278
345
|
};
|
|
279
346
|
}
|
|
347
|
+
const policy = {};
|
|
348
|
+
if (requireKyc != null) policy.require_kyc = requireKyc;
|
|
349
|
+
if (requireSanctionsClear != null) policy.require_sanctions_clear = requireSanctionsClear;
|
|
350
|
+
if (minAge != null) policy.min_age = minAge;
|
|
351
|
+
if (blockedJurisdictions != null) policy.blocked_jurisdictions = blockedJurisdictions;
|
|
352
|
+
if (allowedJurisdictions != null) policy.allowed_jurisdictions = allowedJurisdictions;
|
|
353
|
+
let data;
|
|
280
354
|
try {
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
if (
|
|
289
|
-
if (blockedJurisdictions != null) policy.blocked_jurisdictions = blockedJurisdictions;
|
|
290
|
-
if (allowedJurisdictions != null) policy.allowed_jurisdictions = allowedJurisdictions;
|
|
291
|
-
if (Object.keys(policy).length > 0) body.policy = policy;
|
|
292
|
-
const response = await fetch(`${baseUrl}/v1/assess`, {
|
|
293
|
-
method: "POST",
|
|
294
|
-
headers: {
|
|
295
|
-
"X-API-Key": apiKey,
|
|
296
|
-
"Content-Type": "application/json",
|
|
297
|
-
Accept: "application/json",
|
|
298
|
-
"User-Agent": userAgentHeader
|
|
299
|
-
},
|
|
300
|
-
body: JSON.stringify(body),
|
|
301
|
-
signal: AbortSignal.timeout(API_TIMEOUT_MS)
|
|
302
|
-
});
|
|
303
|
-
if (response.status === 402) {
|
|
355
|
+
const opts = {
|
|
356
|
+
chain: gateChain,
|
|
357
|
+
...Object.keys(policy).length > 0 ? { policy } : {}
|
|
358
|
+
};
|
|
359
|
+
const result = identity.address ? await sdk.assess(identity.address, { ...opts, operatorToken: identity.operatorToken }) : await sdk.assess(null, { ...opts, operatorToken: identity.operatorToken });
|
|
360
|
+
data = result;
|
|
361
|
+
} catch (err) {
|
|
362
|
+
if (err instanceof import_sdk.PaymentRequiredError) {
|
|
304
363
|
if (failOpen) return { kind: "allow" };
|
|
305
364
|
return { kind: "deny", reason: { code: "payment_required" } };
|
|
306
365
|
}
|
|
307
|
-
if (
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
...typeof errData.poll_secret === "string" ? { poll_secret: errData.poll_secret } : {},
|
|
320
|
-
...typeof errData.poll_url === "string" ? { poll_url: errData.poll_url } : {},
|
|
321
|
-
...errData.next_steps ? { agent_instructions: JSON.stringify(errData.next_steps) } : {},
|
|
322
|
-
...errData.agent_memory ? { agent_memory: errData.agent_memory } : {}
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
if (code === "invalid_credential") {
|
|
327
|
-
return {
|
|
328
|
-
kind: "deny",
|
|
329
|
-
reason: {
|
|
330
|
-
code: "invalid_credential",
|
|
331
|
-
agent_instructions: INVALID_CREDENTIAL_INSTRUCTIONS,
|
|
332
|
-
agent_memory: agentMemoryHint
|
|
333
|
-
}
|
|
334
|
-
};
|
|
366
|
+
if (err instanceof import_sdk.TokenExpiredError) {
|
|
367
|
+
return {
|
|
368
|
+
kind: "deny",
|
|
369
|
+
reason: {
|
|
370
|
+
code: "token_expired",
|
|
371
|
+
data: err.details,
|
|
372
|
+
...err.verifyUrl ? { verify_url: err.verifyUrl } : {},
|
|
373
|
+
...err.sessionId ? { session_id: err.sessionId } : {},
|
|
374
|
+
...err.pollSecret ? { poll_secret: err.pollSecret } : {},
|
|
375
|
+
...err.pollUrl ? { poll_url: err.pollUrl } : {},
|
|
376
|
+
...err.nextSteps ? { agent_instructions: JSON.stringify(err.nextSteps) } : {},
|
|
377
|
+
...err.agentMemory ? { agent_memory: err.agentMemory } : {}
|
|
335
378
|
}
|
|
336
|
-
|
|
337
|
-
console.warn(`[gate] /v1/assess returned 401 ${code} \u2014 no specific handler, surfacing as api_error.`);
|
|
338
|
-
}
|
|
339
|
-
} catch (err) {
|
|
340
|
-
console.warn("[gate] /v1/assess 401 body parse failed:", err instanceof Error ? err.message : err);
|
|
341
|
-
}
|
|
379
|
+
};
|
|
342
380
|
}
|
|
343
|
-
if (
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
);
|
|
381
|
+
if (err instanceof import_sdk.InvalidCredentialError) {
|
|
382
|
+
return {
|
|
383
|
+
kind: "deny",
|
|
384
|
+
reason: {
|
|
385
|
+
code: "invalid_credential",
|
|
386
|
+
agent_instructions: INVALID_CREDENTIAL_INSTRUCTIONS,
|
|
387
|
+
agent_memory: agentMemoryHint
|
|
351
388
|
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
389
|
+
};
|
|
354
390
|
}
|
|
355
|
-
if (
|
|
356
|
-
|
|
391
|
+
if (err instanceof import_sdk.QuotaExceededError) {
|
|
392
|
+
console.warn("[gate] /v1/assess returned 429 quota_exceeded");
|
|
393
|
+
if (failOpen) return { kind: "allow", degraded: true, infraReason: "quota_exceeded" };
|
|
394
|
+
return {
|
|
395
|
+
kind: "deny",
|
|
396
|
+
reason: { code: "api_error", agent_instructions: QUOTA_EXCEEDED_INSTRUCTIONS }
|
|
397
|
+
};
|
|
357
398
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
cache.set(cacheKey, { allow, decision: decision ?? void 0, reasons: decisionReasons, raw: data });
|
|
363
|
-
if (allow) {
|
|
364
|
-
return { kind: "allow", data };
|
|
399
|
+
if (err instanceof import_sdk.TimeoutError) {
|
|
400
|
+
console.warn("[gate] /v1/assess timed out:", err.message);
|
|
401
|
+
if (failOpen) return { kind: "allow", degraded: true, infraReason: "network_timeout" };
|
|
402
|
+
return { kind: "deny", reason: { code: "api_error" } };
|
|
365
403
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
404
|
+
const status = err?.status;
|
|
405
|
+
const errName = err instanceof Error ? err.name : "";
|
|
406
|
+
if (status === 429) {
|
|
407
|
+
console.warn("[gate] /v1/assess returned 429 (untyped \u2014 defensive)");
|
|
408
|
+
if (failOpen) return { kind: "allow", degraded: true, infraReason: "quota_exceeded" };
|
|
409
|
+
return {
|
|
410
|
+
kind: "deny",
|
|
411
|
+
reason: { code: "api_error", agent_instructions: QUOTA_EXCEEDED_INSTRUCTIONS }
|
|
412
|
+
};
|
|
369
413
|
}
|
|
414
|
+
if (errName === "TimeoutError" || errName === "AbortError") {
|
|
415
|
+
console.warn("[gate] /v1/assess timed out (by Error.name):", err instanceof Error ? err.message : err);
|
|
416
|
+
if (failOpen) return { kind: "allow", degraded: true, infraReason: "network_timeout" };
|
|
417
|
+
return { kind: "deny", reason: { code: "api_error" } };
|
|
418
|
+
}
|
|
419
|
+
const errCode = err?.code;
|
|
420
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
421
|
+
const detail = errCode ? `${errCode}: ${msg}` : msg;
|
|
422
|
+
console.warn(`[gate] /v1/assess call failed \u2014 surfacing as api_error: ${detail}`);
|
|
423
|
+
if (failOpen) return { kind: "allow", degraded: true, infraReason: "api_error" };
|
|
424
|
+
return { kind: "deny", reason: { code: "api_error" } };
|
|
425
|
+
}
|
|
426
|
+
const decision = data.decision;
|
|
427
|
+
const decisionReasons = data.decision_reasons ?? [];
|
|
428
|
+
const allow = decision === "allow" || decision == null;
|
|
429
|
+
cache.set(cacheKey, { allow, decision: decision ?? void 0, reasons: decisionReasons, raw: data });
|
|
430
|
+
if (allow) {
|
|
431
|
+
const quota = data.quota;
|
|
370
432
|
return {
|
|
371
|
-
kind: "
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
decision: decision ?? void 0,
|
|
375
|
-
reasons: decisionReasons,
|
|
376
|
-
verify_url: data.verify_url,
|
|
377
|
-
data
|
|
378
|
-
}
|
|
433
|
+
kind: "allow",
|
|
434
|
+
data,
|
|
435
|
+
...quota !== void 0 && { quota }
|
|
379
436
|
};
|
|
380
|
-
} catch (err) {
|
|
381
|
-
console.warn("[gate] /v1/assess call failed \u2014 surfacing as api_error:", err instanceof Error ? err.message : err);
|
|
382
|
-
if (failOpen) return { kind: "allow" };
|
|
383
|
-
return { kind: "deny", reason: { code: "api_error" } };
|
|
384
437
|
}
|
|
438
|
+
if (isFixableDenial(decisionReasons)) {
|
|
439
|
+
const sessionReason = await tryMintSessionDenial(ctx);
|
|
440
|
+
if (sessionReason) return { kind: "deny", reason: sessionReason };
|
|
441
|
+
}
|
|
442
|
+
return {
|
|
443
|
+
kind: "deny",
|
|
444
|
+
reason: {
|
|
445
|
+
code: "wallet_not_trusted",
|
|
446
|
+
decision: decision ?? void 0,
|
|
447
|
+
reasons: decisionReasons,
|
|
448
|
+
verify_url: data.verify_url,
|
|
449
|
+
data
|
|
450
|
+
}
|
|
451
|
+
};
|
|
385
452
|
}
|
|
386
453
|
async function captureWallet(options2) {
|
|
387
454
|
try {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
network: options2.network
|
|
392
|
-
|
|
393
|
-
if (options2.idempotencyKey) body.idempotency_key = options2.idempotencyKey;
|
|
394
|
-
await fetch(`${baseUrl}/v1/credentials/wallets`, {
|
|
395
|
-
method: "POST",
|
|
396
|
-
headers: {
|
|
397
|
-
"X-API-Key": apiKey,
|
|
398
|
-
"Content-Type": "application/json",
|
|
399
|
-
Accept: "application/json",
|
|
400
|
-
"User-Agent": userAgentHeader
|
|
401
|
-
},
|
|
402
|
-
body: JSON.stringify(body),
|
|
403
|
-
signal: AbortSignal.timeout(API_TIMEOUT_MS)
|
|
455
|
+
await sdk.associateWallet({
|
|
456
|
+
operatorToken: options2.operatorToken,
|
|
457
|
+
walletAddress: options2.walletAddress,
|
|
458
|
+
network: options2.network,
|
|
459
|
+
...options2.idempotencyKey ? { idempotencyKey: options2.idempotencyKey } : {}
|
|
404
460
|
});
|
|
405
461
|
} catch (err) {
|
|
406
462
|
console.warn("[agentscore-commerce] captureWallet failed:", err instanceof Error ? err.message : err);
|
|
@@ -425,19 +481,7 @@ function createAgentScoreCore(options) {
|
|
|
425
481
|
return { ok: true, ...extractFromCached(resolveCached.raw) };
|
|
426
482
|
}
|
|
427
483
|
try {
|
|
428
|
-
const
|
|
429
|
-
method: "POST",
|
|
430
|
-
headers: {
|
|
431
|
-
"X-API-Key": apiKey,
|
|
432
|
-
"Content-Type": "application/json",
|
|
433
|
-
Accept: "application/json",
|
|
434
|
-
"User-Agent": userAgentHeader
|
|
435
|
-
},
|
|
436
|
-
body: JSON.stringify({ address: walletAddress }),
|
|
437
|
-
signal: AbortSignal.timeout(API_TIMEOUT_MS)
|
|
438
|
-
});
|
|
439
|
-
if (!response.ok) return { ok: false };
|
|
440
|
-
const data = await response.json();
|
|
484
|
+
const data = await sdk.assess(walletAddress);
|
|
441
485
|
cache.set(`resolve:${wallet}`, { allow: true, raw: data });
|
|
442
486
|
return { ok: true, ...extractFromCached(data) };
|
|
443
487
|
} catch (err) {
|
|
@@ -446,28 +490,37 @@ function createAgentScoreCore(options) {
|
|
|
446
490
|
}
|
|
447
491
|
}
|
|
448
492
|
function reportSignerEvent(kind) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
signal: AbortSignal.timeout(API_TIMEOUT_MS)
|
|
460
|
-
});
|
|
461
|
-
if (pending && typeof pending.catch === "function") {
|
|
462
|
-
pending.catch((err) => {
|
|
463
|
-
console.warn("[agentscore-commerce] signer-match telemetry failed:", err instanceof Error ? err.message : err);
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
} catch {
|
|
493
|
+
void sdk.telemetrySignerMatch({ kind });
|
|
494
|
+
}
|
|
495
|
+
function projectSignerMatch(sm, claimedNorm, signerNorm) {
|
|
496
|
+
const kind = sm.kind;
|
|
497
|
+
if (kind === "pass") {
|
|
498
|
+
return {
|
|
499
|
+
kind: "pass",
|
|
500
|
+
claimedOperator: sm.claimed_operator ?? null,
|
|
501
|
+
signerOperator: sm.signer_operator ?? null
|
|
502
|
+
};
|
|
467
503
|
}
|
|
504
|
+
if (kind === "wallet_auth_requires_wallet_signing") {
|
|
505
|
+
return {
|
|
506
|
+
kind: "wallet_auth_requires_wallet_signing",
|
|
507
|
+
claimedWallet: sm.claimed_wallet ?? claimedNorm,
|
|
508
|
+
agentInstructions: sm.agent_instructions ?? WALLET_AUTH_REQUIRES_WALLET_SIGNING_INSTRUCTIONS
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
const linked = sm.linked_wallets;
|
|
512
|
+
return {
|
|
513
|
+
kind: "wallet_signer_mismatch",
|
|
514
|
+
claimedOperator: sm.claimed_operator ?? null,
|
|
515
|
+
actualSignerOperator: sm.signer_operator ?? null,
|
|
516
|
+
expectedSigner: sm.expected_signer ?? claimedNorm,
|
|
517
|
+
actualSigner: sm.actual_signer ?? signerNorm,
|
|
518
|
+
linkedWallets: Array.isArray(linked) ? linked.filter((w) => typeof w === "string") : [],
|
|
519
|
+
agentInstructions: sm.agent_instructions ?? WALLET_SIGNER_MISMATCH_INSTRUCTIONS
|
|
520
|
+
};
|
|
468
521
|
}
|
|
469
522
|
async function verifyWalletSignerMatch(options2) {
|
|
470
|
-
const { claimedWallet, signer } = options2;
|
|
523
|
+
const { claimedWallet, signer, network } = options2;
|
|
471
524
|
if (!signer) {
|
|
472
525
|
reportSignerEvent("wallet_auth_requires_wallet_signing");
|
|
473
526
|
return {
|
|
@@ -482,6 +535,35 @@ function createAgentScoreCore(options) {
|
|
|
482
535
|
reportSignerEvent("pass");
|
|
483
536
|
return { kind: "pass", claimedOperator: null, signerOperator: null };
|
|
484
537
|
}
|
|
538
|
+
const cachedEntry = cache.get(claimedNorm);
|
|
539
|
+
const cachedMatch = cachedEntry?.signerMatchBySigner?.get(signerNorm);
|
|
540
|
+
if (cachedMatch) {
|
|
541
|
+
return projectSignerMatch(cachedMatch, claimedNorm, signerNorm);
|
|
542
|
+
}
|
|
543
|
+
const inferredNetwork = network ?? (signerNorm.startsWith("0x") ? "evm" : "solana");
|
|
544
|
+
let assessResponse;
|
|
545
|
+
try {
|
|
546
|
+
assessResponse = await sdk.assess(claimedNorm, {
|
|
547
|
+
resolveSigner: { address: signerNorm, network: inferredNetwork }
|
|
548
|
+
});
|
|
549
|
+
} catch (err) {
|
|
550
|
+
console.warn("[gate] verifyWalletSignerMatch assess failed:", err instanceof Error ? err.message : err);
|
|
551
|
+
reportSignerEvent("api_error");
|
|
552
|
+
return { kind: "api_error", claimedWallet: claimedNorm };
|
|
553
|
+
}
|
|
554
|
+
const signerMatch = assessResponse.signer_match;
|
|
555
|
+
if (signerMatch && typeof signerMatch === "object") {
|
|
556
|
+
if (cachedEntry) {
|
|
557
|
+
const map = cachedEntry.signerMatchBySigner ?? /* @__PURE__ */ new Map();
|
|
558
|
+
map.set(signerNorm, signerMatch);
|
|
559
|
+
cachedEntry.signerMatchBySigner = map;
|
|
560
|
+
} else {
|
|
561
|
+
const entry = { allow: true, raw: assessResponse };
|
|
562
|
+
entry.signerMatchBySigner = /* @__PURE__ */ new Map([[signerNorm, signerMatch]]);
|
|
563
|
+
cache.set(claimedNorm, entry);
|
|
564
|
+
}
|
|
565
|
+
return projectSignerMatch(signerMatch, claimedNorm, signerNorm);
|
|
566
|
+
}
|
|
485
567
|
const [claimedResolve, signerResolve] = await Promise.all([
|
|
486
568
|
resolveWalletToOperator(claimedNorm),
|
|
487
569
|
resolveWalletToOperator(signerNorm)
|
|
@@ -503,8 +585,6 @@ function createAgentScoreCore(options) {
|
|
|
503
585
|
actualSignerOperator: signerOperator,
|
|
504
586
|
expectedSigner: claimedNorm,
|
|
505
587
|
actualSigner: signerNorm,
|
|
506
|
-
// Populated from /v1/assess.linked_wallets on the claimed wallet — the full set of
|
|
507
|
-
// wallets the agent CAN sign with to satisfy the claim (same-operator rule).
|
|
508
588
|
linkedWallets: claimedResolve.linkedWallets,
|
|
509
589
|
agentInstructions: WALLET_SIGNER_MISMATCH_INSTRUCTIONS
|
|
510
590
|
};
|