@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.mjs
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
// src/core.ts
|
|
2
|
+
import {
|
|
3
|
+
AgentScore,
|
|
4
|
+
InvalidCredentialError,
|
|
5
|
+
PaymentRequiredError,
|
|
6
|
+
QuotaExceededError,
|
|
7
|
+
TimeoutError as SdkTimeoutError,
|
|
8
|
+
TokenExpiredError
|
|
9
|
+
} from "@agent-score/sdk";
|
|
10
|
+
|
|
1
11
|
// src/_denial.ts
|
|
2
12
|
var FIXABLE_DENIAL_REASONS = /* @__PURE__ */ new Set([
|
|
3
13
|
"kyc_required",
|
|
@@ -9,6 +19,61 @@ function isFixableDenial(reasons) {
|
|
|
9
19
|
return reasons.every((r) => FIXABLE_DENIAL_REASONS.has(r));
|
|
10
20
|
}
|
|
11
21
|
|
|
22
|
+
// src/_response.ts
|
|
23
|
+
var WALLET_NOT_TRUSTED_INSTRUCTIONS = JSON.stringify({
|
|
24
|
+
action: "contact_support",
|
|
25
|
+
steps: [
|
|
26
|
+
"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.",
|
|
27
|
+
"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).",
|
|
28
|
+
"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)."
|
|
29
|
+
],
|
|
30
|
+
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."
|
|
31
|
+
});
|
|
32
|
+
var PAYMENT_REQUIRED_INSTRUCTIONS = JSON.stringify({
|
|
33
|
+
action: "contact_merchant",
|
|
34
|
+
steps: [
|
|
35
|
+
"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.",
|
|
36
|
+
"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."
|
|
37
|
+
],
|
|
38
|
+
user_message: "This merchant's identity gate is misconfigured. Contact the merchant \u2014 there's nothing to fix on the agent side."
|
|
39
|
+
});
|
|
40
|
+
var IDENTITY_VERIFICATION_REQUIRED_FALLBACK_INSTRUCTIONS = JSON.stringify({
|
|
41
|
+
action: "deliver_verify_url_and_poll",
|
|
42
|
+
steps: [
|
|
43
|
+
"Share verify_url with the user \u2014 they complete identity verification on AgentScore.",
|
|
44
|
+
"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.",
|
|
45
|
+
"Retry the original request with header `X-Operator-Token: <opc_...>`."
|
|
46
|
+
],
|
|
47
|
+
user_message: "Identity verification is required. Visit verify_url, then poll poll_url for the operator token and retry."
|
|
48
|
+
});
|
|
49
|
+
var API_ERROR_INSTRUCTIONS = JSON.stringify({
|
|
50
|
+
action: "retry_with_backoff",
|
|
51
|
+
steps: [
|
|
52
|
+
"Verification is temporarily unavailable. Retry the request after 5\u201330 seconds with exponential backoff.",
|
|
53
|
+
"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.",
|
|
54
|
+
"If the request continues to fail after 3+ retries (~60 seconds total), surface the error to the user with the merchant's support contact."
|
|
55
|
+
],
|
|
56
|
+
user_message: "Verification is temporarily unavailable. Please try again in a moment \u2014 this is a transient issue, not a problem with your account."
|
|
57
|
+
});
|
|
58
|
+
var QUOTA_EXCEEDED_INSTRUCTIONS = JSON.stringify({
|
|
59
|
+
action: "contact_merchant",
|
|
60
|
+
steps: [
|
|
61
|
+
"AgentScore identity verification is unavailable for this merchant. This is a merchant-side issue and is NOT recoverable via retry.",
|
|
62
|
+
"Do not retry: the same 503 will be returned until the merchant resolves the issue on their side.",
|
|
63
|
+
"Surface to the user with the merchant's support contact. The merchant (not the agent) needs to act."
|
|
64
|
+
],
|
|
65
|
+
user_message: "This merchant's identity verification is temporarily unavailable. Try again later, or contact the merchant directly."
|
|
66
|
+
});
|
|
67
|
+
var TOKEN_EXPIRED_FALLBACK_INSTRUCTIONS = JSON.stringify({
|
|
68
|
+
action: "deliver_verify_url_and_poll",
|
|
69
|
+
steps: [
|
|
70
|
+
"The operator token is expired or revoked. AgentScore auto-mints a fresh verification session \u2014 complete it to receive a new opc_...",
|
|
71
|
+
"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.",
|
|
72
|
+
"Retry the original request with header `X-Operator-Token: <new_opc_...>`."
|
|
73
|
+
],
|
|
74
|
+
user_message: "Operator token is expired or revoked. A new verification session has been minted \u2014 visit verify_url to refresh."
|
|
75
|
+
});
|
|
76
|
+
|
|
12
77
|
// src/address.ts
|
|
13
78
|
var SOLANA_BASE58_RE = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
14
79
|
var isSolanaAddress = (address) => SOLANA_BASE58_RE.test(address) && !address.startsWith("0x");
|
|
@@ -137,9 +202,23 @@ function createAgentScoreCore(options) {
|
|
|
137
202
|
} = options;
|
|
138
203
|
const baseUrl = stripTrailingSlashes(rawBaseUrl);
|
|
139
204
|
const agentMemoryHint = buildAgentMemoryHint();
|
|
140
|
-
const defaultUa = `@agent-score/commerce@${"1.0
|
|
205
|
+
const defaultUa = `@agent-score/commerce@${"1.1.0"}`;
|
|
141
206
|
const userAgentHeader = userAgent ? `${userAgent} (${defaultUa})` : defaultUa;
|
|
142
|
-
const
|
|
207
|
+
const sdk = new AgentScore({ apiKey, baseUrl, userAgent: userAgentHeader });
|
|
208
|
+
const sessionSdkCache = /* @__PURE__ */ new Map();
|
|
209
|
+
function getSessionSdk(sessionApiKey, sessionBaseUrl) {
|
|
210
|
+
const key = `${sessionApiKey}|${sessionBaseUrl ?? ""}`;
|
|
211
|
+
let s = sessionSdkCache.get(key);
|
|
212
|
+
if (!s) {
|
|
213
|
+
s = new AgentScore({
|
|
214
|
+
apiKey: sessionApiKey,
|
|
215
|
+
baseUrl: sessionBaseUrl ?? baseUrl,
|
|
216
|
+
userAgent: userAgentHeader
|
|
217
|
+
});
|
|
218
|
+
sessionSdkCache.set(key, s);
|
|
219
|
+
}
|
|
220
|
+
return s;
|
|
221
|
+
}
|
|
143
222
|
const cache = new TTLCache(cacheSeconds * 1e3);
|
|
144
223
|
async function tryMintSessionDenial(ctx) {
|
|
145
224
|
if (!createSessionOnMissing) return void 0;
|
|
@@ -156,20 +235,11 @@ function createAgentScoreCore(options) {
|
|
|
156
235
|
console.warn("[gate] createSessionOnMissing.getSessionOptions hook failed:", err instanceof Error ? err.message : err);
|
|
157
236
|
}
|
|
158
237
|
}
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
"X-API-Key": createSessionOnMissing.apiKey,
|
|
164
|
-
"Content-Type": "application/json",
|
|
165
|
-
Accept: "application/json",
|
|
166
|
-
"User-Agent": userAgentHeader
|
|
167
|
-
},
|
|
168
|
-
body: JSON.stringify(sessionBody),
|
|
169
|
-
signal: AbortSignal.timeout(API_TIMEOUT_MS)
|
|
238
|
+
const sessionSdk = getSessionSdk(createSessionOnMissing.apiKey, createSessionOnMissing.baseUrl);
|
|
239
|
+
const data = await sessionSdk.createSession({
|
|
240
|
+
...sessionBody.context !== void 0 ? { context: sessionBody.context } : {},
|
|
241
|
+
...sessionBody.product_name !== void 0 ? { product_name: sessionBody.product_name } : {}
|
|
170
242
|
});
|
|
171
|
-
if (!sessionRes.ok) return void 0;
|
|
172
|
-
const data = await sessionRes.json();
|
|
173
243
|
if (typeof data.session_id !== "string" || typeof data.poll_secret !== "string" || typeof data.verify_url !== "string") {
|
|
174
244
|
console.warn("[gate] /v1/sessions returned 200 without required fields \u2014 falling back to bare denial");
|
|
175
245
|
return void 0;
|
|
@@ -233,7 +303,13 @@ function createAgentScoreCore(options) {
|
|
|
233
303
|
const cached = cache.get(cacheKey);
|
|
234
304
|
if (cached) {
|
|
235
305
|
if (cached.allow) {
|
|
236
|
-
|
|
306
|
+
const cachedRaw = cached.raw;
|
|
307
|
+
const cachedQuota = cachedRaw?.quota;
|
|
308
|
+
return {
|
|
309
|
+
kind: "allow",
|
|
310
|
+
data: cachedRaw,
|
|
311
|
+
...cachedQuota !== void 0 && { quota: cachedQuota }
|
|
312
|
+
};
|
|
237
313
|
}
|
|
238
314
|
if (isFixableDenial(cached.reasons)) {
|
|
239
315
|
const sessionReason = await tryMintSessionDenial(ctx);
|
|
@@ -250,130 +326,119 @@ function createAgentScoreCore(options) {
|
|
|
250
326
|
}
|
|
251
327
|
};
|
|
252
328
|
}
|
|
329
|
+
const policy = {};
|
|
330
|
+
if (requireKyc != null) policy.require_kyc = requireKyc;
|
|
331
|
+
if (requireSanctionsClear != null) policy.require_sanctions_clear = requireSanctionsClear;
|
|
332
|
+
if (minAge != null) policy.min_age = minAge;
|
|
333
|
+
if (blockedJurisdictions != null) policy.blocked_jurisdictions = blockedJurisdictions;
|
|
334
|
+
if (allowedJurisdictions != null) policy.allowed_jurisdictions = allowedJurisdictions;
|
|
335
|
+
let data;
|
|
253
336
|
try {
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if (
|
|
262
|
-
if (blockedJurisdictions != null) policy.blocked_jurisdictions = blockedJurisdictions;
|
|
263
|
-
if (allowedJurisdictions != null) policy.allowed_jurisdictions = allowedJurisdictions;
|
|
264
|
-
if (Object.keys(policy).length > 0) body.policy = policy;
|
|
265
|
-
const response = await fetch(`${baseUrl}/v1/assess`, {
|
|
266
|
-
method: "POST",
|
|
267
|
-
headers: {
|
|
268
|
-
"X-API-Key": apiKey,
|
|
269
|
-
"Content-Type": "application/json",
|
|
270
|
-
Accept: "application/json",
|
|
271
|
-
"User-Agent": userAgentHeader
|
|
272
|
-
},
|
|
273
|
-
body: JSON.stringify(body),
|
|
274
|
-
signal: AbortSignal.timeout(API_TIMEOUT_MS)
|
|
275
|
-
});
|
|
276
|
-
if (response.status === 402) {
|
|
337
|
+
const opts = {
|
|
338
|
+
chain: gateChain,
|
|
339
|
+
...Object.keys(policy).length > 0 ? { policy } : {}
|
|
340
|
+
};
|
|
341
|
+
const result = identity.address ? await sdk.assess(identity.address, { ...opts, operatorToken: identity.operatorToken }) : await sdk.assess(null, { ...opts, operatorToken: identity.operatorToken });
|
|
342
|
+
data = result;
|
|
343
|
+
} catch (err) {
|
|
344
|
+
if (err instanceof PaymentRequiredError) {
|
|
277
345
|
if (failOpen) return { kind: "allow" };
|
|
278
346
|
return { kind: "deny", reason: { code: "payment_required" } };
|
|
279
347
|
}
|
|
280
|
-
if (
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
...typeof errData.poll_secret === "string" ? { poll_secret: errData.poll_secret } : {},
|
|
293
|
-
...typeof errData.poll_url === "string" ? { poll_url: errData.poll_url } : {},
|
|
294
|
-
...errData.next_steps ? { agent_instructions: JSON.stringify(errData.next_steps) } : {},
|
|
295
|
-
...errData.agent_memory ? { agent_memory: errData.agent_memory } : {}
|
|
296
|
-
}
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
if (code === "invalid_credential") {
|
|
300
|
-
return {
|
|
301
|
-
kind: "deny",
|
|
302
|
-
reason: {
|
|
303
|
-
code: "invalid_credential",
|
|
304
|
-
agent_instructions: INVALID_CREDENTIAL_INSTRUCTIONS,
|
|
305
|
-
agent_memory: agentMemoryHint
|
|
306
|
-
}
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
if (code) {
|
|
310
|
-
console.warn(`[gate] /v1/assess returned 401 ${code} \u2014 no specific handler, surfacing as api_error.`);
|
|
348
|
+
if (err instanceof TokenExpiredError) {
|
|
349
|
+
return {
|
|
350
|
+
kind: "deny",
|
|
351
|
+
reason: {
|
|
352
|
+
code: "token_expired",
|
|
353
|
+
data: err.details,
|
|
354
|
+
...err.verifyUrl ? { verify_url: err.verifyUrl } : {},
|
|
355
|
+
...err.sessionId ? { session_id: err.sessionId } : {},
|
|
356
|
+
...err.pollSecret ? { poll_secret: err.pollSecret } : {},
|
|
357
|
+
...err.pollUrl ? { poll_url: err.pollUrl } : {},
|
|
358
|
+
...err.nextSteps ? { agent_instructions: JSON.stringify(err.nextSteps) } : {},
|
|
359
|
+
...err.agentMemory ? { agent_memory: err.agentMemory } : {}
|
|
311
360
|
}
|
|
312
|
-
}
|
|
313
|
-
console.warn("[gate] /v1/assess 401 body parse failed:", err instanceof Error ? err.message : err);
|
|
314
|
-
}
|
|
361
|
+
};
|
|
315
362
|
}
|
|
316
|
-
if (
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
);
|
|
363
|
+
if (err instanceof InvalidCredentialError) {
|
|
364
|
+
return {
|
|
365
|
+
kind: "deny",
|
|
366
|
+
reason: {
|
|
367
|
+
code: "invalid_credential",
|
|
368
|
+
agent_instructions: INVALID_CREDENTIAL_INSTRUCTIONS,
|
|
369
|
+
agent_memory: agentMemoryHint
|
|
324
370
|
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
371
|
+
};
|
|
327
372
|
}
|
|
328
|
-
if (
|
|
329
|
-
|
|
373
|
+
if (err instanceof QuotaExceededError) {
|
|
374
|
+
console.warn("[gate] /v1/assess returned 429 quota_exceeded");
|
|
375
|
+
if (failOpen) return { kind: "allow", degraded: true, infraReason: "quota_exceeded" };
|
|
376
|
+
return {
|
|
377
|
+
kind: "deny",
|
|
378
|
+
reason: { code: "api_error", agent_instructions: QUOTA_EXCEEDED_INSTRUCTIONS }
|
|
379
|
+
};
|
|
330
380
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
cache.set(cacheKey, { allow, decision: decision ?? void 0, reasons: decisionReasons, raw: data });
|
|
336
|
-
if (allow) {
|
|
337
|
-
return { kind: "allow", data };
|
|
381
|
+
if (err instanceof SdkTimeoutError) {
|
|
382
|
+
console.warn("[gate] /v1/assess timed out:", err.message);
|
|
383
|
+
if (failOpen) return { kind: "allow", degraded: true, infraReason: "network_timeout" };
|
|
384
|
+
return { kind: "deny", reason: { code: "api_error" } };
|
|
338
385
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
386
|
+
const status = err?.status;
|
|
387
|
+
const errName = err instanceof Error ? err.name : "";
|
|
388
|
+
if (status === 429) {
|
|
389
|
+
console.warn("[gate] /v1/assess returned 429 (untyped \u2014 defensive)");
|
|
390
|
+
if (failOpen) return { kind: "allow", degraded: true, infraReason: "quota_exceeded" };
|
|
391
|
+
return {
|
|
392
|
+
kind: "deny",
|
|
393
|
+
reason: { code: "api_error", agent_instructions: QUOTA_EXCEEDED_INSTRUCTIONS }
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
if (errName === "TimeoutError" || errName === "AbortError") {
|
|
397
|
+
console.warn("[gate] /v1/assess timed out (by Error.name):", err instanceof Error ? err.message : err);
|
|
398
|
+
if (failOpen) return { kind: "allow", degraded: true, infraReason: "network_timeout" };
|
|
399
|
+
return { kind: "deny", reason: { code: "api_error" } };
|
|
342
400
|
}
|
|
401
|
+
const errCode = err?.code;
|
|
402
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
403
|
+
const detail = errCode ? `${errCode}: ${msg}` : msg;
|
|
404
|
+
console.warn(`[gate] /v1/assess call failed \u2014 surfacing as api_error: ${detail}`);
|
|
405
|
+
if (failOpen) return { kind: "allow", degraded: true, infraReason: "api_error" };
|
|
406
|
+
return { kind: "deny", reason: { code: "api_error" } };
|
|
407
|
+
}
|
|
408
|
+
const decision = data.decision;
|
|
409
|
+
const decisionReasons = data.decision_reasons ?? [];
|
|
410
|
+
const allow = decision === "allow" || decision == null;
|
|
411
|
+
cache.set(cacheKey, { allow, decision: decision ?? void 0, reasons: decisionReasons, raw: data });
|
|
412
|
+
if (allow) {
|
|
413
|
+
const quota = data.quota;
|
|
343
414
|
return {
|
|
344
|
-
kind: "
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
decision: decision ?? void 0,
|
|
348
|
-
reasons: decisionReasons,
|
|
349
|
-
verify_url: data.verify_url,
|
|
350
|
-
data
|
|
351
|
-
}
|
|
415
|
+
kind: "allow",
|
|
416
|
+
data,
|
|
417
|
+
...quota !== void 0 && { quota }
|
|
352
418
|
};
|
|
353
|
-
} catch (err) {
|
|
354
|
-
console.warn("[gate] /v1/assess call failed \u2014 surfacing as api_error:", err instanceof Error ? err.message : err);
|
|
355
|
-
if (failOpen) return { kind: "allow" };
|
|
356
|
-
return { kind: "deny", reason: { code: "api_error" } };
|
|
357
419
|
}
|
|
420
|
+
if (isFixableDenial(decisionReasons)) {
|
|
421
|
+
const sessionReason = await tryMintSessionDenial(ctx);
|
|
422
|
+
if (sessionReason) return { kind: "deny", reason: sessionReason };
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
kind: "deny",
|
|
426
|
+
reason: {
|
|
427
|
+
code: "wallet_not_trusted",
|
|
428
|
+
decision: decision ?? void 0,
|
|
429
|
+
reasons: decisionReasons,
|
|
430
|
+
verify_url: data.verify_url,
|
|
431
|
+
data
|
|
432
|
+
}
|
|
433
|
+
};
|
|
358
434
|
}
|
|
359
435
|
async function captureWallet(options2) {
|
|
360
436
|
try {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
network: options2.network
|
|
365
|
-
|
|
366
|
-
if (options2.idempotencyKey) body.idempotency_key = options2.idempotencyKey;
|
|
367
|
-
await fetch(`${baseUrl}/v1/credentials/wallets`, {
|
|
368
|
-
method: "POST",
|
|
369
|
-
headers: {
|
|
370
|
-
"X-API-Key": apiKey,
|
|
371
|
-
"Content-Type": "application/json",
|
|
372
|
-
Accept: "application/json",
|
|
373
|
-
"User-Agent": userAgentHeader
|
|
374
|
-
},
|
|
375
|
-
body: JSON.stringify(body),
|
|
376
|
-
signal: AbortSignal.timeout(API_TIMEOUT_MS)
|
|
437
|
+
await sdk.associateWallet({
|
|
438
|
+
operatorToken: options2.operatorToken,
|
|
439
|
+
walletAddress: options2.walletAddress,
|
|
440
|
+
network: options2.network,
|
|
441
|
+
...options2.idempotencyKey ? { idempotencyKey: options2.idempotencyKey } : {}
|
|
377
442
|
});
|
|
378
443
|
} catch (err) {
|
|
379
444
|
console.warn("[agentscore-commerce] captureWallet failed:", err instanceof Error ? err.message : err);
|
|
@@ -398,19 +463,7 @@ function createAgentScoreCore(options) {
|
|
|
398
463
|
return { ok: true, ...extractFromCached(resolveCached.raw) };
|
|
399
464
|
}
|
|
400
465
|
try {
|
|
401
|
-
const
|
|
402
|
-
method: "POST",
|
|
403
|
-
headers: {
|
|
404
|
-
"X-API-Key": apiKey,
|
|
405
|
-
"Content-Type": "application/json",
|
|
406
|
-
Accept: "application/json",
|
|
407
|
-
"User-Agent": userAgentHeader
|
|
408
|
-
},
|
|
409
|
-
body: JSON.stringify({ address: walletAddress }),
|
|
410
|
-
signal: AbortSignal.timeout(API_TIMEOUT_MS)
|
|
411
|
-
});
|
|
412
|
-
if (!response.ok) return { ok: false };
|
|
413
|
-
const data = await response.json();
|
|
466
|
+
const data = await sdk.assess(walletAddress);
|
|
414
467
|
cache.set(`resolve:${wallet}`, { allow: true, raw: data });
|
|
415
468
|
return { ok: true, ...extractFromCached(data) };
|
|
416
469
|
} catch (err) {
|
|
@@ -419,28 +472,37 @@ function createAgentScoreCore(options) {
|
|
|
419
472
|
}
|
|
420
473
|
}
|
|
421
474
|
function reportSignerEvent(kind) {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
}
|
|
439
|
-
} catch {
|
|
475
|
+
void sdk.telemetrySignerMatch({ kind });
|
|
476
|
+
}
|
|
477
|
+
function projectSignerMatch(sm, claimedNorm, signerNorm) {
|
|
478
|
+
const kind = sm.kind;
|
|
479
|
+
if (kind === "pass") {
|
|
480
|
+
return {
|
|
481
|
+
kind: "pass",
|
|
482
|
+
claimedOperator: sm.claimed_operator ?? null,
|
|
483
|
+
signerOperator: sm.signer_operator ?? null
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
if (kind === "wallet_auth_requires_wallet_signing") {
|
|
487
|
+
return {
|
|
488
|
+
kind: "wallet_auth_requires_wallet_signing",
|
|
489
|
+
claimedWallet: sm.claimed_wallet ?? claimedNorm,
|
|
490
|
+
agentInstructions: sm.agent_instructions ?? WALLET_AUTH_REQUIRES_WALLET_SIGNING_INSTRUCTIONS
|
|
491
|
+
};
|
|
440
492
|
}
|
|
493
|
+
const linked = sm.linked_wallets;
|
|
494
|
+
return {
|
|
495
|
+
kind: "wallet_signer_mismatch",
|
|
496
|
+
claimedOperator: sm.claimed_operator ?? null,
|
|
497
|
+
actualSignerOperator: sm.signer_operator ?? null,
|
|
498
|
+
expectedSigner: sm.expected_signer ?? claimedNorm,
|
|
499
|
+
actualSigner: sm.actual_signer ?? signerNorm,
|
|
500
|
+
linkedWallets: Array.isArray(linked) ? linked.filter((w) => typeof w === "string") : [],
|
|
501
|
+
agentInstructions: sm.agent_instructions ?? WALLET_SIGNER_MISMATCH_INSTRUCTIONS
|
|
502
|
+
};
|
|
441
503
|
}
|
|
442
504
|
async function verifyWalletSignerMatch(options2) {
|
|
443
|
-
const { claimedWallet, signer } = options2;
|
|
505
|
+
const { claimedWallet, signer, network } = options2;
|
|
444
506
|
if (!signer) {
|
|
445
507
|
reportSignerEvent("wallet_auth_requires_wallet_signing");
|
|
446
508
|
return {
|
|
@@ -455,6 +517,35 @@ function createAgentScoreCore(options) {
|
|
|
455
517
|
reportSignerEvent("pass");
|
|
456
518
|
return { kind: "pass", claimedOperator: null, signerOperator: null };
|
|
457
519
|
}
|
|
520
|
+
const cachedEntry = cache.get(claimedNorm);
|
|
521
|
+
const cachedMatch = cachedEntry?.signerMatchBySigner?.get(signerNorm);
|
|
522
|
+
if (cachedMatch) {
|
|
523
|
+
return projectSignerMatch(cachedMatch, claimedNorm, signerNorm);
|
|
524
|
+
}
|
|
525
|
+
const inferredNetwork = network ?? (signerNorm.startsWith("0x") ? "evm" : "solana");
|
|
526
|
+
let assessResponse;
|
|
527
|
+
try {
|
|
528
|
+
assessResponse = await sdk.assess(claimedNorm, {
|
|
529
|
+
resolveSigner: { address: signerNorm, network: inferredNetwork }
|
|
530
|
+
});
|
|
531
|
+
} catch (err) {
|
|
532
|
+
console.warn("[gate] verifyWalletSignerMatch assess failed:", err instanceof Error ? err.message : err);
|
|
533
|
+
reportSignerEvent("api_error");
|
|
534
|
+
return { kind: "api_error", claimedWallet: claimedNorm };
|
|
535
|
+
}
|
|
536
|
+
const signerMatch = assessResponse.signer_match;
|
|
537
|
+
if (signerMatch && typeof signerMatch === "object") {
|
|
538
|
+
if (cachedEntry) {
|
|
539
|
+
const map = cachedEntry.signerMatchBySigner ?? /* @__PURE__ */ new Map();
|
|
540
|
+
map.set(signerNorm, signerMatch);
|
|
541
|
+
cachedEntry.signerMatchBySigner = map;
|
|
542
|
+
} else {
|
|
543
|
+
const entry = { allow: true, raw: assessResponse };
|
|
544
|
+
entry.signerMatchBySigner = /* @__PURE__ */ new Map([[signerNorm, signerMatch]]);
|
|
545
|
+
cache.set(claimedNorm, entry);
|
|
546
|
+
}
|
|
547
|
+
return projectSignerMatch(signerMatch, claimedNorm, signerNorm);
|
|
548
|
+
}
|
|
458
549
|
const [claimedResolve, signerResolve] = await Promise.all([
|
|
459
550
|
resolveWalletToOperator(claimedNorm),
|
|
460
551
|
resolveWalletToOperator(signerNorm)
|
|
@@ -476,8 +567,6 @@ function createAgentScoreCore(options) {
|
|
|
476
567
|
actualSignerOperator: signerOperator,
|
|
477
568
|
expectedSigner: claimedNorm,
|
|
478
569
|
actualSigner: signerNorm,
|
|
479
|
-
// Populated from /v1/assess.linked_wallets on the claimed wallet — the full set of
|
|
480
|
-
// wallets the agent CAN sign with to satisfy the claim (same-operator rule).
|
|
481
570
|
linkedWallets: claimedResolve.linkedWallets,
|
|
482
571
|
agentInstructions: WALLET_SIGNER_MISMATCH_INSTRUCTIONS
|
|
483
572
|
};
|