@astrasyncai/verification-gateway 3.1.0 → 3.2.1
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/dist/adapter-interface/interface.d.mts +2 -2
- package/dist/adapter-interface/interface.d.ts +2 -2
- package/dist/adapters/express.d.mts +2 -2
- package/dist/adapters/express.d.ts +2 -2
- package/dist/adapters/express.js +46 -61
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/express.mjs +46 -61
- package/dist/adapters/express.mjs.map +1 -1
- package/dist/adapters/mcp.d.mts +12 -7
- package/dist/adapters/mcp.d.ts +12 -7
- package/dist/adapters/mcp.js +60 -99
- package/dist/adapters/mcp.js.map +1 -1
- package/dist/adapters/mcp.mjs +60 -99
- package/dist/adapters/mcp.mjs.map +1 -1
- package/dist/adapters/nextjs.d.mts +2 -2
- package/dist/adapters/nextjs.d.ts +2 -2
- package/dist/adapters/nextjs.js +37 -30
- package/dist/adapters/nextjs.js.map +1 -1
- package/dist/adapters/nextjs.mjs +37 -30
- package/dist/adapters/nextjs.mjs.map +1 -1
- package/dist/adapters/sdk.d.mts +2 -2
- package/dist/adapters/sdk.d.ts +2 -2
- package/dist/adapters/sdk.js +25 -14
- package/dist/adapters/sdk.js.map +1 -1
- package/dist/adapters/sdk.mjs +25 -14
- package/dist/adapters/sdk.mjs.map +1 -1
- package/dist/agent/index.d.mts +2 -2
- package/dist/agent/index.d.ts +2 -2
- package/dist/browser/background.js +18 -21
- package/dist/browser/background.js.map +1 -1
- package/dist/browser/background.mjs +18 -21
- package/dist/browser/background.mjs.map +1 -1
- package/dist/browser/browser-adapter.d.mts +2 -2
- package/dist/browser/browser-adapter.d.ts +2 -2
- package/dist/cli/index.d.mts +2 -2
- package/dist/cli/index.d.ts +2 -2
- package/dist/cursor/cursor-adapter.d.mts +2 -2
- package/dist/cursor/cursor-adapter.d.ts +2 -2
- package/dist/cursor/extension.d.mts +2 -2
- package/dist/cursor/extension.d.ts +2 -2
- package/dist/cursor/extension.js +18 -21
- package/dist/cursor/extension.js.map +1 -1
- package/dist/cursor/extension.mjs +18 -21
- package/dist/cursor/extension.mjs.map +1 -1
- package/dist/{express-DavQ76oF.d.ts → express-BowlMHQF.d.ts} +1 -1
- package/dist/{express-DFVBlXr_.d.mts → express-CeoSdOAZ.d.mts} +1 -1
- package/dist/gateway/gateway.d.mts +2 -2
- package/dist/gateway/gateway.d.ts +2 -2
- package/dist/gateway/gateway.js +18 -21
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/gateway.mjs +18 -21
- package/dist/gateway/gateway.mjs.map +1 -1
- package/dist/git-trigger/git-hooks.d.mts +2 -2
- package/dist/git-trigger/git-hooks.d.ts +2 -2
- package/dist/{index-BhL2R65s.d.mts → index-B51W8gn8.d.mts} +1 -1
- package/dist/{index-BhEgEiJL.d.ts → index-DBmlycVm.d.ts} +1 -1
- package/dist/{index-BVxantdv.d.mts → index-DtGziFEm.d.mts} +1 -1
- package/dist/{index-Dk2nIA4w.d.ts → index-DzXXBuLm.d.ts} +1 -1
- package/dist/index.d.mts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +87 -122
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +87 -122
- package/dist/index.mjs.map +1 -1
- package/dist/local-evaluator/evaluator.d.mts +2 -2
- package/dist/local-evaluator/evaluator.d.ts +2 -2
- package/dist/{nextjs-D-maqrNz.d.mts → nextjs-BW1rzr1I.d.mts} +1 -1
- package/dist/{nextjs-BXLH1hJj.d.ts → nextjs-V_K0qlAQ.d.ts} +1 -1
- package/dist/{sdk-767LaEP8.d.mts → sdk-ZYgI7G9f.d.ts} +14 -3
- package/dist/{sdk-K8IgssHI.d.ts → sdk-e5jg7sqW.d.mts} +14 -3
- package/dist/transport/index.d.mts +2 -2
- package/dist/transport/index.d.ts +2 -2
- package/dist/{types-CyFwZ_Yu.d.mts → types-BNiLZY0i.d.mts} +1 -1
- package/dist/{types-WIRp_BP_.d.ts → types-DJi-u3fz.d.ts} +1 -1
- package/dist/{types-Cuh7ELfr.d.mts → types-rFh4VMH4.d.mts} +5 -2
- package/dist/{types-Cuh7ELfr.d.ts → types-rFh4VMH4.d.ts} +5 -2
- package/dist/ui/index.d.mts +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/adapters/mcp.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Request, Response, RequestHandler } from 'express';
|
|
2
|
-
import { A as AccessLevel, G as GatewayConfig, i as VerificationResult } from '../types-
|
|
2
|
+
import { A as AccessLevel, G as GatewayConfig, i as VerificationResult } from '../types-rFh4VMH4.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* MCP server-side helpers — companion to `transport/mcp.ts` (which handles the
|
|
@@ -264,16 +264,21 @@ interface McpMiddlewareOptions extends GatewayConfig {
|
|
|
264
264
|
* Per-tool gating for `tools/call` invocations. Tools not listed inherit
|
|
265
265
|
* the default tier from `mcpRiskTier` (`tools/call` → `'standard'`).
|
|
266
266
|
*
|
|
267
|
-
* Accepts both the shorthand access-level string and the full object shape
|
|
267
|
+
* Accepts both the shorthand access-level string and the full object shape.
|
|
268
|
+
* NOTE (3.2.0): the access-level band no longer gates, and a gated `tools/call`
|
|
269
|
+
* must carry a resolvable PDLSS **purpose** (the backend requires it). So the
|
|
270
|
+
* bare string shorthand — which carries no purpose — only works if the caller
|
|
271
|
+
* supplies the purpose another way (an `X-Astra-Purpose` header, or
|
|
272
|
+
* `params._meta.astrasync.purpose`); otherwise the call fails fast with a
|
|
273
|
+
* `PDLSS_PURPOSE_REQUIRED` 400. Prefer the **object form with `purpose`**:
|
|
268
274
|
* ```typescript
|
|
269
275
|
* toolGates: {
|
|
270
|
-
* browse_catalog: 'read-only',
|
|
271
|
-
*
|
|
272
|
-
*
|
|
276
|
+
* browse_catalog: 'read-only', // shorthand — purpose must come
|
|
277
|
+
* // from a header / _meta, else 400
|
|
278
|
+
* list_products: { purpose: 'shopping', // object form (recommended)
|
|
273
279
|
* action: 'shopping.search',
|
|
274
280
|
* resource: '/api/catalog' },
|
|
275
|
-
* start_checkout: {
|
|
276
|
-
* purpose: 'shopping',
|
|
281
|
+
* start_checkout: { purpose: 'shopping',
|
|
277
282
|
* action: 'shopping.purchase',
|
|
278
283
|
* resource: '/api/checkout/*' },
|
|
279
284
|
* }
|
package/dist/adapters/mcp.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Request, Response, RequestHandler } from 'express';
|
|
2
|
-
import { A as AccessLevel, G as GatewayConfig, i as VerificationResult } from '../types-
|
|
2
|
+
import { A as AccessLevel, G as GatewayConfig, i as VerificationResult } from '../types-rFh4VMH4.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* MCP server-side helpers — companion to `transport/mcp.ts` (which handles the
|
|
@@ -264,16 +264,21 @@ interface McpMiddlewareOptions extends GatewayConfig {
|
|
|
264
264
|
* Per-tool gating for `tools/call` invocations. Tools not listed inherit
|
|
265
265
|
* the default tier from `mcpRiskTier` (`tools/call` → `'standard'`).
|
|
266
266
|
*
|
|
267
|
-
* Accepts both the shorthand access-level string and the full object shape
|
|
267
|
+
* Accepts both the shorthand access-level string and the full object shape.
|
|
268
|
+
* NOTE (3.2.0): the access-level band no longer gates, and a gated `tools/call`
|
|
269
|
+
* must carry a resolvable PDLSS **purpose** (the backend requires it). So the
|
|
270
|
+
* bare string shorthand — which carries no purpose — only works if the caller
|
|
271
|
+
* supplies the purpose another way (an `X-Astra-Purpose` header, or
|
|
272
|
+
* `params._meta.astrasync.purpose`); otherwise the call fails fast with a
|
|
273
|
+
* `PDLSS_PURPOSE_REQUIRED` 400. Prefer the **object form with `purpose`**:
|
|
268
274
|
* ```typescript
|
|
269
275
|
* toolGates: {
|
|
270
|
-
* browse_catalog: 'read-only',
|
|
271
|
-
*
|
|
272
|
-
*
|
|
276
|
+
* browse_catalog: 'read-only', // shorthand — purpose must come
|
|
277
|
+
* // from a header / _meta, else 400
|
|
278
|
+
* list_products: { purpose: 'shopping', // object form (recommended)
|
|
273
279
|
* action: 'shopping.search',
|
|
274
280
|
* resource: '/api/catalog' },
|
|
275
|
-
* start_checkout: {
|
|
276
|
-
* purpose: 'shopping',
|
|
281
|
+
* start_checkout: { purpose: 'shopping',
|
|
277
282
|
* action: 'shopping.purchase',
|
|
278
283
|
* resource: '/api/checkout/*' },
|
|
279
284
|
* }
|
package/dist/adapters/mcp.js
CHANGED
|
@@ -32,26 +32,15 @@ __export(mcp_exports, {
|
|
|
32
32
|
module.exports = __toCommonJS(mcp_exports);
|
|
33
33
|
|
|
34
34
|
// src/access-levels.ts
|
|
35
|
-
var ACCESS_LEVEL_HIERARCHY = {
|
|
36
|
-
none: 0,
|
|
37
|
-
restricted: 1,
|
|
38
|
-
"read-only": 2,
|
|
39
|
-
standard: 3,
|
|
40
|
-
full: 4,
|
|
41
|
-
internal: 5
|
|
42
|
-
};
|
|
43
35
|
function getTrustLevel(score) {
|
|
44
36
|
if (score >= 80) return "PLATINUM";
|
|
45
37
|
if (score >= 60) return "GOLD";
|
|
46
38
|
if (score >= 40) return "SILVER";
|
|
47
39
|
return "BRONZE";
|
|
48
40
|
}
|
|
49
|
-
function hasMinimumAccess(actual, required) {
|
|
50
|
-
return ACCESS_LEVEL_HIERARCHY[actual] >= ACCESS_LEVEL_HIERARCHY[required];
|
|
51
|
-
}
|
|
52
41
|
|
|
53
42
|
// src/version.ts
|
|
54
|
-
var SDK_VERSION = "3.1
|
|
43
|
+
var SDK_VERSION = "3.2.1";
|
|
55
44
|
|
|
56
45
|
// src/well-known.ts
|
|
57
46
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -156,7 +145,7 @@ async function performInitCheck(apiBaseUrl, debug, strictInit) {
|
|
|
156
145
|
}
|
|
157
146
|
}
|
|
158
147
|
var verificationCache = /* @__PURE__ */ new Map();
|
|
159
|
-
function getCacheKey(request) {
|
|
148
|
+
function getCacheKey(request, counterpartyId) {
|
|
160
149
|
const c = request.credentials;
|
|
161
150
|
return [
|
|
162
151
|
c.astraId || "",
|
|
@@ -169,6 +158,14 @@ function getCacheKey(request) {
|
|
|
169
158
|
request.jurisdiction || "",
|
|
170
159
|
request.transactionValue ?? "",
|
|
171
160
|
request.currency || "",
|
|
161
|
+
// SECURITY (cross-merchant cache leak): the merchant identity is sent via
|
|
162
|
+
// `config.counterpartyId`, NOT on the request, so it was previously absent
|
|
163
|
+
// from the key — two verifies for the SAME agent/purpose/action/value but
|
|
164
|
+
// DIFFERENT merchants collided, and a grant at a permissive merchant (low
|
|
165
|
+
// trust floor) was served for a stricter one. Same bug class as the
|
|
166
|
+
// duration omission (F-A1-07). counterpartyId affects the backend verdict
|
|
167
|
+
// (trust floor / per-route policy), so it MUST key the cache.
|
|
168
|
+
counterpartyId || "",
|
|
172
169
|
request.counterpartyUrl || "",
|
|
173
170
|
request.counterpartyType || "",
|
|
174
171
|
request.isSubAgentRequest ? "1" : "0",
|
|
@@ -192,8 +189,8 @@ function getCacheKey(request) {
|
|
|
192
189
|
request.callerMetadata?.agentCardUrl || ""
|
|
193
190
|
].join("|");
|
|
194
191
|
}
|
|
195
|
-
function getCachedResult(request) {
|
|
196
|
-
const key = getCacheKey(request);
|
|
192
|
+
function getCachedResult(request, counterpartyId) {
|
|
193
|
+
const key = getCacheKey(request, counterpartyId);
|
|
197
194
|
const cached = verificationCache.get(key);
|
|
198
195
|
if (cached && cached.expiresAt > Date.now()) {
|
|
199
196
|
return cached.result;
|
|
@@ -205,9 +202,9 @@ function getCachedResult(request) {
|
|
|
205
202
|
}
|
|
206
203
|
var DEFAULT_AUTONOMOUS_TTL_SECONDS = 60;
|
|
207
204
|
var DEFAULT_STEP_UP_TTL_SECONDS = 300;
|
|
208
|
-
function cacheResult(request, result, configuredTtl) {
|
|
205
|
+
function cacheResult(request, result, configuredTtl, counterpartyId) {
|
|
209
206
|
const ttlSeconds = configuredTtl && configuredTtl > 0 ? configuredTtl : result.requiresStepUp ? DEFAULT_STEP_UP_TTL_SECONDS : DEFAULT_AUTONOMOUS_TTL_SECONDS;
|
|
210
|
-
const key = getCacheKey(request);
|
|
207
|
+
const key = getCacheKey(request, counterpartyId);
|
|
211
208
|
verificationCache.set(key, {
|
|
212
209
|
result,
|
|
213
210
|
expiresAt: Date.now() + ttlSeconds * 1e3
|
|
@@ -399,7 +396,7 @@ async function verify(config, request) {
|
|
|
399
396
|
);
|
|
400
397
|
}
|
|
401
398
|
if (mergedConfig.cacheTtl !== 0) {
|
|
402
|
-
const cached = getCachedResult(request);
|
|
399
|
+
const cached = getCachedResult(request, mergedConfig.counterpartyId);
|
|
403
400
|
if (cached) {
|
|
404
401
|
if (mergedConfig.debug) {
|
|
405
402
|
console.log("[VerificationGateway] Returning cached result");
|
|
@@ -451,8 +448,8 @@ async function verify(config, request) {
|
|
|
451
448
|
verifiedAt: /* @__PURE__ */ new Date(),
|
|
452
449
|
// Extract sessionId so decisions can be recorded for denials too
|
|
453
450
|
sessionId: apiResponse.sessionId,
|
|
454
|
-
//
|
|
455
|
-
//
|
|
451
|
+
// Anonymous traffic has no session → correlationId is the per-attempt
|
|
452
|
+
// linking key (the sessionId-equivalent for anonymous callers).
|
|
456
453
|
correlationId: apiResponse.correlationId,
|
|
457
454
|
recommendation: apiResponse.recommendation,
|
|
458
455
|
recommendationReasons: apiResponse.recommendationReasons
|
|
@@ -526,17 +523,14 @@ async function verify(config, request) {
|
|
|
526
523
|
};
|
|
527
524
|
} else if (result.recommendation === "step_up_required") {
|
|
528
525
|
result.requiresStepUp = true;
|
|
529
|
-
if (ACCESS_LEVEL_HIERARCHY[result.accessLevel] > ACCESS_LEVEL_HIERARCHY["read-only"]) {
|
|
530
|
-
result.accessLevel = "read-only";
|
|
531
|
-
}
|
|
532
526
|
result.denialReasons = result.recommendationReasons || ["Step-up verification required"];
|
|
533
527
|
}
|
|
534
528
|
if (mergedConfig.cacheTtl !== 0 && result.recommendation !== "deny") {
|
|
535
|
-
cacheResult(request, result, mergedConfig.cacheTtl);
|
|
529
|
+
cacheResult(request, result, mergedConfig.cacheTtl, mergedConfig.counterpartyId);
|
|
536
530
|
}
|
|
537
531
|
return result;
|
|
538
532
|
}
|
|
539
|
-
async function recordDecision(config, sessionId, decision, reason
|
|
533
|
+
async function recordDecision(config, sessionId, decision, reason) {
|
|
540
534
|
const headers = { "Content-Type": "application/json" };
|
|
541
535
|
if (config.apiKey) {
|
|
542
536
|
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
@@ -545,38 +539,22 @@ async function recordDecision(config, sessionId, decision, reason, override) {
|
|
|
545
539
|
await fetch(`${config.apiBaseUrl}/agents/verify-access/${sessionId}/decision`, {
|
|
546
540
|
method: "POST",
|
|
547
541
|
headers,
|
|
548
|
-
body: JSON.stringify({
|
|
549
|
-
decision,
|
|
550
|
-
reason,
|
|
551
|
-
...override && {
|
|
552
|
-
overriddenBy: override.overriddenBy,
|
|
553
|
-
toolName: override.toolName,
|
|
554
|
-
requestedLevel: override.requestedLevel,
|
|
555
|
-
grantedLevel: override.grantedLevel
|
|
556
|
-
}
|
|
557
|
-
})
|
|
542
|
+
body: JSON.stringify({ decision, reason })
|
|
558
543
|
}).catch(() => {
|
|
559
544
|
});
|
|
560
545
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
overriddenBy: override.overriddenBy,
|
|
574
|
-
toolName: override.toolName,
|
|
575
|
-
requestedLevel: override.requestedLevel,
|
|
576
|
-
grantedLevel: override.grantedLevel
|
|
577
|
-
})
|
|
578
|
-
}).catch(() => {
|
|
579
|
-
});
|
|
546
|
+
|
|
547
|
+
// src/adapters/approval-gate.ts
|
|
548
|
+
var APPROVAL_REASON = "Transaction is above the autonomous limit and requires human approval, which is not yet available \u2014 it cannot be completed automatically.";
|
|
549
|
+
function requiresHumanApproval(result) {
|
|
550
|
+
return result.requiresStepUp === true || result.requiresApproval === true;
|
|
551
|
+
}
|
|
552
|
+
function annotateApprovalRequired(result) {
|
|
553
|
+
result.failures = [
|
|
554
|
+
...result.failures ?? [],
|
|
555
|
+
{ dimension: "commerce.intent.approval_required", message: APPROVAL_REASON }
|
|
556
|
+
];
|
|
557
|
+
result.denialReasons = [APPROVAL_REASON, ...result.denialReasons ?? []];
|
|
580
558
|
}
|
|
581
559
|
|
|
582
560
|
// src/transport/mcp-server.ts
|
|
@@ -791,7 +769,6 @@ function createMcpMiddleware(options) {
|
|
|
791
769
|
return next();
|
|
792
770
|
}
|
|
793
771
|
req.mcpRequest = parsed;
|
|
794
|
-
const wellKnownUrls = config.apiBaseUrl ? await getWellKnownUrls(config.apiBaseUrl).catch(() => void 0) : void 0;
|
|
795
772
|
const headerRaw = req.headers["x-astra-id"] ?? req.headers["x-astra-agentid"];
|
|
796
773
|
const headerAstraId = typeof headerRaw === "string" ? headerRaw : Array.isArray(headerRaw) ? headerRaw[0] : void 0;
|
|
797
774
|
const bodyAstraId = parsed.agentIdFromBody;
|
|
@@ -828,7 +805,7 @@ function createMcpMiddleware(options) {
|
|
|
828
805
|
return next();
|
|
829
806
|
}
|
|
830
807
|
}
|
|
831
|
-
const { level: minAccessLevel
|
|
808
|
+
const { level: minAccessLevel } = resolveMinAccessLevel(parsed, {
|
|
832
809
|
toolGates,
|
|
833
810
|
methodGates
|
|
834
811
|
});
|
|
@@ -864,6 +841,23 @@ function createMcpMiddleware(options) {
|
|
|
864
841
|
resolved_action: pdlss.action
|
|
865
842
|
});
|
|
866
843
|
}
|
|
844
|
+
if (!pdlss.purpose) {
|
|
845
|
+
const id = req.body?.id ?? null;
|
|
846
|
+
res.status(400).json({
|
|
847
|
+
jsonrpc: "2.0",
|
|
848
|
+
id,
|
|
849
|
+
error: {
|
|
850
|
+
code: -32602,
|
|
851
|
+
message: "PDLSS_PURPOSE_REQUIRED",
|
|
852
|
+
data: {
|
|
853
|
+
dimension: "pdlss.purpose",
|
|
854
|
+
detail: "This tool is access-gated but the call declared no PDLSS purpose. Supply a bare-category purpose via the X-Astra-Purpose header or params._meta.astrasync.purpose, or have the merchant set the tool\u2019s purpose in its toolGate config.",
|
|
855
|
+
resolvedAction: pdlss.action
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
867
861
|
const counterpartyUrl = config.counterpartyUrl || `${req.protocol}://${req.get("host")}${req.path}`;
|
|
868
862
|
const shouldRecordDecisions = recordDecisions !== false;
|
|
869
863
|
const result = await verify(config, {
|
|
@@ -887,7 +881,6 @@ function createMcpMiddleware(options) {
|
|
|
887
881
|
});
|
|
888
882
|
req.agentVerification = result;
|
|
889
883
|
const sessionId = result.sessionId;
|
|
890
|
-
const correlationId = result.correlationId;
|
|
891
884
|
if (!result.identityVerified || !result.policyAllowed) {
|
|
892
885
|
if (shouldRecordDecisions && sessionId) {
|
|
893
886
|
recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
|
|
@@ -897,6 +890,16 @@ function createMcpMiddleware(options) {
|
|
|
897
890
|
onDenied(result, req, res);
|
|
898
891
|
return;
|
|
899
892
|
}
|
|
893
|
+
if (requiresHumanApproval(result)) {
|
|
894
|
+
annotateApprovalRequired(result);
|
|
895
|
+
if (shouldRecordDecisions && sessionId) {
|
|
896
|
+
recordDecision(config, sessionId, "denied", result.denialReasons?.[0]).catch(() => {
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
dedupeFailures(result);
|
|
900
|
+
onDenied(result, req, res);
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
900
903
|
if (!shouldEnforce) {
|
|
901
904
|
if (config.setPassThroughHeader) {
|
|
902
905
|
res.setHeader("X-Astra-Gateway-Mode", "enforced");
|
|
@@ -908,48 +911,6 @@ function createMcpMiddleware(options) {
|
|
|
908
911
|
}
|
|
909
912
|
return next();
|
|
910
913
|
}
|
|
911
|
-
if (!hasMinimumAccess(result.accessLevel, minAccessLevel)) {
|
|
912
|
-
const insufficientFailure = {
|
|
913
|
-
dimension: "access_level.insufficient",
|
|
914
|
-
message: `Tool requires accessLevel '${minAccessLevel}'; agent has '${result.accessLevel}'.`,
|
|
915
|
-
guidance: "Request elevated access via step-up verification (coming soon \u2014 ships this month). Step-up lets the agent owner approve a one-time elevation for this specific counterparty + purpose without changing the agent's baseline trust score."
|
|
916
|
-
};
|
|
917
|
-
result.failures = [...result.failures ?? [], insufficientFailure];
|
|
918
|
-
result.denialReasons = [...result.denialReasons ?? [], insufficientFailure.message];
|
|
919
|
-
if (!result.guidance && wellKnownUrls) {
|
|
920
|
-
result.guidance = {
|
|
921
|
-
message: insufficientFailure.message,
|
|
922
|
-
registrationUrl: wellKnownUrls.registrationUrl,
|
|
923
|
-
documentationUrl: wellKnownUrls.documentationUrl
|
|
924
|
-
};
|
|
925
|
-
}
|
|
926
|
-
if (shouldRecordDecisions) {
|
|
927
|
-
const overrideKind = gateSource === "toolGate" ? "toolGate" : gateSource === "methodGate" ? "methodGate" : "other";
|
|
928
|
-
const override = {
|
|
929
|
-
overriddenBy: overrideKind,
|
|
930
|
-
...parsed.toolName && { toolName: parsed.toolName },
|
|
931
|
-
requestedLevel: minAccessLevel,
|
|
932
|
-
grantedLevel: result.accessLevel
|
|
933
|
-
};
|
|
934
|
-
if (sessionId) {
|
|
935
|
-
recordDecision(config, sessionId, "denied", result.denialReasons?.[0], override).catch(
|
|
936
|
-
() => {
|
|
937
|
-
}
|
|
938
|
-
);
|
|
939
|
-
} else if (correlationId) {
|
|
940
|
-
recordAnonymousLocalOverride(
|
|
941
|
-
config,
|
|
942
|
-
correlationId,
|
|
943
|
-
override,
|
|
944
|
-
result.denialReasons?.[0]
|
|
945
|
-
).catch(() => {
|
|
946
|
-
});
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
dedupeFailures(result);
|
|
950
|
-
onDenied(result, req, res);
|
|
951
|
-
return;
|
|
952
|
-
}
|
|
953
914
|
if (effectiveAstraId) {
|
|
954
915
|
res.setHeader(
|
|
955
916
|
MCP_VERIFIED_HOP_HEADER,
|