@kya-os/mcp-i-core 1.8.0 → 1.8.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.
Files changed (111) hide show
  1. package/dist/conformance/capability-verifier-adapter.d.ts +66 -0
  2. package/dist/conformance/capability-verifier-adapter.js +61 -0
  3. package/dist/conformance/index.d.ts +3 -1
  4. package/dist/conformance/index.js +7 -1
  5. package/dist/conformance/validate-level2.d.ts +13 -3
  6. package/dist/conformance/validate-level2.js +15 -5
  7. package/dist/conformance/vc-jwt-verifier.d.ts +55 -0
  8. package/dist/conformance/vc-jwt-verifier.js +147 -0
  9. package/dist/delegation/did-web-resolver.d.ts +60 -0
  10. package/dist/delegation/did-web-resolver.js +129 -0
  11. package/dist/index.d.ts +2 -1
  12. package/dist/index.js +9 -1
  13. package/package.json +3 -3
  14. package/dist/auth/handshake.d.ts +0 -119
  15. package/dist/auth/handshake.js +0 -250
  16. package/dist/auth/index.d.ts +0 -6
  17. package/dist/auth/index.js +0 -11
  18. package/dist/auth/types.d.ts +0 -46
  19. package/dist/auth/types.js +0 -10
  20. package/dist/cache/oauth-config-cache.d.ts +0 -69
  21. package/dist/cache/oauth-config-cache.js +0 -76
  22. package/dist/cache/tool-protection-cache.d.ts +0 -89
  23. package/dist/cache/tool-protection-cache.js +0 -118
  24. package/dist/compliance/index.d.ts +0 -8
  25. package/dist/compliance/index.js +0 -24
  26. package/dist/compliance/schema-registry.d.ts +0 -50
  27. package/dist/compliance/schema-registry.js +0 -438
  28. package/dist/compliance/schema-verifier.d.ts +0 -110
  29. package/dist/compliance/schema-verifier.js +0 -518
  30. package/dist/config/remote-config.d.ts +0 -173
  31. package/dist/config/remote-config.js +0 -253
  32. package/dist/config.d.ts +0 -182
  33. package/dist/config.js +0 -148
  34. package/dist/identity/idp-token-resolver.d.ts +0 -69
  35. package/dist/identity/idp-token-resolver.js +0 -136
  36. package/dist/identity/idp-token-storage.interface.d.ts +0 -80
  37. package/dist/identity/idp-token-storage.interface.js +0 -14
  38. package/dist/identity/user-did-manager.d.ts +0 -228
  39. package/dist/identity/user-did-manager.js +0 -356
  40. package/dist/logging/index.d.ts +0 -5
  41. package/dist/logging/index.js +0 -10
  42. package/dist/logging/logger.d.ts +0 -33
  43. package/dist/logging/logger.js +0 -104
  44. package/dist/proof/generator.d.ts +0 -109
  45. package/dist/proof/generator.js +0 -236
  46. package/dist/proof/index.d.ts +0 -5
  47. package/dist/proof/index.js +0 -11
  48. package/dist/providers/base.d.ts +0 -95
  49. package/dist/providers/base.js +0 -47
  50. package/dist/providers/memory.d.ts +0 -42
  51. package/dist/providers/memory.js +0 -121
  52. package/dist/runtime/audit-logger.d.ts +0 -37
  53. package/dist/runtime/audit-logger.js +0 -9
  54. package/dist/runtime/base.d.ts +0 -375
  55. package/dist/runtime/base.js +0 -1491
  56. package/dist/runtime/ext-apps-constants.d.ts +0 -14
  57. package/dist/runtime/ext-apps-constants.js +0 -17
  58. package/dist/services/access-control.service.d.ts +0 -138
  59. package/dist/services/access-control.service.js +0 -721
  60. package/dist/services/authorization/authorization-registry.d.ts +0 -29
  61. package/dist/services/authorization/authorization-registry.js +0 -58
  62. package/dist/services/authorization/types.d.ts +0 -53
  63. package/dist/services/authorization/types.js +0 -10
  64. package/dist/services/batch-delegation.service.d.ts +0 -53
  65. package/dist/services/batch-delegation.service.js +0 -95
  66. package/dist/services/crypto.service.d.ts +0 -69
  67. package/dist/services/crypto.service.js +0 -225
  68. package/dist/services/delegation-error-formatter.d.ts +0 -124
  69. package/dist/services/delegation-error-formatter.js +0 -214
  70. package/dist/services/errors.d.ts +0 -49
  71. package/dist/services/errors.js +0 -66
  72. package/dist/services/index.d.ts +0 -9
  73. package/dist/services/index.js +0 -15
  74. package/dist/services/oauth-config.service.d.ts +0 -53
  75. package/dist/services/oauth-config.service.js +0 -141
  76. package/dist/services/oauth-provider-registry.d.ts +0 -88
  77. package/dist/services/oauth-provider-registry.js +0 -128
  78. package/dist/services/oauth-service.d.ts +0 -122
  79. package/dist/services/oauth-service.js +0 -442
  80. package/dist/services/oauth-token-retrieval.service.d.ts +0 -49
  81. package/dist/services/oauth-token-retrieval.service.js +0 -150
  82. package/dist/services/policy.service.d.ts +0 -130
  83. package/dist/services/policy.service.js +0 -399
  84. package/dist/services/proof-verifier.d.ts +0 -98
  85. package/dist/services/proof-verifier.js +0 -319
  86. package/dist/services/provider-resolver.d.ts +0 -89
  87. package/dist/services/provider-resolver.js +0 -196
  88. package/dist/services/provider-validator.d.ts +0 -55
  89. package/dist/services/provider-validator.js +0 -135
  90. package/dist/services/session-registration.service.d.ts +0 -80
  91. package/dist/services/session-registration.service.js +0 -174
  92. package/dist/services/storage.service.d.ts +0 -116
  93. package/dist/services/storage.service.js +0 -411
  94. package/dist/services/tool-context-builder.d.ts +0 -74
  95. package/dist/services/tool-context-builder.js +0 -215
  96. package/dist/services/tool-protection.service.d.ts +0 -193
  97. package/dist/services/tool-protection.service.js +0 -736
  98. package/dist/session/index.d.ts +0 -5
  99. package/dist/session/index.js +0 -11
  100. package/dist/session/manager.d.ts +0 -113
  101. package/dist/session/manager.js +0 -273
  102. package/dist/types/oauth-required-error.d.ts +0 -46
  103. package/dist/types/oauth-required-error.js +0 -42
  104. package/dist/types/tool-protection.d.ts +0 -144
  105. package/dist/types/tool-protection.js +0 -48
  106. package/dist/utils/cors.d.ts +0 -44
  107. package/dist/utils/cors.js +0 -71
  108. package/dist/utils/index.d.ts +0 -8
  109. package/dist/utils/index.js +0 -24
  110. package/dist/utils/storage-keys.d.ts +0 -120
  111. package/dist/utils/storage-keys.js +0 -217
@@ -1,721 +0,0 @@
1
- "use strict";
2
- /**
3
- * Access Control API Service
4
- *
5
- * Brand-neutral service for interacting with access-control APIs
6
- * (currently AgentShield/bouncer, but designed to be provider-agnostic).
7
- *
8
- * This service provides:
9
- * - Delegation verification
10
- * - Configuration fetching
11
- * - Proof submission
12
- *
13
- * @package @kya-os/mcp-i-core
14
- */
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.AccessControlApiService = void 0;
17
- exports.authorizationMatches = authorizationMatches;
18
- const agentshield_api_1 = require("@kya-os/contracts/agentshield-api");
19
- const agentshield_api_2 = require("@kya-os/contracts/agentshield-api");
20
- /**
21
- * Generate correlation ID for request tracking
22
- */
23
- function generateCorrelationId() {
24
- // Use crypto.randomUUID() if available (Node.js 14.17+, Cloudflare Workers)
25
- if (typeof crypto !== "undefined" && crypto.randomUUID) {
26
- return crypto.randomUUID();
27
- }
28
- // Fallback for older environments
29
- return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
30
- }
31
- /**
32
- * Access Control API Service
33
- *
34
- * Handles all interactions with the access-control API (AgentShield/bouncer).
35
- * Designed to be brand-neutral and work with any access-control provider.
36
- */
37
- class AccessControlApiService {
38
- config;
39
- metrics;
40
- constructor(config) {
41
- const retryConfig = config.retryConfig || {};
42
- this.config = {
43
- retryConfig: {
44
- maxRetries: retryConfig.maxRetries ?? 3,
45
- initialDelayMs: retryConfig.initialDelayMs ?? 100,
46
- maxDelayMs: retryConfig.maxDelayMs ?? 5000,
47
- },
48
- logger: config.logger || (() => { }),
49
- baseUrl: config.baseUrl,
50
- apiKey: config.apiKey,
51
- fetchProvider: config.fetchProvider,
52
- sleepProvider: config.sleepProvider ||
53
- ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
54
- };
55
- this.metrics = {
56
- successCount: 0,
57
- errorCount: 0,
58
- retryCount: 0,
59
- };
60
- }
61
- /**
62
- * Fetch tool protection configuration for an agent
63
- *
64
- * GET /api/v1/bouncer/config?agent_did={agentDid}
65
- */
66
- async fetchConfig(options) {
67
- return this.retryWithBackoff(async () => {
68
- const correlationId = generateCorrelationId();
69
- const url = `${this.config.baseUrl}/api/v1/bouncer/config?agent_did=${encodeURIComponent(options.agentDid)}`;
70
- this.config.logger(`[AccessControl] Fetching config for agent: ${options.agentDid}`, {
71
- correlationId,
72
- url,
73
- });
74
- const response = await this.config.fetchProvider.fetch(url, {
75
- method: "GET",
76
- headers: {
77
- Authorization: `Bearer ${this.config.apiKey}`,
78
- "Content-Type": "application/json",
79
- "X-Request-ID": correlationId,
80
- },
81
- });
82
- const responseText = await response.text();
83
- const responseData = this.parseResponseJSON(response, responseText);
84
- // Handle error responses
85
- if (!response.ok) {
86
- this.handleErrorResponse(response, responseData);
87
- }
88
- // Validate and parse success response
89
- const parsed = agentshield_api_1.toolProtectionConfigAPIResponseSchema.safeParse(responseData);
90
- if (!parsed.success) {
91
- throw new agentshield_api_2.AgentShieldAPIError("invalid_response", "Response validation failed", { zodErrors: parsed.error.errors });
92
- }
93
- this.config.logger(`[AccessControl] Config fetched successfully`, {
94
- correlationId,
95
- agentDid: options.agentDid,
96
- });
97
- return parsed.data;
98
- });
99
- }
100
- /**
101
- * Verify a delegation token
102
- *
103
- * POST /api/v1/bouncer/delegations/verify
104
- */
105
- async verifyDelegation(request, context) {
106
- return this.retryWithBackoff(async () => {
107
- const correlationId = generateCorrelationId();
108
- const url = `${this.config.baseUrl}/api/v1/bouncer/delegations/verify`;
109
- // Build request body dynamically to handle optional fields
110
- const requestBody = {
111
- agent_did: request.agent_did,
112
- };
113
- // Add optional fields only if they exist
114
- if (request.scopes !== undefined) {
115
- requestBody.scopes = request.scopes;
116
- }
117
- // ✅ CRITICAL: Include user_did for user-scoped delegation verification
118
- // This prevents cross-user delegation leakage (Priority 3 fallback issue)
119
- if (request.user_did !== undefined) {
120
- requestBody.user_did = request.user_did;
121
- }
122
- // Handle credential_jwt: prefer request, fallback to context
123
- if (request.credential_jwt !== undefined) {
124
- requestBody.credential_jwt = request.credential_jwt;
125
- }
126
- else if (context?.credentialJwt) {
127
- requestBody.credential_jwt = context.credentialJwt;
128
- }
129
- // Handle delegation_token: prefer request, fallback to context
130
- if (request.delegation_token !== undefined) {
131
- requestBody.delegation_token = request.delegation_token;
132
- }
133
- else if (context?.delegationToken) {
134
- requestBody.delegation_token = context.delegationToken;
135
- }
136
- if (request.timestamp !== undefined) {
137
- requestBody.timestamp = request.timestamp;
138
- }
139
- if (request.client_info) {
140
- requestBody.client_info = request.client_info;
141
- }
142
- // Remove undefined values to ensure Zod validation passes (optional fields should be omitted, not undefined)
143
- const cleanedRequestBody = Object.fromEntries(Object.entries(requestBody).filter(([_, value]) => value !== undefined));
144
- // Validate the cleaned request body
145
- // Note: Workaround for Zod schema issue where .optional() doesn't properly handle omitted fields
146
- // See AGENTSHIELD_MCPI_COMPLIANCE_REVIEW.md for details
147
- const validationResult = agentshield_api_1.verifyDelegationRequestSchema.safeParse(cleanedRequestBody);
148
- if (!validationResult.success) {
149
- // Check if the error is specifically about scopes being required when it's omitted
150
- const scopesError = validationResult.error.errors.find((e) => e.path.includes("scopes") && e.message === "Required");
151
- if (scopesError && !("scopes" in cleanedRequestBody)) {
152
- // This is a known Zod schema issue in AgentShield - scopes is optional but Zod treats it as required
153
- // Skip validation for this case since the field is correctly omitted
154
- // TODO: Remove this workaround once AgentShield fixes their schema
155
- }
156
- else {
157
- this.config.logger(`[AccessControl] Validation failed:`, {
158
- errors: validationResult.error.errors,
159
- requestBody: requestBody,
160
- });
161
- throw new agentshield_api_2.AgentShieldAPIError("validation_error", "Request validation failed", { zodErrors: validationResult.error.errors });
162
- }
163
- }
164
- this.config.logger(`[AccessControl] Verifying delegation for agent: ${request.agent_did}`, {
165
- correlationId,
166
- url,
167
- hasScopes: (request.scopes?.length || 0) > 0,
168
- });
169
- const response = await this.config.fetchProvider.fetch(url, {
170
- method: "POST",
171
- headers: {
172
- Authorization: `Bearer ${this.config.apiKey}`,
173
- "Content-Type": "application/json",
174
- "X-Request-ID": correlationId,
175
- },
176
- // Use cleanedRequestBody directly instead of validated data
177
- // because Zod seems to strip optional fields in some cases
178
- body: JSON.stringify(cleanedRequestBody),
179
- });
180
- const responseText = await response.text();
181
- const responseData = this.parseResponseJSON(response, responseText);
182
- // Handle error responses
183
- if (!response.ok) {
184
- this.handleErrorResponse(response, responseData);
185
- }
186
- // Validate and parse success response
187
- const parsed = agentshield_api_1.verifyDelegationAPIResponseSchema.safeParse(responseData);
188
- if (!parsed.success) {
189
- throw new agentshield_api_2.AgentShieldAPIError("invalid_response", "Response validation failed", { zodErrors: parsed.error.errors });
190
- }
191
- this.config.logger(`[AccessControl] Delegation verified`, {
192
- correlationId,
193
- agentDid: request.agent_did,
194
- valid: parsed.data.data.valid,
195
- });
196
- return parsed.data;
197
- });
198
- }
199
- /**
200
- * Submit proofs for audit and verification
201
- *
202
- * POST /api/v1/bouncer/proofs
203
- */
204
- async submitProofs(request) {
205
- return this.retryWithBackoff(async () => {
206
- // Validate request directly - Zod's .nullish() should handle null/undefined correctly
207
- const validationResult = agentshield_api_1.proofSubmissionRequestSchema.safeParse(request);
208
- if (!validationResult.success) {
209
- // Log validation errors for debugging
210
- const errorDetails = JSON.stringify(validationResult.error.errors, null, 2);
211
- this.config.logger(`[AccessControl] Proof submission validation failed:`, {
212
- errors: validationResult.error.errors,
213
- request: JSON.stringify(request, null, 2),
214
- });
215
- throw new agentshield_api_2.AgentShieldAPIError("validation_error", `Request validation failed: ${errorDetails}`, { zodErrors: validationResult.error.errors });
216
- }
217
- // Use validated request for the API call
218
- const validatedRequest = validationResult.data;
219
- const correlationId = generateCorrelationId();
220
- const url = `${this.config.baseUrl}/api/v1/bouncer/proofs`;
221
- this.config.logger(`[AccessControl] Submitting ${request.proofs.length} proof(s)`, {
222
- correlationId,
223
- url,
224
- sessionId: request.session_id,
225
- delegationId: request.delegation_id,
226
- });
227
- const httpResponse = await this.config.fetchProvider.fetch(url, {
228
- method: "POST",
229
- headers: {
230
- Authorization: `Bearer ${this.config.apiKey}`,
231
- "Content-Type": "application/json",
232
- "X-Request-ID": correlationId,
233
- },
234
- body: JSON.stringify(validatedRequest),
235
- });
236
- const responseText = await httpResponse.text();
237
- // CRITICAL: Log raw response IMMEDIATELY before parsing/validation
238
- // This will help us debug validation failures
239
- console.error(`[AccessControl] 🔍 RAW API RESPONSE (before parsing):`, {
240
- correlationId,
241
- status: httpResponse.status,
242
- statusText: httpResponse.statusText,
243
- headers: Object.fromEntries(httpResponse.headers.entries()),
244
- responseTextLength: responseText.length,
245
- responseTextPreview: responseText.substring(0, 500),
246
- fullResponseText: responseText, // Full response for debugging
247
- });
248
- const responseData = this.parseResponseJSON(httpResponse, responseText);
249
- // Log parsed response immediately after parsing
250
- console.error(`[AccessControl] 🔍 PARSED RESPONSE DATA:`, {
251
- correlationId,
252
- status: httpResponse.status,
253
- responseDataType: typeof responseData,
254
- responseDataKeys: Object.keys(responseData || {}),
255
- responseData: JSON.stringify(responseData, null, 2),
256
- });
257
- // Handle error responses
258
- if (!httpResponse.ok) {
259
- const errorData = responseData;
260
- if (errorData.error) {
261
- const errorCode = errorData.error.code || "api_error";
262
- // Special handling for all_proofs_rejected - return response instead of throwing
263
- if (errorCode === "all_proofs_rejected" &&
264
- httpResponse.status === 400) {
265
- // Parse the error details to extract accepted/rejected counts
266
- const errorDetails = errorData.error.details;
267
- // Create response matching ProofSubmissionResponse interface
268
- // Validate structure with Zod to ensure type safety
269
- const errorResponseData = {
270
- success: false, // ProofSubmissionResponse has a success field
271
- accepted: 0,
272
- rejected: errorDetails?.rejected || request.proofs.length,
273
- outcomes: {
274
- success: 0,
275
- failed: 0,
276
- blocked: 0,
277
- error: errorDetails?.rejected || request.proofs.length,
278
- },
279
- errors: errorDetails?.errors,
280
- };
281
- // Validate with Zod schema to ensure type safety
282
- const validated = agentshield_api_1.proofSubmissionResponseSchema.safeParse(errorResponseData);
283
- if (validated.success) {
284
- return validated.data;
285
- }
286
- // If validation fails, log and throw error
287
- throw new agentshield_api_2.AgentShieldAPIError("invalid_response", "Error response validation failed", { zodErrors: validated.error.errors });
288
- }
289
- // Ensure error details include status code for retry detection
290
- const errorDetails = {
291
- ...(errorData.error.details || {}),
292
- status: httpResponse.status,
293
- };
294
- throw new agentshield_api_2.AgentShieldAPIError(errorCode, errorData.error.message || `API error: ${httpResponse.status}`, errorDetails);
295
- }
296
- // Map status codes to error codes
297
- let errorCode = "api_error";
298
- if (httpResponse.status === 400) {
299
- errorCode = "validation_error";
300
- }
301
- else if (httpResponse.status === 404) {
302
- errorCode = httpResponse.statusText.includes("delegation")
303
- ? "delegation_not_found"
304
- : "session_not_found";
305
- }
306
- throw new agentshield_api_2.AgentShieldAPIError(errorCode, `API request failed: ${httpResponse.status} ${httpResponse.statusText}`, { status: httpResponse.status, responseData });
307
- }
308
- // Try to handle wrapped response format first
309
- const wrappedResponse = responseData;
310
- // Log raw response for debugging (always log full response on first submission)
311
- const rawResponseLog = {
312
- correlationId,
313
- hasSuccess: wrappedResponse.success !== undefined,
314
- hasData: wrappedResponse.data !== undefined,
315
- responseType: typeof responseData,
316
- responseKeys: Object.keys(responseData || {}),
317
- responseData: JSON.stringify(responseData, null, 2).substring(0, 2000), // Increased limit for debugging
318
- };
319
- this.config.logger(`[AccessControl] Raw response received`, rawResponseLog);
320
- // Also log to console.error for visibility (especially for first proof submission errors)
321
- console.error(`[AccessControl] Raw response received:`, JSON.stringify(responseData, null, 2));
322
- if (wrappedResponse.success !== undefined && wrappedResponse.data) {
323
- // Response is wrapped in { success, data }
324
- // Extract data and add success field if missing (for schema validation)
325
- // CRITICAL: Use JSON.parse(JSON.stringify()) to create a clean object
326
- // This handles edge cases in Cloudflare Workers where response.json()
327
- // might return objects with proxy/getter issues that prevent property access
328
- let dataToValidate;
329
- try {
330
- // Deep clone via JSON to ensure we have a plain JavaScript object
331
- // This is the most reliable way to handle potential proxy/getter issues
332
- const rawData = wrappedResponse.data;
333
- const clonedData = JSON.parse(JSON.stringify(rawData));
334
- if (typeof clonedData !== "object" || clonedData === null) {
335
- throw new agentshield_api_2.AgentShieldAPIError("invalid_response", "Response data is not an object after cloning", {
336
- originalType: typeof rawData,
337
- clonedType: typeof clonedData,
338
- clonedValue: clonedData,
339
- });
340
- }
341
- dataToValidate = clonedData;
342
- }
343
- catch (cloneError) {
344
- if (cloneError instanceof agentshield_api_2.AgentShieldAPIError) {
345
- throw cloneError;
346
- }
347
- throw new agentshield_api_2.AgentShieldAPIError("invalid_response", "Failed to clone response data", {
348
- error: cloneError instanceof Error
349
- ? cloneError.message
350
- : String(cloneError),
351
- dataType: typeof wrappedResponse.data,
352
- });
353
- }
354
- // CRITICAL: Log the actual data structure for debugging
355
- console.error(`[AccessControl] 🔍 DATA OBJECT STRUCTURE (after deep clone):`, {
356
- correlationId,
357
- dataKeys: Object.keys(dataToValidate),
358
- hasAccepted: "accepted" in dataToValidate,
359
- hasRejected: "rejected" in dataToValidate,
360
- hasOutcomes: "outcomes" in dataToValidate,
361
- hasErrors: "errors" in dataToValidate,
362
- acceptedType: typeof dataToValidate.accepted,
363
- acceptedValue: dataToValidate.accepted,
364
- rejectedType: typeof dataToValidate.rejected,
365
- rejectedValue: dataToValidate.rejected,
366
- outcomesType: typeof dataToValidate.outcomes,
367
- outcomesValue: dataToValidate.outcomes,
368
- errorsType: typeof dataToValidate.errors,
369
- errorsIsArray: Array.isArray(dataToValidate.errors),
370
- fullData: JSON.stringify(dataToValidate, null, 2),
371
- });
372
- // Ensure success field is present (required by schema)
373
- // wrappedResponse.success should be true since we checked it exists
374
- // CRITICAL: Construct from the cloned data to ensure all fields are present
375
- const dataWithSuccess = {
376
- success: wrappedResponse.success === true,
377
- accepted: dataToValidate.accepted,
378
- rejected: dataToValidate.rejected,
379
- };
380
- // Add optional fields if present
381
- if ("outcomes" in dataToValidate &&
382
- dataToValidate.outcomes !== undefined) {
383
- dataWithSuccess.outcomes = dataToValidate.outcomes;
384
- }
385
- if ("errors" in dataToValidate && dataToValidate.errors !== undefined) {
386
- dataWithSuccess.errors = dataToValidate.errors;
387
- }
388
- // CRITICAL: Log what we're validating
389
- console.error(`[AccessControl] 🔍 VALIDATING DATA WITH SUCCESS:`, {
390
- correlationId,
391
- dataWithSuccessKeys: Object.keys(dataWithSuccess),
392
- hasSuccess: "success" in dataWithSuccess,
393
- successValue: dataWithSuccess.success,
394
- hasAccepted: "accepted" in dataWithSuccess,
395
- acceptedValue: dataWithSuccess.accepted,
396
- hasRejected: "rejected" in dataWithSuccess,
397
- rejectedValue: dataWithSuccess.rejected,
398
- hasOutcomes: "outcomes" in dataWithSuccess,
399
- outcomesValue: dataWithSuccess.outcomes,
400
- fullDataWithSuccess: JSON.stringify(dataWithSuccess, null, 2),
401
- });
402
- // CRITICAL: Ensure all required fields are present before validation
403
- if (typeof dataWithSuccess.accepted !== "number" ||
404
- typeof dataWithSuccess.rejected !== "number") {
405
- console.error(`[AccessControl] ❌ MISSING REQUIRED FIELDS AFTER CONSTRUCTION:`, {
406
- correlationId,
407
- hasAccepted: "accepted" in dataWithSuccess,
408
- acceptedType: typeof dataWithSuccess.accepted,
409
- acceptedValue: dataWithSuccess.accepted,
410
- hasRejected: "rejected" in dataWithSuccess,
411
- rejectedType: typeof dataWithSuccess.rejected,
412
- rejectedValue: dataWithSuccess.rejected,
413
- dataWithSuccessKeys: Object.keys(dataWithSuccess),
414
- fullDataWithSuccess: JSON.stringify(dataWithSuccess, null, 2),
415
- dataToValidateKeys: Object.keys(dataToValidate),
416
- fullDataToValidate: JSON.stringify(dataToValidate, null, 2),
417
- originalResponseData: JSON.stringify(responseData, null, 2),
418
- });
419
- throw new agentshield_api_2.AgentShieldAPIError("invalid_response", "Response data missing required fields (accepted/rejected)", {
420
- responseData,
421
- dataToValidate,
422
- dataWithSuccess,
423
- });
424
- }
425
- const dataParsed = agentshield_api_1.proofSubmissionResponseSchema.safeParse(dataWithSuccess);
426
- if (dataParsed.success) {
427
- this.config.logger(`[AccessControl] Proofs submitted successfully (wrapped)`, {
428
- correlationId,
429
- accepted: dataParsed.data.accepted,
430
- rejected: dataParsed.data.rejected,
431
- });
432
- return dataParsed.data;
433
- }
434
- else {
435
- // Log validation errors for wrapped response (always log errors)
436
- const validationErrorLog = {
437
- correlationId,
438
- zodErrors: dataParsed.error.errors,
439
- zodErrorDetails: JSON.stringify(dataParsed.error.errors, null, 2),
440
- dataToValidate: JSON.stringify(dataToValidate, null, 2).substring(0, 2000),
441
- dataWithSuccess: JSON.stringify(dataWithSuccess, null, 2).substring(0, 2000),
442
- dataKeys: Object.keys(dataToValidate),
443
- dataWithSuccessKeys: Object.keys(dataWithSuccess),
444
- originalResponse: JSON.stringify(responseData, null, 2).substring(0, 2000),
445
- };
446
- this.config.logger(`[AccessControl] Wrapped response validation failed`, validationErrorLog);
447
- // Also log to console.error for visibility in production
448
- console.error(`[AccessControl] Wrapped response validation failed`, validationErrorLog);
449
- console.error(`[AccessControl] Original wrapped response:`, JSON.stringify(responseData, null, 2));
450
- // CRITICAL: Log each zod error individually for easier debugging
451
- console.error(`[AccessControl] ❌ ZOD VALIDATION FAILED - ${dataParsed.error.errors.length} error(s):`);
452
- dataParsed.error.errors.forEach((err, idx) => {
453
- const errorDetails = {
454
- path: err.path.join(".") || "(root)",
455
- message: err.message,
456
- code: err.code,
457
- };
458
- // Only include properties that exist on specific error types
459
- if ("received" in err)
460
- errorDetails.received = err.received;
461
- if ("expected" in err)
462
- errorDetails.expected = err.expected;
463
- if ("input" in err)
464
- errorDetails.input = err.input;
465
- console.error(`[AccessControl] Error ${idx + 1}:`, JSON.stringify(errorDetails, null, 2));
466
- });
467
- console.error(`[AccessControl] ❌ Full ZOD errors JSON:`, JSON.stringify(dataParsed.error.errors, null, 2));
468
- // CRITICAL: Throw error instead of falling through to direct parsing
469
- // The response is wrapped, so direct parsing will also fail
470
- throw new agentshield_api_2.AgentShieldAPIError("invalid_response", "Response validation failed", { zodErrors: dataParsed.error.errors, responseData });
471
- }
472
- }
473
- // Try parsing as direct ProofSubmissionResponse
474
- const parsed = agentshield_api_1.proofSubmissionResponseSchema.safeParse(responseData);
475
- if (!parsed.success) {
476
- // Log detailed validation errors (always log errors)
477
- const validationErrorLog = {
478
- correlationId,
479
- zodErrors: parsed.error.errors,
480
- zodErrorDetails: JSON.stringify(parsed.error.errors, null, 2),
481
- responseData: JSON.stringify(responseData, null, 2),
482
- responseDataType: typeof responseData,
483
- responseKeys: Object.keys(responseData || {}),
484
- httpStatus: httpResponse.status,
485
- httpStatusText: httpResponse.statusText,
486
- };
487
- this.config.logger(`[AccessControl] Response validation failed`, validationErrorLog);
488
- // CRITICAL: Log to console.error with full details for debugging
489
- // This format matches test expectations: single call with message and error object
490
- // This log must include 'Response validation failed' in the message for test compatibility
491
- console.error(`[AccessControl] Response validation failed`, {
492
- zodErrors: parsed.error.errors,
493
- responseData: responseData,
494
- });
495
- // Additional detailed logging for debugging
496
- console.error(`[AccessControl] Response validation failed`, validationErrorLog);
497
- // CRITICAL: Log each zod error individually for easier debugging
498
- console.error(`[AccessControl] ❌ ZOD VALIDATION FAILED (direct) - ${parsed.error.errors.length} error(s):`);
499
- parsed.error.errors.forEach((err, idx) => {
500
- const errorDetails = {
501
- path: err.path.join(".") || "(root)",
502
- message: err.message,
503
- code: err.code,
504
- };
505
- // Only include properties that exist on specific error types
506
- if ("received" in err)
507
- errorDetails.received = err.received;
508
- if ("expected" in err)
509
- errorDetails.expected = err.expected;
510
- if ("input" in err)
511
- errorDetails.input = err.input;
512
- console.error(`[AccessControl] Error ${idx + 1}:`, errorDetails);
513
- });
514
- console.error(`[AccessControl] ❌ Full ZOD errors JSON:`, JSON.stringify(parsed.error.errors, null, 2));
515
- console.error(`[AccessControl] ❌ ACTUAL RESPONSE DATA:`, JSON.stringify(responseData, null, 2));
516
- throw new agentshield_api_2.AgentShieldAPIError("invalid_response", "Response validation failed", { zodErrors: parsed.error.errors, responseData });
517
- }
518
- this.config.logger(`[AccessControl] Proofs submitted successfully`, {
519
- correlationId,
520
- accepted: parsed.data.accepted,
521
- rejected: parsed.data.rejected,
522
- });
523
- return parsed.data;
524
- });
525
- }
526
- /**
527
- * Get current metrics
528
- */
529
- getMetrics() {
530
- return { ...this.metrics };
531
- }
532
- /**
533
- * Reset metrics
534
- */
535
- resetMetrics() {
536
- this.metrics = {
537
- successCount: 0,
538
- errorCount: 0,
539
- retryCount: 0,
540
- };
541
- }
542
- /**
543
- * Retry logic with exponential backoff
544
- *
545
- * @internal
546
- */
547
- async retryWithBackoff(operation, retryCount = 0) {
548
- try {
549
- const result = await operation();
550
- this.metrics.successCount++;
551
- return result;
552
- }
553
- catch (error) {
554
- // Check if error is retryable (5xx status codes)
555
- const isRetryable = this.isRetryableError(error);
556
- const { maxRetries, initialDelayMs, maxDelayMs } = this.config.retryConfig;
557
- if (isRetryable && retryCount < maxRetries) {
558
- const delay = Math.min(initialDelayMs * Math.pow(2, retryCount), maxDelayMs);
559
- this.metrics.retryCount++;
560
- this.config.logger(`Retrying after ${delay}ms (attempt ${retryCount + 1}/${maxRetries})`);
561
- await this.sleep(delay);
562
- return this.retryWithBackoff(operation, retryCount + 1);
563
- }
564
- this.metrics.errorCount++;
565
- throw error;
566
- }
567
- }
568
- /**
569
- * Check if an error is retryable (5xx status codes)
570
- *
571
- * @internal
572
- */
573
- isRetryableError(error) {
574
- // Network errors (fetch failures) are retryable
575
- if (error instanceof TypeError && error.message.includes("fetch")) {
576
- return true;
577
- }
578
- // AgentShieldAPIError with 5xx status codes are retryable
579
- if (error instanceof agentshield_api_2.AgentShieldAPIError) {
580
- // Check error code for server errors
581
- if (error.code === "server_error") {
582
- return true;
583
- }
584
- // Check if error details contain status code
585
- const status = error.details?.status;
586
- if (status && status >= 500 && status < 600) {
587
- return true;
588
- }
589
- // Rate limiting (429) is also retryable
590
- if (status === 429) {
591
- return true;
592
- }
593
- }
594
- // Check for timeout errors
595
- if (error instanceof Error) {
596
- const message = error.message.toLowerCase();
597
- if (message.includes("timeout") || message.includes("timed out")) {
598
- return true;
599
- }
600
- }
601
- return false;
602
- }
603
- /**
604
- * Sleep utility for retry delays
605
- * Uses platform-agnostic sleep provider
606
- *
607
- * @internal
608
- */
609
- sleep(ms) {
610
- return this.config.sleepProvider(ms);
611
- }
612
- /**
613
- * Parse response text to JSON with error handling
614
- *
615
- * @internal
616
- */
617
- parseResponseJSON(response, responseText) {
618
- try {
619
- return JSON.parse(responseText);
620
- }
621
- catch (error) {
622
- // Handle non-JSON error responses (e.g., plain text "Internal Server Error")
623
- if (!response.ok) {
624
- const errorCode = this.mapStatusToErrorCode(response.status);
625
- throw new agentshield_api_2.AgentShieldAPIError(errorCode, `API request failed: ${response.status} ${response.statusText}`, {
626
- status: response.status,
627
- responseText: responseText.substring(0, 500),
628
- });
629
- }
630
- // For success responses that aren't JSON, throw invalid_response error
631
- throw new agentshield_api_2.AgentShieldAPIError("invalid_response", `Failed to parse response: ${error instanceof Error ? error.message : String(error)}`, { responseText: responseText.substring(0, 500) });
632
- }
633
- }
634
- /**
635
- * Map HTTP status codes to error codes
636
- *
637
- * @internal
638
- */
639
- mapStatusToErrorCode(status, statusText = "") {
640
- if (status === 400) {
641
- return "validation_error";
642
- }
643
- else if (status === 401 || status === 403) {
644
- return "authentication_failed";
645
- }
646
- else if (status === 404) {
647
- return "config_not_found";
648
- }
649
- else if (status >= 500) {
650
- return "server_error";
651
- }
652
- return "api_error";
653
- }
654
- /**
655
- * Handle error responses and throw appropriate AgentShieldAPIError
656
- *
657
- * @internal
658
- */
659
- handleErrorResponse(response, responseData) {
660
- const errorData = responseData;
661
- if (errorData.error) {
662
- const errorCode = errorData.error.code || "api_error";
663
- // Ensure error details include status code for retry detection
664
- const errorDetails = {
665
- ...(errorData.error.details || {}),
666
- status: response.status,
667
- };
668
- throw new agentshield_api_2.AgentShieldAPIError(errorCode, errorData.error.message || `API error: ${response.status}`, errorDetails);
669
- }
670
- // Map status codes to error codes
671
- const errorCode = this.mapStatusToErrorCode(response.status, response.statusText);
672
- throw new agentshield_api_2.AgentShieldAPIError(errorCode, `API request failed: ${response.status} ${response.statusText}`, { status: response.status, responseData });
673
- }
674
- }
675
- exports.AccessControlApiService = AccessControlApiService;
676
- /**
677
- * Compare delegation authorization with current tool protection authorization
678
- * Returns true if they match, false otherwise
679
- *
680
- * @param delegationAuth - Authorization from the delegation credential
681
- * @param toolAuth - Authorization required by the tool protection config
682
- * @returns true if authorization methods match
683
- */
684
- function authorizationMatches(delegationAuth, toolAuth) {
685
- // No tool auth requirement - allow any delegation
686
- if (!toolAuth)
687
- return true;
688
- // ✅ CROSS-TYPE COMPATIBILITY: credential providers can satisfy oauth requirements
689
- // When a credential provider (like email/password) is used:
690
- // - Delegation is created with: { type: 'credential', credentialType: 'credentials' }
691
- // - Tool protection may specify: { type: 'oauth', provider: 'credentials' }
692
- // These should be considered equivalent since they represent the same auth method.
693
- const isCredentialToOAuthCompatible = delegationAuth.type === "credential" &&
694
- toolAuth.type === "oauth" &&
695
- delegationAuth.credentialType &&
696
- toolAuth.provider &&
697
- delegationAuth.credentialType === toolAuth.provider;
698
- if (isCredentialToOAuthCompatible) {
699
- return true;
700
- }
701
- // Compare type (strict match for other cases)
702
- if (delegationAuth.type !== toolAuth.type)
703
- return false;
704
- // For OAuth, compare provider (undefined in toolAuth means "any provider is acceptable")
705
- if (delegationAuth.type === "oauth") {
706
- // If tool doesn't specify a provider, any provider is acceptable
707
- if (!toolAuth.provider)
708
- return true;
709
- return delegationAuth.provider === toolAuth.provider;
710
- }
711
- // For verifiable_credential, compare credentialType
712
- if (delegationAuth.type === "verifiable_credential") {
713
- // If tool doesn't specify a credential type, any type is acceptable
714
- if (!toolAuth.credentialType)
715
- return true;
716
- return delegationAuth.credentialType === toolAuth.credentialType;
717
- }
718
- // For 'none' type, they match if both are 'none'
719
- return true;
720
- }
721
- //# sourceMappingURL=access-control.service.js.map