@astrasyncai/verification-gateway 3.11.1 → 4.0.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 (99) hide show
  1. package/dist/adapter-interface/interface.d.mts +2 -2
  2. package/dist/adapter-interface/interface.d.ts +2 -2
  3. package/dist/adapters/express.d.mts +2 -2
  4. package/dist/adapters/express.d.ts +2 -2
  5. package/dist/adapters/express.js +106 -10
  6. package/dist/adapters/express.js.map +1 -1
  7. package/dist/adapters/express.mjs +106 -10
  8. package/dist/adapters/express.mjs.map +1 -1
  9. package/dist/adapters/mcp.d.mts +1 -1
  10. package/dist/adapters/mcp.d.ts +1 -1
  11. package/dist/adapters/mcp.js +111 -10
  12. package/dist/adapters/mcp.js.map +1 -1
  13. package/dist/adapters/mcp.mjs +111 -10
  14. package/dist/adapters/mcp.mjs.map +1 -1
  15. package/dist/adapters/nextjs.d.mts +2 -2
  16. package/dist/adapters/nextjs.d.ts +2 -2
  17. package/dist/adapters/nextjs.js +107 -10
  18. package/dist/adapters/nextjs.js.map +1 -1
  19. package/dist/adapters/nextjs.mjs +107 -10
  20. package/dist/adapters/nextjs.mjs.map +1 -1
  21. package/dist/adapters/sdk.d.mts +2 -2
  22. package/dist/adapters/sdk.d.ts +2 -2
  23. package/dist/adapters/sdk.js +92 -10
  24. package/dist/adapters/sdk.js.map +1 -1
  25. package/dist/adapters/sdk.mjs +92 -10
  26. package/dist/adapters/sdk.mjs.map +1 -1
  27. package/dist/agent/index.d.mts +2 -2
  28. package/dist/agent/index.d.ts +2 -2
  29. package/dist/bin/astrasync-claude-hook.js +92 -10
  30. package/dist/bin/astrasync-codex-hook.js +92 -10
  31. package/dist/bin/astrasync-guard.js +92 -10
  32. package/dist/bin/astrasync.js +93 -10
  33. package/dist/browser/background.js +92 -10
  34. package/dist/browser/background.js.map +1 -1
  35. package/dist/browser/background.mjs +92 -10
  36. package/dist/browser/background.mjs.map +1 -1
  37. package/dist/browser/browser-adapter.d.mts +2 -2
  38. package/dist/browser/browser-adapter.d.ts +2 -2
  39. package/dist/claude-code/claude-code-adapter.d.mts +2 -2
  40. package/dist/claude-code/claude-code-adapter.d.ts +2 -2
  41. package/dist/cli/index.d.mts +2 -2
  42. package/dist/cli/index.d.ts +2 -2
  43. package/dist/codex/index.d.mts +2 -2
  44. package/dist/codex/index.d.ts +2 -2
  45. package/dist/codex/index.js +92 -10
  46. package/dist/codex/index.js.map +1 -1
  47. package/dist/codex/index.mjs +92 -10
  48. package/dist/codex/index.mjs.map +1 -1
  49. package/dist/cursor/cursor-adapter.d.mts +2 -2
  50. package/dist/cursor/cursor-adapter.d.ts +2 -2
  51. package/dist/cursor/extension.d.mts +2 -2
  52. package/dist/cursor/extension.d.ts +2 -2
  53. package/dist/cursor/extension.js +92 -10
  54. package/dist/cursor/extension.js.map +1 -1
  55. package/dist/cursor/extension.mjs +92 -10
  56. package/dist/cursor/extension.mjs.map +1 -1
  57. package/dist/edge-config.d.mts +1 -1
  58. package/dist/edge-config.d.ts +1 -1
  59. package/dist/{express-DCXa1oqj.d.mts → express-BZajRs4i.d.mts} +1 -1
  60. package/dist/{express-ClNtDhhc.d.ts → express-CjCUB1k7.d.ts} +1 -1
  61. package/dist/gateway/gateway.d.mts +2 -2
  62. package/dist/gateway/gateway.d.ts +2 -2
  63. package/dist/gateway/gateway.js +92 -10
  64. package/dist/gateway/gateway.js.map +1 -1
  65. package/dist/gateway/gateway.mjs +92 -10
  66. package/dist/gateway/gateway.mjs.map +1 -1
  67. package/dist/git-trigger/git-hooks.d.mts +2 -2
  68. package/dist/git-trigger/git-hooks.d.ts +2 -2
  69. package/dist/{index-BrNKDmiR.d.mts → index-B0cyMZDL.d.mts} +1 -1
  70. package/dist/{index-Dr_XiN65.d.mts → index-BRYteOqO.d.mts} +1 -1
  71. package/dist/{index-CfHurO1P.d.ts → index-BZQdVqNw.d.ts} +1 -1
  72. package/dist/{index-D2ztkumS.d.ts → index-adgKhujR.d.ts} +1 -1
  73. package/dist/index.d.mts +9 -9
  74. package/dist/index.d.ts +9 -9
  75. package/dist/index.js +160 -10
  76. package/dist/index.js.map +1 -1
  77. package/dist/index.mjs +159 -10
  78. package/dist/index.mjs.map +1 -1
  79. package/dist/local-evaluator/evaluator.d.mts +2 -2
  80. package/dist/local-evaluator/evaluator.d.ts +2 -2
  81. package/dist/{nextjs-CEBVBsdi.d.ts → nextjs-D_iFf5k-.d.ts} +1 -1
  82. package/dist/{nextjs-DcQIk6Zj.d.mts → nextjs-DeUTnOBd.d.mts} +1 -1
  83. package/dist/{sdk-H0JCLW2y.d.ts → sdk-BM3mwEAq.d.ts} +1 -1
  84. package/dist/{sdk-CZkbSOeO.d.mts → sdk-_il1Q41f.d.mts} +1 -1
  85. package/dist/transport/index.d.mts +2 -2
  86. package/dist/transport/index.d.ts +2 -2
  87. package/dist/{types-sy3tq8lp.d.mts → types-BcDf9Kk0.d.mts} +118 -1
  88. package/dist/{types-DhFPiZ3t.d.ts → types-CHKCgDFC.d.ts} +1 -1
  89. package/dist/{types-cQVCMeNV.d.mts → types-DfnP7JiM.d.mts} +1 -1
  90. package/dist/{types-9tns4wyl.d.ts → types-fTQD448H.d.ts} +118 -1
  91. package/dist/ui/index.d.mts +1 -1
  92. package/dist/ui/index.d.ts +1 -1
  93. package/dist/verify.d.mts +15 -2
  94. package/dist/verify.d.ts +15 -2
  95. package/dist/verify.js +117 -10
  96. package/dist/verify.js.map +1 -1
  97. package/dist/verify.mjs +115 -10
  98. package/dist/verify.mjs.map +1 -1
  99. package/package.json +1 -1
@@ -5000,7 +5000,7 @@ var SDK_VERSION;
5000
5000
  var init_version = __esm({
5001
5001
  "src/version.ts"() {
5002
5002
  "use strict";
5003
- SDK_VERSION = "3.11.1";
5003
+ SDK_VERSION = "4.0.0";
5004
5004
  }
5005
5005
  });
5006
5006
 
@@ -5018,7 +5018,7 @@ var init_well_known = __esm({
5018
5018
  });
5019
5019
 
5020
5020
  // src/verify.ts
5021
- async function performInitCheck(apiBaseUrl, debug, strictInit) {
5021
+ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
5022
5022
  initCheckPerformed = true;
5023
5023
  try {
5024
5024
  const probeUrl = `${apiBaseUrl}/agents/verify-access`;
@@ -5035,6 +5035,28 @@ async function performInitCheck(apiBaseUrl, debug, strictInit) {
5035
5035
  `[VerificationGateway] init check passed for ${apiBaseUrl} (content-type: ${contentType})`
5036
5036
  );
5037
5037
  }
5038
+ if (apiKey) {
5039
+ const selfCheck = await fetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
5040
+ headers: { Authorization: `Bearer ${apiKey}` }
5041
+ });
5042
+ if (selfCheck.status === 401) {
5043
+ const body = await selfCheck.json().catch(() => ({}));
5044
+ const message = `[VerificationGateway] the configured AstraSync API key was REJECTED at startup (${body.code ?? "api_key_invalid"}: ${body.message ?? "invalid key"}). Every inbound agent will be turned away until the key is rotated` + (body.remediation?.manageUrl ? ` at ${body.remediation.manageUrl}` : "") + `.`;
5045
+ if (strictInit) {
5046
+ throw new Error(`${message} (strictInit=true)`);
5047
+ }
5048
+ console.error(message);
5049
+ } else if (selfCheck.ok) {
5050
+ const body = await selfCheck.json().catch(() => ({}));
5051
+ if (typeof body.daysToExpiry === "number" && body.daysToExpiry <= 7) {
5052
+ console.warn(
5053
+ `[VerificationGateway] the configured AstraSync API key expires in ${body.daysToExpiry} day(s) \u2014 rotate it at Settings \u2192 Developer Tools before integrations start failing.`
5054
+ );
5055
+ } else if (debug) {
5056
+ console.log("[VerificationGateway] API key self-check passed");
5057
+ }
5058
+ }
5059
+ }
5038
5060
  } catch (err) {
5039
5061
  if (strictInit) {
5040
5062
  throw err;
@@ -5110,8 +5132,17 @@ function cacheResult(request2, result, configuredTtl, counterpartyId) {
5110
5132
  function createGuidanceResponse(_config, reason, options = {}) {
5111
5133
  const source = options.source ?? "no_credentials";
5112
5134
  const isApiError = source === "api_error";
5135
+ const isMisconfig = source === "merchant_misconfig";
5113
5136
  const urls = options.urls;
5114
- const guidance = isApiError ? {
5137
+ const guidance = isMisconfig ? {
5138
+ message: "This site's AstraSync integration is misconfigured (its API key was revoked, expired, or rotated). Your identity and the AstraSync platform are fine \u2014 contact the site operator. Retrying will not help.",
5139
+ registrationUrl: urls?.registrationUrl ?? "",
5140
+ documentationUrl: options.remediation?.docsUrl ?? urls?.documentationUrl ?? "",
5141
+ steps: [
5142
+ "Report the failure to the site operator \u2014 their AstraSync API key needs rotating",
5143
+ "Do not retry: the failure is deterministic until the operator updates their key"
5144
+ ]
5145
+ } : isApiError ? {
5115
5146
  message: "Verification is temporarily unavailable. Retry with exponential backoff; if the issue persists, contact support with the correlationId.",
5116
5147
  registrationUrl: urls?.registrationUrl ?? "",
5117
5148
  documentationUrl: urls?.documentationUrl ?? "",
@@ -5149,14 +5180,24 @@ function createGuidanceResponse(_config, reason, options = {}) {
5149
5180
  // partners (and their custom onDenied handlers) can branch on
5150
5181
  // dimension. Without this, the synthesised stub was indistinguishable
5151
5182
  // from a real policy deny.
5152
- failures: isApiError ? [
5183
+ failures: isMisconfig ? [
5184
+ {
5185
+ dimension: "verify_access.merchant_misconfigured",
5186
+ message: reason ?? "The integration API key was rejected by AstraSync",
5187
+ guidance: guidance.message
5188
+ }
5189
+ ] : isApiError ? [
5153
5190
  {
5154
5191
  dimension: "verify_access.api_error",
5155
5192
  message: reason ?? "Verification temporarily unavailable",
5156
5193
  guidance: guidance.message
5157
5194
  }
5158
5195
  ] : void 0,
5196
+ // 4.0.0: typed marker so adapters render the misconfig envelope (503 +
5197
+ // MERCHANT_VERIFICATION_MISCONFIGURED) without string-matching.
5198
+ ...isMisconfig && { misconfigured: true },
5159
5199
  correlationId: options.correlationId,
5200
+ attemptId: options.attemptId,
5160
5201
  verifiedAt: /* @__PURE__ */ new Date()
5161
5202
  };
5162
5203
  }
@@ -5186,6 +5227,8 @@ async function callVerifyAccessAPI(config, request2) {
5186
5227
  body.runtimeChallengeOptions = requestData.runtimeChallengeOptions;
5187
5228
  if (requestData.invocationProtocol) body.invocationProtocol = requestData.invocationProtocol;
5188
5229
  if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
5230
+ if (requestData.attemptId) body.attemptId = requestData.attemptId;
5231
+ if (requestData.considerationSet) body.considerationSet = requestData.considerationSet;
5189
5232
  body.sdkVersion = SDK_VERSION;
5190
5233
  if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {
5191
5234
  const meta = {
@@ -5207,12 +5250,16 @@ async function callVerifyAccessAPI(config, request2) {
5207
5250
  const response = await fetch(`${config.apiBaseUrl}/agents/verify-access`, {
5208
5251
  method: "POST",
5209
5252
  headers,
5210
- body: JSON.stringify(body)
5253
+ body: JSON.stringify(body),
5254
+ signal: AbortSignal.timeout(config.verifyTimeoutMs ?? 1e4)
5211
5255
  });
5212
5256
  const data = await response.json();
5213
5257
  if (response.status === 410) {
5214
5258
  return {
5215
5259
  success: true,
5260
+ // 3.12.0 (Trust Record): keep the server-echoed attempt handle even on
5261
+ // the synthesised 410 deny shape (echoed on every branch).
5262
+ attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0,
5216
5263
  access: {
5217
5264
  allowed: false,
5218
5265
  accessLevel: "none",
@@ -5228,10 +5275,22 @@ async function callVerifyAccessAPI(config, request2) {
5228
5275
  };
5229
5276
  }
5230
5277
  if (!response.ok) {
5278
+ if (response.status === 401) {
5279
+ return {
5280
+ success: false,
5281
+ error: data.message || data.error || "Integration API key rejected",
5282
+ misconfig: true,
5283
+ misconfigCode: typeof data?.code === "string" ? data.code : void 0,
5284
+ remediation: data?.remediation && typeof data.remediation === "object" ? data.remediation : void 0,
5285
+ correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
5286
+ attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
5287
+ };
5288
+ }
5231
5289
  return {
5232
5290
  success: false,
5233
5291
  error: data.message || data.error || `API returned ${response.status}`,
5234
- correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0
5292
+ correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
5293
+ attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
5235
5294
  };
5236
5295
  }
5237
5296
  return data;
@@ -5248,9 +5307,19 @@ async function verify(config, request2, options) {
5248
5307
  const urls = mergedConfig.apiBaseUrl ? getCachedWellKnownUrls(mergedConfig.apiBaseUrl) : void 0;
5249
5308
  if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {
5250
5309
  if (mergedConfig.strictInit) {
5251
- await performInitCheck(mergedConfig.apiBaseUrl, mergedConfig.debug, true);
5310
+ await performInitCheck(
5311
+ mergedConfig.apiBaseUrl,
5312
+ mergedConfig.debug,
5313
+ true,
5314
+ mergedConfig.apiKey
5315
+ );
5252
5316
  } else {
5253
- void performInitCheck(mergedConfig.apiBaseUrl, mergedConfig.debug, false);
5317
+ void performInitCheck(
5318
+ mergedConfig.apiBaseUrl,
5319
+ mergedConfig.debug,
5320
+ false,
5321
+ mergedConfig.apiKey
5322
+ );
5254
5323
  }
5255
5324
  }
5256
5325
  if (!deprecationWarningShown && (config.minTrustScore !== void 0 || config.minTrustScoreForFull !== void 0)) {
@@ -5265,7 +5334,13 @@ async function verify(config, request2, options) {
5265
5334
  if (mergedConfig.debug) {
5266
5335
  console.log("[VerificationGateway] Returning cached result");
5267
5336
  }
5268
- return cached;
5337
+ const hit = { ...cached };
5338
+ if (request2.attemptId) {
5339
+ hit.attemptId = request2.attemptId;
5340
+ } else {
5341
+ delete hit.attemptId;
5342
+ }
5343
+ return hit;
5269
5344
  }
5270
5345
  }
5271
5346
  const enrichedRequest = { ...request2 };
@@ -5283,8 +5358,10 @@ async function verify(config, request2, options) {
5283
5358
  const serverDenialReasons = Array.isArray(rawServerDenialReasons) && rawServerDenialReasons.length > 0 ? rawServerDenialReasons : void 0;
5284
5359
  if (!apiResponse.success) {
5285
5360
  return createGuidanceResponse(mergedConfig, apiResponse.error, {
5286
- source: "api_error",
5361
+ source: apiResponse.misconfig ? "merchant_misconfig" : "api_error",
5287
5362
  correlationId: apiResponse.correlationId,
5363
+ attemptId: apiResponse.attemptId,
5364
+ remediation: apiResponse.remediation,
5288
5365
  urls
5289
5366
  });
5290
5367
  }
@@ -5320,6 +5397,9 @@ async function verify(config, request2, options) {
5320
5397
  // Anonymous traffic has no session → correlationId is the per-attempt
5321
5398
  // linking key (the sessionId-equivalent for anonymous callers).
5322
5399
  correlationId: apiResponse.correlationId,
5400
+ // 3.12.0 (Trust Record): server echoes the attempt-chain handle on
5401
+ // every branch — denials included (the funnel outlives one deny).
5402
+ attemptId: apiResponse.attemptId,
5323
5403
  recommendation: apiResponse.recommendation,
5324
5404
  recommendationReasons: apiResponse.recommendationReasons,
5325
5405
  stepUpApproval: apiResponse.stepUpApproval,
@@ -5371,6 +5451,8 @@ async function verify(config, request2, options) {
5371
5451
  // v2.3.10 (defect #34, round-4): anonymous responses surface correlationId
5372
5452
  // (no session row exists for unverified callers).
5373
5453
  correlationId: apiResponse.correlationId,
5454
+ // 3.12.0 (Trust Record): server-echoed attempt-chain handle.
5455
+ attemptId: apiResponse.attemptId,
5374
5456
  runtimeChallenge: apiResponse.runtimeChallenge,
5375
5457
  tokenGuidance: apiResponse.tokenGuidance,
5376
5458
  recommendation: apiResponse.recommendation,
@@ -5949,6 +6031,7 @@ var init_src = __esm({
5949
6031
  init_edge_config();
5950
6032
  init_platform_signatures();
5951
6033
  init_verify();
6034
+ init_verify();
5952
6035
  init_metadata_capture();
5953
6036
  init_version();
5954
6037
  }
@@ -3938,7 +3938,7 @@ function getTrustLevel(score) {
3938
3938
  }
3939
3939
 
3940
3940
  // src/version.ts
3941
- var SDK_VERSION = "3.11.1";
3941
+ var SDK_VERSION = "4.0.0";
3942
3942
 
3943
3943
  // src/well-known.ts
3944
3944
  var CACHE_TTL_MS = 60 * 60 * 1e3;
@@ -3964,7 +3964,7 @@ var DEFAULT_CONFIG = {
3964
3964
  };
3965
3965
  var initCheckPerformed = false;
3966
3966
  var deprecationWarningShown = false;
3967
- async function performInitCheck(apiBaseUrl, debug, strictInit) {
3967
+ async function performInitCheck(apiBaseUrl, debug, strictInit, apiKey) {
3968
3968
  initCheckPerformed = true;
3969
3969
  try {
3970
3970
  const probeUrl = `${apiBaseUrl}/agents/verify-access`;
@@ -3981,6 +3981,28 @@ async function performInitCheck(apiBaseUrl, debug, strictInit) {
3981
3981
  `[VerificationGateway] init check passed for ${apiBaseUrl} (content-type: ${contentType})`
3982
3982
  );
3983
3983
  }
3984
+ if (apiKey) {
3985
+ const selfCheck = await fetch(`${apiBaseUrl}/agents/verify-access/self-check`, {
3986
+ headers: { Authorization: `Bearer ${apiKey}` }
3987
+ });
3988
+ if (selfCheck.status === 401) {
3989
+ const body = await selfCheck.json().catch(() => ({}));
3990
+ const message = `[VerificationGateway] the configured AstraSync API key was REJECTED at startup (${body.code ?? "api_key_invalid"}: ${body.message ?? "invalid key"}). Every inbound agent will be turned away until the key is rotated` + (body.remediation?.manageUrl ? ` at ${body.remediation.manageUrl}` : "") + `.`;
3991
+ if (strictInit) {
3992
+ throw new Error(`${message} (strictInit=true)`);
3993
+ }
3994
+ console.error(message);
3995
+ } else if (selfCheck.ok) {
3996
+ const body = await selfCheck.json().catch(() => ({}));
3997
+ if (typeof body.daysToExpiry === "number" && body.daysToExpiry <= 7) {
3998
+ console.warn(
3999
+ `[VerificationGateway] the configured AstraSync API key expires in ${body.daysToExpiry} day(s) \u2014 rotate it at Settings \u2192 Developer Tools before integrations start failing.`
4000
+ );
4001
+ } else if (debug) {
4002
+ console.log("[VerificationGateway] API key self-check passed");
4003
+ }
4004
+ }
4005
+ }
3984
4006
  } catch (err) {
3985
4007
  if (strictInit) {
3986
4008
  throw err;
@@ -4058,8 +4080,17 @@ function cacheResult(request, result, configuredTtl, counterpartyId) {
4058
4080
  function createGuidanceResponse(_config, reason, options = {}) {
4059
4081
  const source = options.source ?? "no_credentials";
4060
4082
  const isApiError = source === "api_error";
4083
+ const isMisconfig = source === "merchant_misconfig";
4061
4084
  const urls = options.urls;
4062
- const guidance = isApiError ? {
4085
+ const guidance = isMisconfig ? {
4086
+ message: "This site's AstraSync integration is misconfigured (its API key was revoked, expired, or rotated). Your identity and the AstraSync platform are fine \u2014 contact the site operator. Retrying will not help.",
4087
+ registrationUrl: urls?.registrationUrl ?? "",
4088
+ documentationUrl: options.remediation?.docsUrl ?? urls?.documentationUrl ?? "",
4089
+ steps: [
4090
+ "Report the failure to the site operator \u2014 their AstraSync API key needs rotating",
4091
+ "Do not retry: the failure is deterministic until the operator updates their key"
4092
+ ]
4093
+ } : isApiError ? {
4063
4094
  message: "Verification is temporarily unavailable. Retry with exponential backoff; if the issue persists, contact support with the correlationId.",
4064
4095
  registrationUrl: urls?.registrationUrl ?? "",
4065
4096
  documentationUrl: urls?.documentationUrl ?? "",
@@ -4097,14 +4128,24 @@ function createGuidanceResponse(_config, reason, options = {}) {
4097
4128
  // partners (and their custom onDenied handlers) can branch on
4098
4129
  // dimension. Without this, the synthesised stub was indistinguishable
4099
4130
  // from a real policy deny.
4100
- failures: isApiError ? [
4131
+ failures: isMisconfig ? [
4132
+ {
4133
+ dimension: "verify_access.merchant_misconfigured",
4134
+ message: reason ?? "The integration API key was rejected by AstraSync",
4135
+ guidance: guidance.message
4136
+ }
4137
+ ] : isApiError ? [
4101
4138
  {
4102
4139
  dimension: "verify_access.api_error",
4103
4140
  message: reason ?? "Verification temporarily unavailable",
4104
4141
  guidance: guidance.message
4105
4142
  }
4106
4143
  ] : void 0,
4144
+ // 4.0.0: typed marker so adapters render the misconfig envelope (503 +
4145
+ // MERCHANT_VERIFICATION_MISCONFIGURED) without string-matching.
4146
+ ...isMisconfig && { misconfigured: true },
4107
4147
  correlationId: options.correlationId,
4148
+ attemptId: options.attemptId,
4108
4149
  verifiedAt: /* @__PURE__ */ new Date()
4109
4150
  };
4110
4151
  }
@@ -4134,6 +4175,8 @@ async function callVerifyAccessAPI(config, request) {
4134
4175
  body.runtimeChallengeOptions = requestData.runtimeChallengeOptions;
4135
4176
  if (requestData.invocationProtocol) body.invocationProtocol = requestData.invocationProtocol;
4136
4177
  if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
4178
+ if (requestData.attemptId) body.attemptId = requestData.attemptId;
4179
+ if (requestData.considerationSet) body.considerationSet = requestData.considerationSet;
4137
4180
  body.sdkVersion = SDK_VERSION;
4138
4181
  if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {
4139
4182
  const meta = {
@@ -4155,12 +4198,16 @@ async function callVerifyAccessAPI(config, request) {
4155
4198
  const response = await fetch(`${config.apiBaseUrl}/agents/verify-access`, {
4156
4199
  method: "POST",
4157
4200
  headers,
4158
- body: JSON.stringify(body)
4201
+ body: JSON.stringify(body),
4202
+ signal: AbortSignal.timeout(config.verifyTimeoutMs ?? 1e4)
4159
4203
  });
4160
4204
  const data = await response.json();
4161
4205
  if (response.status === 410) {
4162
4206
  return {
4163
4207
  success: true,
4208
+ // 3.12.0 (Trust Record): keep the server-echoed attempt handle even on
4209
+ // the synthesised 410 deny shape (echoed on every branch).
4210
+ attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0,
4164
4211
  access: {
4165
4212
  allowed: false,
4166
4213
  accessLevel: "none",
@@ -4176,10 +4223,22 @@ async function callVerifyAccessAPI(config, request) {
4176
4223
  };
4177
4224
  }
4178
4225
  if (!response.ok) {
4226
+ if (response.status === 401) {
4227
+ return {
4228
+ success: false,
4229
+ error: data.message || data.error || "Integration API key rejected",
4230
+ misconfig: true,
4231
+ misconfigCode: typeof data?.code === "string" ? data.code : void 0,
4232
+ remediation: data?.remediation && typeof data.remediation === "object" ? data.remediation : void 0,
4233
+ correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
4234
+ attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
4235
+ };
4236
+ }
4179
4237
  return {
4180
4238
  success: false,
4181
4239
  error: data.message || data.error || `API returned ${response.status}`,
4182
- correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0
4240
+ correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
4241
+ attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
4183
4242
  };
4184
4243
  }
4185
4244
  return data;
@@ -4196,9 +4255,19 @@ async function verify(config, request, options) {
4196
4255
  const urls = mergedConfig.apiBaseUrl ? getCachedWellKnownUrls(mergedConfig.apiBaseUrl) : void 0;
4197
4256
  if (!initCheckPerformed && !mergedConfig.disableInitChecks && mergedConfig.apiBaseUrl) {
4198
4257
  if (mergedConfig.strictInit) {
4199
- await performInitCheck(mergedConfig.apiBaseUrl, mergedConfig.debug, true);
4258
+ await performInitCheck(
4259
+ mergedConfig.apiBaseUrl,
4260
+ mergedConfig.debug,
4261
+ true,
4262
+ mergedConfig.apiKey
4263
+ );
4200
4264
  } else {
4201
- void performInitCheck(mergedConfig.apiBaseUrl, mergedConfig.debug, false);
4265
+ void performInitCheck(
4266
+ mergedConfig.apiBaseUrl,
4267
+ mergedConfig.debug,
4268
+ false,
4269
+ mergedConfig.apiKey
4270
+ );
4202
4271
  }
4203
4272
  }
4204
4273
  if (!deprecationWarningShown && (config.minTrustScore !== void 0 || config.minTrustScoreForFull !== void 0)) {
@@ -4213,7 +4282,13 @@ async function verify(config, request, options) {
4213
4282
  if (mergedConfig.debug) {
4214
4283
  console.log("[VerificationGateway] Returning cached result");
4215
4284
  }
4216
- return cached;
4285
+ const hit = { ...cached };
4286
+ if (request.attemptId) {
4287
+ hit.attemptId = request.attemptId;
4288
+ } else {
4289
+ delete hit.attemptId;
4290
+ }
4291
+ return hit;
4217
4292
  }
4218
4293
  }
4219
4294
  const enrichedRequest = { ...request };
@@ -4231,8 +4306,10 @@ async function verify(config, request, options) {
4231
4306
  const serverDenialReasons = Array.isArray(rawServerDenialReasons) && rawServerDenialReasons.length > 0 ? rawServerDenialReasons : void 0;
4232
4307
  if (!apiResponse.success) {
4233
4308
  return createGuidanceResponse(mergedConfig, apiResponse.error, {
4234
- source: "api_error",
4309
+ source: apiResponse.misconfig ? "merchant_misconfig" : "api_error",
4235
4310
  correlationId: apiResponse.correlationId,
4311
+ attemptId: apiResponse.attemptId,
4312
+ remediation: apiResponse.remediation,
4236
4313
  urls
4237
4314
  });
4238
4315
  }
@@ -4268,6 +4345,9 @@ async function verify(config, request, options) {
4268
4345
  // Anonymous traffic has no session → correlationId is the per-attempt
4269
4346
  // linking key (the sessionId-equivalent for anonymous callers).
4270
4347
  correlationId: apiResponse.correlationId,
4348
+ // 3.12.0 (Trust Record): server echoes the attempt-chain handle on
4349
+ // every branch — denials included (the funnel outlives one deny).
4350
+ attemptId: apiResponse.attemptId,
4271
4351
  recommendation: apiResponse.recommendation,
4272
4352
  recommendationReasons: apiResponse.recommendationReasons,
4273
4353
  stepUpApproval: apiResponse.stepUpApproval,
@@ -4319,6 +4399,8 @@ async function verify(config, request, options) {
4319
4399
  // v2.3.10 (defect #34, round-4): anonymous responses surface correlationId
4320
4400
  // (no session row exists for unverified callers).
4321
4401
  correlationId: apiResponse.correlationId,
4402
+ // 3.12.0 (Trust Record): server-echoed attempt-chain handle.
4403
+ attemptId: apiResponse.attemptId,
4322
4404
  runtimeChallenge: apiResponse.runtimeChallenge,
4323
4405
  tokenGuidance: apiResponse.tokenGuidance,
4324
4406
  recommendation: apiResponse.recommendation,