@astrasyncai/verification-gateway 5.18.1 → 5.19.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 (113) hide show
  1. package/CHANGELOG.md +149 -0
  2. package/README.md +168 -4
  3. package/dist/adapters/express.d.mts +9 -1
  4. package/dist/adapters/express.d.ts +9 -1
  5. package/dist/adapters/express.js +96 -37
  6. package/dist/adapters/express.js.map +1 -1
  7. package/dist/adapters/express.mjs +96 -37
  8. package/dist/adapters/express.mjs.map +1 -1
  9. package/dist/adapters/http-pdlss.d.mts +7 -1
  10. package/dist/adapters/http-pdlss.d.ts +7 -1
  11. package/dist/adapters/http-pdlss.js +18 -2
  12. package/dist/adapters/http-pdlss.js.map +1 -1
  13. package/dist/adapters/http-pdlss.mjs +16 -1
  14. package/dist/adapters/http-pdlss.mjs.map +1 -1
  15. package/dist/adapters/kit-express.d.mts +93 -1
  16. package/dist/adapters/kit-express.d.ts +93 -1
  17. package/dist/adapters/kit-express.js +181 -2
  18. package/dist/adapters/kit-express.js.map +1 -1
  19. package/dist/adapters/kit-express.mjs +180 -2
  20. package/dist/adapters/kit-express.mjs.map +1 -1
  21. package/dist/adapters/mcp.d.mts +80 -9
  22. package/dist/adapters/mcp.d.ts +80 -9
  23. package/dist/adapters/mcp.js +153 -5
  24. package/dist/adapters/mcp.js.map +1 -1
  25. package/dist/adapters/mcp.mjs +151 -5
  26. package/dist/adapters/mcp.mjs.map +1 -1
  27. package/dist/adapters/nextjs.d.mts +1 -1
  28. package/dist/adapters/nextjs.d.ts +1 -1
  29. package/dist/adapters/nextjs.js +41 -4
  30. package/dist/adapters/nextjs.js.map +1 -1
  31. package/dist/adapters/nextjs.mjs +41 -4
  32. package/dist/adapters/nextjs.mjs.map +1 -1
  33. package/dist/adapters/sdk.d.mts +44 -1
  34. package/dist/adapters/sdk.d.ts +44 -1
  35. package/dist/adapters/sdk.js +76 -2
  36. package/dist/adapters/sdk.js.map +1 -1
  37. package/dist/adapters/sdk.mjs +76 -2
  38. package/dist/adapters/sdk.mjs.map +1 -1
  39. package/dist/agent/index.js +1 -1
  40. package/dist/agent/index.js.map +1 -1
  41. package/dist/agent/index.mjs +1 -1
  42. package/dist/agent/index.mjs.map +1 -1
  43. package/dist/bin/astrasync-claude-hook.js +46 -6
  44. package/dist/bin/astrasync-codex-hook.js +46 -6
  45. package/dist/bin/astrasync-guard.js +46 -6
  46. package/dist/bin/astrasync.js +77 -5
  47. package/dist/browser/background.js +46 -6
  48. package/dist/browser/background.js.map +1 -1
  49. package/dist/browser/background.mjs +46 -6
  50. package/dist/browser/background.mjs.map +1 -1
  51. package/dist/cli/index.js +1 -1
  52. package/dist/cli/index.js.map +1 -1
  53. package/dist/cli/index.mjs +1 -1
  54. package/dist/cli/index.mjs.map +1 -1
  55. package/dist/codex/index.js +46 -6
  56. package/dist/codex/index.js.map +1 -1
  57. package/dist/codex/index.mjs +46 -6
  58. package/dist/codex/index.mjs.map +1 -1
  59. package/dist/cursor/extension.js +46 -6
  60. package/dist/cursor/extension.js.map +1 -1
  61. package/dist/cursor/extension.mjs +46 -6
  62. package/dist/cursor/extension.mjs.map +1 -1
  63. package/dist/edge-config.d.mts +1 -1
  64. package/dist/edge-config.d.ts +1 -1
  65. package/dist/edge-config.js +1 -1
  66. package/dist/edge-config.js.map +1 -1
  67. package/dist/edge-config.mjs +1 -1
  68. package/dist/edge-config.mjs.map +1 -1
  69. package/dist/edge-core/index.d.mts +1 -1
  70. package/dist/edge-core/index.d.ts +1 -1
  71. package/dist/edge-core/index.js +21 -2
  72. package/dist/edge-core/index.js.map +1 -1
  73. package/dist/edge-core/index.mjs +21 -2
  74. package/dist/edge-core/index.mjs.map +1 -1
  75. package/dist/gateway/gateway.js +46 -6
  76. package/dist/gateway/gateway.js.map +1 -1
  77. package/dist/gateway/gateway.mjs +46 -6
  78. package/dist/gateway/gateway.mjs.map +1 -1
  79. package/dist/git-trigger/git-hooks.d.mts +1 -1
  80. package/dist/git-trigger/git-hooks.d.ts +1 -1
  81. package/dist/index.d.mts +541 -29
  82. package/dist/index.d.ts +541 -29
  83. package/dist/index.js +1171 -183
  84. package/dist/index.js.map +1 -1
  85. package/dist/index.mjs +1157 -173
  86. package/dist/index.mjs.map +1 -1
  87. package/dist/registration/index.js +1 -1
  88. package/dist/registration/index.js.map +1 -1
  89. package/dist/registration/index.mjs +1 -1
  90. package/dist/registration/index.mjs.map +1 -1
  91. package/dist/transport/index.js +3 -2
  92. package/dist/transport/index.js.map +1 -1
  93. package/dist/transport/index.mjs +3 -2
  94. package/dist/transport/index.mjs.map +1 -1
  95. package/dist/{types-3ObBpsnf.d.mts → types-BoGjyon0.d.mts} +81 -2
  96. package/dist/{types-Cns4l8gX.d.mts → types-DqWPkiYl.d.mts} +90 -12
  97. package/dist/{types-Cns4l8gX.d.ts → types-DqWPkiYl.d.ts} +90 -12
  98. package/dist/{types-BoxZEYPS.d.ts → types-eWyZuz-3.d.ts} +81 -2
  99. package/dist/ui/index.d.mts +1 -1
  100. package/dist/ui/index.d.ts +1 -1
  101. package/dist/verify.d.mts +23 -2
  102. package/dist/verify.d.ts +23 -2
  103. package/dist/verify.js +23 -4
  104. package/dist/verify.js.map +1 -1
  105. package/dist/verify.mjs +23 -4
  106. package/dist/verify.mjs.map +1 -1
  107. package/dist/webhooks.d.mts +16 -3
  108. package/dist/webhooks.d.ts +16 -3
  109. package/dist/webhooks.js +24 -13
  110. package/dist/webhooks.js.map +1 -1
  111. package/dist/webhooks.mjs +24 -13
  112. package/dist/webhooks.mjs.map +1 -1
  113. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { Request, Response, RequestHandler } from 'express';
2
- import { d as VerificationResult, G as GatewayConfig } from '../types-Cns4l8gX.js';
2
+ import { e as VerificationResult, G as GatewayConfig } from '../types-DqWPkiYl.js';
3
3
 
4
4
  /**
5
5
  * X-Astra-Verified-Hop — cross-hop verify-access dedupe marker.
@@ -378,23 +378,94 @@ interface McpMiddlewareOptions extends GatewayConfig {
378
378
  recordDecisions?: boolean;
379
379
  /** Forward runtime challenge (default `true`). */
380
380
  enableRuntimeChallenge?: boolean;
381
+ /**
382
+ * 5.19.0: how a dashboard tool rule combines with a code `toolGates`
383
+ * entry for the same tool. With `apiKey` + `counterpartyId` set, the
384
+ * middleware fetches the endpoint's tool rules (`{tool, purpose, action,
385
+ * resource?, observe?}`, edited in the AstraSync dashboard; refreshed every
386
+ * `routesRefreshMs`) and turns each into a gate for that tool.
387
+ *
388
+ * - `'code'` (default): a dashboard rule can TIGHTEN a code gate but never
389
+ * loosen it. For a tool gated in code (object form), a dashboard
390
+ * `observe` rule, or one with no purpose, is ignored; a dashboard rule
391
+ * whose purpose or action differs is evaluated IN ADDITION — both must
392
+ * pass. Tools with no code gate (or a code `'observe'`) follow the
393
+ * dashboard rule.
394
+ * - `'dashboard'`: the dashboard rule wins outright for every tool it
395
+ * names (including `observe`).
396
+ *
397
+ * Rules flagged `legacy: true` (path rules stored on an MCP endpoint before
398
+ * tool rules existed) are ignored. The middleware sends its code-gated tool
399
+ * names on the policy fetch (`?codeGates=`) so the dashboard can mark them.
400
+ */
401
+ toolGatePrecedence?: 'code' | 'dashboard';
402
+ /** 5.19.0: refresh interval (ms) for the dashboard tool rules. Default 5 minutes. */
403
+ routesRefreshMs?: number;
381
404
  /**
382
405
  * Posture when the MCP middleware itself throws an internal error.
383
- * Default `'open'` for backward compatibility in SDK 2.4.13. Shadow logs
384
- * record what WOULD be denied with `failOnError: 'closed'` so merchants
385
- * can grep correlationId for impact analysis during the observation
386
- * window. Default flips to `'closed'` in a follow-up release.
387
406
  *
388
- * 5.12.0: `'closed'` answers the retryable verification-unavailable
389
- * JSON-RPC error (HTTP 503 + Retry-After, code -32003) — never an
390
- * identity-shaped 401.
407
+ * 5.19.0 (behaviour change): default `'closed'` — an internal throw answers
408
+ * the retryable verification-unavailable JSON-RPC error (HTTP 503 +
409
+ * Retry-After, code -32003; never an identity-shaped 401) and the tool
410
+ * handler is not called. `'open'` (pass the call through unverified) is
411
+ * an explicit opt-in. The `[SHADOW] would-have-denied` log line is still
412
+ * written on every throw, with the correlationId.
391
413
  */
392
414
  failOnError?: 'open' | 'closed';
393
415
  }
416
+ /** 5.19.0: a dashboard tool rule as the MCP middleware evaluates it. */
417
+ interface DashboardToolRule {
418
+ tool: string;
419
+ purpose?: string;
420
+ action?: string;
421
+ resource?: string;
422
+ observe?: boolean;
423
+ }
424
+ /** The usable tool rules in a fetched policy (tool key set, not legacy), by tool name. */
425
+ declare function dashboardToolRules(routes: ReadonlyArray<{
426
+ tool?: string;
427
+ legacy?: boolean;
428
+ purpose?: string;
429
+ action?: string;
430
+ resource?: string;
431
+ observe?: boolean;
432
+ }>): Map<string, DashboardToolRule>;
433
+ type GateSource = 'toolGate' | 'methodGate' | 'dashboard' | 'tier' | 'discovery_default';
434
+ /**
435
+ * Resolve whether a parsed MCP request is enforced, and the gate config to
436
+ * apply. SDK 5.0.0 removed the access-level band — a gate's only decision is
437
+ * enforce vs observe.
438
+ *
439
+ * Precedence:
440
+ * 1. Non-tools/call without `methodGates` entry → pass through
441
+ * (`discovery_default`). Discovery is open — agents must list tools
442
+ * before invoking them.
443
+ * 2. `methodGates[method]` — per-method override (`'observe'` or a config)
444
+ * 3. tools/call: the code `toolGates[toolName]` and the dashboard tool rule
445
+ * (5.19.0) — see `toolGatePrecedence`. With the default `'code'`, a
446
+ * dashboard rule only tightens a code gate: it is ignored when it is
447
+ * `observe` or has no purpose, and evaluated as an `additional` gate
448
+ * (both must pass) when its purpose/action differ.
449
+ * 4. `mcpDefaultEnforced` — built-in default (tools/call → enforced)
450
+ *
451
+ * The `source` field is logged for forensics / activity-feed attribution.
452
+ */
453
+ declare function resolveGate(parsed: ParsedMcpRequest, opts: {
454
+ toolGates?: Record<string, ToolGate>;
455
+ methodGates?: Record<string, ToolGate>;
456
+ dashboardRules?: Map<string, DashboardToolRule>;
457
+ toolGatePrecedence?: 'code' | 'dashboard';
458
+ }): {
459
+ enforce: boolean;
460
+ config?: ToolGateConfig;
461
+ /** 5.19.0: a dashboard gate evaluated in addition to `config` (both must pass). */
462
+ additional?: ToolGateConfig;
463
+ source: GateSource;
464
+ };
394
465
  /**
395
466
  * Create the MCP middleware. Attach AFTER `express.json()` — the body must
396
467
  * already be a parsed object.
397
468
  */
398
469
  declare function createMcpMiddleware(options: McpMiddlewareOptions): RequestHandler;
399
470
 
400
- export { MCP_VERIFIED_HOP_HEADER, type McpMiddlewareOptions, type ParsedMcpRequest, type ToolGate, type ToolGateConfig, createMcpMiddleware, isVerifiedHopValidFor, mcpDefaultEnforced, mcpToPdlss, parseMcpJsonRpc, parseVerifiedHop, serializeVerifiedHop };
471
+ export { type DashboardToolRule, MCP_VERIFIED_HOP_HEADER, type McpMiddlewareOptions, type ParsedMcpRequest, type ToolGate, type ToolGateConfig, createMcpMiddleware, dashboardToolRules, isVerifiedHopValidFor, mcpDefaultEnforced, mcpToPdlss, parseMcpJsonRpc, parseVerifiedHop, resolveGate, serializeVerifiedHop };
@@ -22,17 +22,19 @@ var mcp_exports = {};
22
22
  __export(mcp_exports, {
23
23
  MCP_VERIFIED_HOP_HEADER: () => MCP_VERIFIED_HOP_HEADER,
24
24
  createMcpMiddleware: () => createMcpMiddleware,
25
+ dashboardToolRules: () => dashboardToolRules,
25
26
  isVerifiedHopValidFor: () => isVerifiedHopValidFor,
26
27
  mcpDefaultEnforced: () => mcpDefaultEnforced,
27
28
  mcpToPdlss: () => mcpToPdlss,
28
29
  parseMcpJsonRpc: () => parseMcpJsonRpc,
29
30
  parseVerifiedHop: () => parseVerifiedHop,
31
+ resolveGate: () => resolveGate,
30
32
  serializeVerifiedHop: () => serializeVerifiedHop
31
33
  });
32
34
  module.exports = __toCommonJS(mcp_exports);
33
35
 
34
36
  // src/version.ts
35
- var SDK_VERSION = "5.18.1";
37
+ var SDK_VERSION = "5.19.0";
36
38
 
37
39
  // src/http.ts
38
40
  var SDK_USER_AGENT = `astrasync-sdk/${SDK_VERSION}`;
@@ -274,7 +276,12 @@ function getCacheKey(request, counterpartyId) {
274
276
  // its start_checkout quote was served the cached quote-leg grant (no
275
277
  // settlementOutcome) and never reached the backend. Same F-A1-07 bug class.
276
278
  request.commercePhase || "",
277
- request.checkoutSessionId || ""
279
+ request.checkoutSessionId || "",
280
+ // 5.19.0: a route_config purpose claim is checked server-side against
281
+ // the stored rule for this method + path, so it changes the verdict.
282
+ request.purposeSource || "",
283
+ request.requestMethod || "",
284
+ request.requestPath || ""
278
285
  ].join("|");
279
286
  }
280
287
  function getCachedResult(request, counterpartyId) {
@@ -482,6 +489,11 @@ async function callVerifyAccessAPI(config, request) {
482
489
  if (requestData.buyerEmail) body.buyerEmail = requestData.buyerEmail;
483
490
  if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
484
491
  if (requestData.attemptId) body.attemptId = requestData.attemptId;
492
+ if (requestData.purposeSource) body.purposeSource = requestData.purposeSource;
493
+ if (requestData.agentArtifacts) body.agentArtifacts = requestData.agentArtifacts;
494
+ if (requestData.metadata) body.metadata = requestData.metadata;
495
+ if (requestData.requestMethod) body.requestMethod = requestData.requestMethod;
496
+ if (requestData.requestPath) body.requestPath = requestData.requestPath;
485
497
  if (requestData.considerationSet) body.considerationSet = requestData.considerationSet;
486
498
  body.sdkVersion = SDK_VERSION;
487
499
  if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {
@@ -671,9 +683,18 @@ async function verify(config, request, options) {
671
683
  if (!apiResponse.access?.allowed) {
672
684
  const aggregatedFailures = apiResponse.access?.failures;
673
685
  const idVerifiedFromBackend = apiResponse.verificationContext?.idVerified === true && !aggregatedFailures?.some((f) => REJECTED_IDENTITY_DIMENSIONS.has(f.dimension));
686
+ const denyAgent = idVerifiedFromBackend && apiResponse.agent ? {
687
+ astraId: apiResponse.agent.astraId,
688
+ name: apiResponse.agent.name,
689
+ trustScore: apiResponse.agent.trustScore,
690
+ trustLevel: getTrustLevel(apiResponse.agent.trustScore),
691
+ blockchainAnchored: apiResponse.agent.blockchainStatus === "anchored",
692
+ status: apiResponse.agent.agentStatus
693
+ } : void 0;
674
694
  const result2 = {
675
695
  identityVerified: idVerifiedFromBackend,
676
696
  policyAllowed: false,
697
+ ...denyAgent && { agent: denyAgent },
677
698
  // 3.6.0: the server now emits top-level denialReasons on every deny
678
699
  // path — prefer it verbatim; the failure-message reconstruction remains
679
700
  // as the fallback for older backends.
@@ -822,6 +843,34 @@ async function recordDecision(config, sessionId, decision, reason) {
822
843
  }).catch(() => {
823
844
  });
824
845
  }
846
+ var refusedPolicyReads = /* @__PURE__ */ new Set();
847
+ async function fetchRoutes(config, counterpartyId, options = {}) {
848
+ if (!counterpartyId) return null;
849
+ const headers = { "Content-Type": "application/json" };
850
+ if (config.apiKey) {
851
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
852
+ headers["X-API-Key"] = config.apiKey;
853
+ }
854
+ try {
855
+ const response = await sdkFetch(
856
+ `${config.apiBaseUrl}/endpoints/${encodeURIComponent(counterpartyId)}/routes` + (options.codeGates?.length ? `?codeGates=${options.codeGates.map(encodeURIComponent).join(",")}` : ""),
857
+ { method: "GET", headers }
858
+ );
859
+ if (!response.ok) {
860
+ if ((response.status === 401 || response.status === 403 || response.status === 404) && !refusedPolicyReads.has(counterpartyId)) {
861
+ refusedPolicyReads.add(counterpartyId);
862
+ console.error(
863
+ `[VerificationGateway] the configured API key cannot read the route policy for ${counterpartyId} (HTTP ${response.status}). The key belongs to an account that does not own this endpoint, or the counterpartyId is wrong. The gateway is in PASS-THROUGH mode: no agent is being verified.`
864
+ );
865
+ }
866
+ return null;
867
+ }
868
+ const body = await response.json();
869
+ return body.data?.routes ?? [];
870
+ } catch {
871
+ return null;
872
+ }
873
+ }
825
874
 
826
875
  // src/metadata-capture.ts
827
876
  var CAPTURE_SCHEMA_VERSION = 1;
@@ -1144,6 +1193,42 @@ async function awaitStepUpApproval(config, approval) {
1144
1193
  return "timeout";
1145
1194
  }
1146
1195
 
1196
+ // src/adapters/routes-cache.ts
1197
+ var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
1198
+ function createRoutesCache(config, opts = {}) {
1199
+ const refreshMs = opts.refreshMs ?? DEFAULT_ROUTES_REFRESH_MS;
1200
+ let cached = [];
1201
+ let lastFetchAt = 0;
1202
+ let refreshing = null;
1203
+ async function refresh() {
1204
+ if (!config.counterpartyId) {
1205
+ opts.onNoCounterparty?.();
1206
+ return;
1207
+ }
1208
+ const fetched = await fetchRoutes(config, config.counterpartyId, opts.fetchOptions);
1209
+ if (fetched) {
1210
+ cached = fetched;
1211
+ lastFetchAt = Date.now();
1212
+ opts.onFetched?.(fetched);
1213
+ }
1214
+ }
1215
+ function start() {
1216
+ refreshing = refresh().catch(() => void 0).finally(() => {
1217
+ refreshing = null;
1218
+ });
1219
+ }
1220
+ start();
1221
+ return {
1222
+ async current() {
1223
+ if (refreshing) await refreshing.catch(() => {
1224
+ });
1225
+ if (config.counterpartyId && Date.now() - lastFetchAt > refreshMs) start();
1226
+ return cached;
1227
+ },
1228
+ peek: () => cached
1229
+ };
1230
+ }
1231
+
1147
1232
  // src/verified-hop.ts
1148
1233
  var MCP_VERIFIED_HOP_HEADER = "X-Astra-Verified-Hop";
1149
1234
  var MCP_VERIFIED_HOP_MAX_AGE_MS = 6e4;
@@ -1364,6 +1449,21 @@ function defaultMcpDenied(result, req, res) {
1364
1449
  }
1365
1450
  });
1366
1451
  }
1452
+ function dashboardToolRules(routes) {
1453
+ const out = /* @__PURE__ */ new Map();
1454
+ for (const r of routes) {
1455
+ if (typeof r.tool !== "string" || !r.tool || r.legacy === true) continue;
1456
+ if (out.has(r.tool)) continue;
1457
+ out.set(r.tool, {
1458
+ tool: r.tool,
1459
+ ...r.purpose && { purpose: r.purpose },
1460
+ ...r.action && { action: r.action },
1461
+ ...r.resource && { resource: r.resource },
1462
+ ...r.observe === true && { observe: true }
1463
+ });
1464
+ }
1465
+ return out;
1466
+ }
1367
1467
  function resolveGate(parsed, opts) {
1368
1468
  if (!parsed.toolName) {
1369
1469
  const methodGate = opts.methodGates?.[parsed.method];
@@ -1373,8 +1473,30 @@ function resolveGate(parsed, opts) {
1373
1473
  return { enforce: false, source: "discovery_default" };
1374
1474
  }
1375
1475
  const toolGate = opts.toolGates?.[parsed.toolName];
1476
+ const rule = opts.dashboardRules?.get(parsed.toolName);
1477
+ const fromRule = (r) => r.observe ? { enforce: false, source: "dashboard" } : {
1478
+ enforce: true,
1479
+ config: {
1480
+ ...r.purpose && { purpose: r.purpose },
1481
+ ...r.action && { action: r.action },
1482
+ ...r.resource && { resource: r.resource }
1483
+ },
1484
+ source: "dashboard"
1485
+ };
1486
+ const codeGated = toolGate !== void 0 && toolGate !== "observe";
1487
+ if (rule && (opts.toolGatePrecedence === "dashboard" || !codeGated)) return fromRule(rule);
1376
1488
  if (toolGate !== void 0) {
1377
- return toolGate === "observe" ? { enforce: false, source: "toolGate" } : { enforce: true, config: toolGate, source: "toolGate" };
1489
+ if (toolGate === "observe") return { enforce: false, source: "toolGate" };
1490
+ if (rule && !rule.observe && rule.purpose && (rule.purpose !== toolGate.purpose || rule.action && rule.action !== toolGate.action)) {
1491
+ const extra = fromRule(rule).config;
1492
+ return {
1493
+ enforce: true,
1494
+ config: toolGate,
1495
+ additional: { ...extra, ...!extra.resource && toolGate.resource && { resource: toolGate.resource } },
1496
+ source: "toolGate"
1497
+ };
1498
+ }
1499
+ return { enforce: true, config: toolGate, source: "toolGate" };
1378
1500
  }
1379
1501
  return { enforce: mcpDefaultEnforced(parsed), source: "tier" };
1380
1502
  }
@@ -1389,13 +1511,20 @@ function createMcpMiddleware(options) {
1389
1511
  verifiedHopMaxAgeMs,
1390
1512
  recordDecisions,
1391
1513
  enableRuntimeChallenge = true,
1392
- failOnError = "open",
1514
+ failOnError = "closed",
1515
+ toolGatePrecedence = "code",
1516
+ routesRefreshMs,
1393
1517
  ...config
1394
1518
  } = options;
1395
1519
  if (config.apiBaseUrl) {
1396
1520
  prefetchWellKnown(config.apiBaseUrl).catch(() => {
1397
1521
  });
1398
1522
  }
1523
+ const codeGatedTools = Object.entries(toolGates ?? {}).filter(([, g]) => g !== "observe").map(([name]) => name);
1524
+ const rulesCache = config.apiKey && config.counterpartyId ? createRoutesCache(config, {
1525
+ ...routesRefreshMs !== void 0 && { refreshMs: routesRefreshMs },
1526
+ ...codeGatedTools.length && { fetchOptions: { codeGates: codeGatedTools } }
1527
+ }) : void 0;
1399
1528
  return async (req, res, next) => {
1400
1529
  try {
1401
1530
  if (skip) return next();
@@ -1444,9 +1573,12 @@ function createMcpMiddleware(options) {
1444
1573
  return next();
1445
1574
  }
1446
1575
  }
1576
+ const dashboardRules = rulesCache ? dashboardToolRules(await rulesCache.current()) : void 0;
1447
1577
  const gateDecision = resolveGate(parsed, {
1448
1578
  toolGates,
1449
- methodGates
1579
+ methodGates,
1580
+ dashboardRules,
1581
+ toolGatePrecedence
1450
1582
  });
1451
1583
  const credentials = extractCredentials(
1452
1584
  req.headers,
@@ -1544,6 +1676,20 @@ function createMcpMiddleware(options) {
1544
1676
  result.stepUpOutcome = outcome;
1545
1677
  }
1546
1678
  }
1679
+ if (shouldEnforce && gateDecision.additional?.purpose && result.policyAllowed && !requiresHumanApproval(result)) {
1680
+ const extra = mcpToPdlss(parsed, req.path, void 0, void 0, gateDecision.additional);
1681
+ const extraResult = await verify(config, {
1682
+ ...verificationRequest,
1683
+ purpose: extra.purpose,
1684
+ action: extra.action,
1685
+ resource: extra.resource
1686
+ });
1687
+ if (!extraResult.policyAllowed || requiresHumanApproval(extraResult)) {
1688
+ result = extraResult;
1689
+ req.agentVerification = result;
1690
+ sessionId = result.sessionId;
1691
+ }
1692
+ }
1547
1693
  if (!result.policyAllowed && isVerificationUnavailable(result) && (!shouldEnforce || isReadOnlyMcpMethod(parsed) && !credentials.astraId && !credentials.jwt)) {
1548
1694
  res.setHeader(VERIFICATION_STATUS_HEADER, "unavailable");
1549
1695
  return next();
@@ -1629,11 +1775,13 @@ function createMcpMiddleware(options) {
1629
1775
  0 && (module.exports = {
1630
1776
  MCP_VERIFIED_HOP_HEADER,
1631
1777
  createMcpMiddleware,
1778
+ dashboardToolRules,
1632
1779
  isVerifiedHopValidFor,
1633
1780
  mcpDefaultEnforced,
1634
1781
  mcpToPdlss,
1635
1782
  parseMcpJsonRpc,
1636
1783
  parseVerifiedHop,
1784
+ resolveGate,
1637
1785
  serializeVerifiedHop
1638
1786
  });
1639
1787
  //# sourceMappingURL=mcp.js.map