@kya-os/mcp-i-cloudflare 1.14.7 → 1.15.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 (152) hide show
  1. package/README.md +8 -8
  2. package/dist/adapter.d.ts.map +1 -1
  3. package/dist/adapter.js +8 -4
  4. package/dist/adapter.js.map +1 -1
  5. package/dist/agent.d.ts +22 -5
  6. package/dist/agent.d.ts.map +1 -1
  7. package/dist/agent.js +88 -46
  8. package/dist/agent.js.map +1 -1
  9. package/dist/app.d.ts.map +1 -1
  10. package/dist/app.js +15 -8
  11. package/dist/app.js.map +1 -1
  12. package/dist/config.d.ts +101 -5
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/config.js +18 -12
  15. package/dist/config.js.map +1 -1
  16. package/dist/constants.d.ts +5 -3
  17. package/dist/constants.d.ts.map +1 -1
  18. package/dist/constants.js +6 -4
  19. package/dist/constants.js.map +1 -1
  20. package/dist/delegation-http/anchor-registration.d.ts +2 -2
  21. package/dist/delegation-http/anchor-registration.js +4 -4
  22. package/dist/delegation-http/anchor-registration.js.map +1 -1
  23. package/dist/delegation-http/consent-anchor-bridge.js +3 -3
  24. package/dist/delegation-http/consent-anchor-bridge.js.map +1 -1
  25. package/dist/delegation-http/credential-consent.d.ts +2 -2
  26. package/dist/delegation-http/credential-consent.js +14 -14
  27. package/dist/delegation-http/credential-consent.js.map +1 -1
  28. package/dist/delegation-http/native-http.js +5 -5
  29. package/dist/delegation-http/native-http.js.map +1 -1
  30. package/dist/delegation-http/native-oauth-state-do.d.ts +43 -0
  31. package/dist/delegation-http/native-oauth-state-do.d.ts.map +1 -0
  32. package/dist/delegation-http/native-oauth-state-do.js +175 -0
  33. package/dist/delegation-http/native-oauth-state-do.js.map +1 -0
  34. package/dist/delegation-http/native-oauth-state.d.ts +12 -0
  35. package/dist/delegation-http/native-oauth-state.d.ts.map +1 -0
  36. package/dist/delegation-http/native-oauth-state.js +131 -0
  37. package/dist/delegation-http/native-oauth-state.js.map +1 -0
  38. package/dist/delegation-http/native-oauth.d.ts.map +1 -1
  39. package/dist/delegation-http/native-oauth.js +22 -17
  40. package/dist/delegation-http/native-oauth.js.map +1 -1
  41. package/dist/delegation-http/register.js +1 -1
  42. package/dist/delegation-http/register.js.map +1 -1
  43. package/dist/delegation-http/registrar-auth.d.ts +1 -1
  44. package/dist/delegation-http/registrar-auth.js +2 -2
  45. package/dist/delegation-http/registrar-auth.js.map +1 -1
  46. package/dist/delegation-http/remote-issuer.d.ts +1 -1
  47. package/dist/delegation-http/remote-issuer.d.ts.map +1 -1
  48. package/dist/delegation-http/remote-issuer.js +4 -4
  49. package/dist/delegation-http/remote-issuer.js.map +1 -1
  50. package/dist/helpers/env-mapper.d.ts +2 -2
  51. package/dist/helpers/env-mapper.d.ts.map +1 -1
  52. package/dist/helpers/env-mapper.js +21 -14
  53. package/dist/helpers/env-mapper.js.map +1 -1
  54. package/dist/index.d.ts +3 -1
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +4 -3
  57. package/dist/index.js.map +1 -1
  58. package/dist/needs-authorization-meta.d.ts +5 -0
  59. package/dist/needs-authorization-meta.d.ts.map +1 -1
  60. package/dist/needs-authorization-meta.js +9 -0
  61. package/dist/needs-authorization-meta.js.map +1 -1
  62. package/dist/proof-generator.d.ts +3 -3
  63. package/dist/proof-generator.js +4 -4
  64. package/dist/proof-generator.js.map +1 -1
  65. package/dist/runtime/invoke-tool-handler.d.ts +26 -1
  66. package/dist/runtime/invoke-tool-handler.d.ts.map +1 -1
  67. package/dist/runtime/invoke-tool-handler.js +32 -0
  68. package/dist/runtime/invoke-tool-handler.js.map +1 -1
  69. package/dist/runtime/oauth-handler.d.ts +9 -7
  70. package/dist/runtime/oauth-handler.d.ts.map +1 -1
  71. package/dist/runtime/oauth-handler.js +53 -49
  72. package/dist/runtime/oauth-handler.js.map +1 -1
  73. package/dist/runtime/oidc/authorize-url.js +2 -2
  74. package/dist/runtime/oidc/authorize-url.js.map +1 -1
  75. package/dist/runtime/oidc/token-exchange.d.ts +1 -1
  76. package/dist/runtime/oidc/token-exchange.d.ts.map +1 -1
  77. package/dist/runtime/tool-activity.d.ts +56 -0
  78. package/dist/runtime/tool-activity.d.ts.map +1 -0
  79. package/dist/runtime/tool-activity.js +55 -0
  80. package/dist/runtime/tool-activity.js.map +1 -0
  81. package/dist/runtime.d.ts +30 -13
  82. package/dist/runtime.d.ts.map +1 -1
  83. package/dist/runtime.js +71 -18
  84. package/dist/runtime.js.map +1 -1
  85. package/dist/server.d.ts.map +1 -1
  86. package/dist/server.js +17 -15
  87. package/dist/server.js.map +1 -1
  88. package/dist/services/admin.service.d.ts +1 -1
  89. package/dist/services/admin.service.js +13 -13
  90. package/dist/services/admin.service.js.map +1 -1
  91. package/dist/services/consent-config.service.d.ts +5 -5
  92. package/dist/services/consent-config.service.js +12 -12
  93. package/dist/services/consent-config.service.js.map +1 -1
  94. package/dist/services/consent-templates/base/components.d.ts +1 -1
  95. package/dist/services/consent-templates/base/components.js +1 -1
  96. package/dist/services/consent-templates/base/styles.d.ts +2 -2
  97. package/dist/services/consent-templates/base/styles.js +4 -4
  98. package/dist/services/consent-templates/base/styles.js.map +1 -1
  99. package/dist/services/consent-templates/helpers.d.ts +3 -3
  100. package/dist/services/consent-templates/helpers.js +4 -4
  101. package/dist/services/consent-templates/helpers.js.map +1 -1
  102. package/dist/services/consent-templates/index.d.ts +1 -1
  103. package/dist/services/consent-templates/index.js +1 -1
  104. package/dist/services/consent-templates/modes/credentials.template.d.ts +5 -5
  105. package/dist/services/consent-templates/modes/credentials.template.js +9 -9
  106. package/dist/services/consent-templates/modes/credentials.template.js.map +1 -1
  107. package/dist/services/consent-templates/registry.d.ts +3 -3
  108. package/dist/services/consent-templates/registry.js +3 -3
  109. package/dist/services/consent-templates/template-renderer.d.ts +5 -3
  110. package/dist/services/consent-templates/template-renderer.d.ts.map +1 -1
  111. package/dist/services/consent-templates/template-renderer.js +5 -3
  112. package/dist/services/consent-templates/template-renderer.js.map +1 -1
  113. package/dist/services/consent-templates/types.d.ts +4 -4
  114. package/dist/services/consent-templates/types.js +2 -2
  115. package/dist/services/consent.service.d.ts +44 -24
  116. package/dist/services/consent.service.d.ts.map +1 -1
  117. package/dist/services/consent.service.js +197 -119
  118. package/dist/services/consent.service.js.map +1 -1
  119. package/dist/services/delegation-mandate.d.ts +40 -0
  120. package/dist/services/delegation-mandate.d.ts.map +1 -0
  121. package/dist/services/delegation-mandate.js +46 -0
  122. package/dist/services/delegation-mandate.js.map +1 -0
  123. package/dist/services/delegation.service.d.ts +28 -12
  124. package/dist/services/delegation.service.d.ts.map +1 -1
  125. package/dist/services/delegation.service.js +88 -99
  126. package/dist/services/delegation.service.js.map +1 -1
  127. package/dist/services/kya-os-events.service.d.ts +15 -3
  128. package/dist/services/kya-os-events.service.d.ts.map +1 -1
  129. package/dist/services/kya-os-events.service.js +14 -4
  130. package/dist/services/kya-os-events.service.js.map +1 -1
  131. package/dist/services/proof-batch-queue.d.ts +4 -4
  132. package/dist/services/proof-batch-queue.d.ts.map +1 -1
  133. package/dist/services/proof-batch-queue.js +10 -10
  134. package/dist/services/proof-batch-queue.js.map +1 -1
  135. package/dist/services/proof.service.d.ts +64 -5
  136. package/dist/services/proof.service.d.ts.map +1 -1
  137. package/dist/services/proof.service.js +127 -40
  138. package/dist/services/proof.service.js.map +1 -1
  139. package/dist/services/provider-loader.service.d.ts +6 -6
  140. package/dist/services/provider-loader.service.d.ts.map +1 -1
  141. package/dist/services/provider-loader.service.js +4 -4
  142. package/dist/services/provider-loader.service.js.map +1 -1
  143. package/dist/services/vault-resolver.d.ts +4 -4
  144. package/dist/services/vault-resolver.d.ts.map +1 -1
  145. package/dist/services/vault-resolver.js +2 -2
  146. package/dist/types.d.ts +6 -4
  147. package/dist/types.d.ts.map +1 -1
  148. package/dist/utils/day0-config.d.ts +1 -1
  149. package/dist/utils/day0-config.js +1 -1
  150. package/dist/utils/env-secret-resolver.d.ts +1 -1
  151. package/dist/utils/env-secret-resolver.js +1 -1
  152. package/package.json +5 -5
@@ -8,19 +8,20 @@
8
8
  */
9
9
  import { ConsentConfigService } from "./consent-config.service";
10
10
  import { TemplateRenderer } from "./consent-templates/template-renderer";
11
- import { DEFAULT_AGENTSHIELD_URL, DEFAULT_SESSION_CACHE_TTL, KEY_PAIR_TTL_SECONDS, CONSENT_PROVIDER_TYPES, } from "../constants";
11
+ import { DEFAULT_CHECKPOINT_URL, DEFAULT_SESSION_CACHE_TTL, KEY_PAIR_TTL_SECONDS, CONSENT_PROVIDER_TYPES, } from "../constants";
12
12
  import { STORAGE_KEYS, isPendingDelegationId } from "../constants/storage-keys";
13
13
  import { loadDay0Config, getDelegationFieldName } from "../utils/day0-config";
14
14
  import { buildProviderAuthorizeUrl } from "../runtime/oidc/authorize-url.js";
15
15
  import { resolveIdpScopes } from "../runtime/oidc/idp-scopes.js";
16
16
  import { validateConsentApprovalRequest, } from "@kya-os/contracts/consent";
17
- import { AGENTSHIELD_ENDPOINTS, createDelegationAPIResponseSchema, createDelegationResponseSchema, } from "@kya-os/contracts/agentshield-api";
17
+ import { CHECKPOINT_ENDPOINTS, createDelegationAPIResponseSchema, createDelegationResponseSchema, } from "@kya-os/contracts/checkpoint-api";
18
18
  import { createUnsignedVCJWT, completeVCJWT, parseVCJWT, generateDidKeyFromBase64, createDelegationVerifier, createDidKeyResolver, logger, base64urlEncodeFromBytes, base64urlDecodeToBytes, bytesToBase64, wrapDelegationAsVC, } from "@kya-os/mcp";
19
19
  import { fetchRemoteConfig, normalizeAuthType, } from "@kya-os/mcp-i-runtime"; // product-layer symbols stay on OLD (C2 #2918)
20
20
  import { WebCryptoProvider } from "../providers/crypto";
21
21
  import { ConsentAuditService } from "./consent-audit.service";
22
22
  import { CloudflareProofGenerator } from "../proof-generator";
23
23
  import { ProofService } from "./proof.service";
24
+ import { withMandateConstraints } from "./delegation-mandate";
24
25
  import { createCredentialAuthHandler } from "./credential-auth.handler";
25
26
  import { IdpTokenStorage } from "./idp-token-storage";
26
27
  import { OAuthSecurityService } from "./oauth-security.service";
@@ -54,6 +55,10 @@ function friendlyAgentNameFromDid(did) {
54
55
  .replace(/[-_]+/g, " ")
55
56
  .replace(/\b\w/g, (c) => c.toUpperCase());
56
57
  }
58
+ const MANDATE_ERROR = {
59
+ error: "Delegation limits could not be resolved",
60
+ error_code: "mandate_error",
61
+ };
57
62
  export class ConsentService {
58
63
  configService;
59
64
  renderer;
@@ -66,19 +71,56 @@ export class ConsentService {
66
71
  // Phase 2: Provider resolution services (optional for backward compatibility)
67
72
  providerResolver;
68
73
  providerRegistry;
74
+ delegationMandate;
69
75
  /**
70
76
  * ✅ FIXED: Constructor takes env: CloudflareEnv, not config
71
77
  * Phase 2: Optional providerResolver and providerRegistry for tool-specific providers
72
78
  */
73
- constructor(env, runtime, providerResolver, providerRegistry) {
79
+ constructor(env, runtime, providerResolver, providerRegistry, options = {}) {
74
80
  this.env = env;
75
81
  this.runtime = runtime;
76
82
  this.configService = new ConsentConfigService(env);
77
83
  this.renderer = new TemplateRenderer({ useCSR: true });
78
84
  this.providerResolver = providerResolver;
79
85
  this.providerRegistry = providerRegistry;
86
+ this.delegationMandate = options.delegationMandate;
80
87
  // No initialization here - keep constructor synchronous
81
88
  }
89
+ /**
90
+ * Ask the server's mandate provider about these scopes.
91
+ *
92
+ * Keyed on scopes (what the grant authorizes), never on a tool name, so a
93
+ * request naming a different tool with the same scopes gets the same
94
+ * limits. The provider gets the runtime identity DID, so a mandate can name
95
+ * this server as the merchant without trusting any request field.
96
+ *
97
+ * Without a provider this always returns `{ ok: true, mandate: undefined }`.
98
+ * With one, a missing identity or a throw returns `{ ok: false }`.
99
+ */
100
+ async resolveMandate(scopes, projectId) {
101
+ const provider = this.delegationMandate;
102
+ if (!provider)
103
+ return { ok: true, mandate: undefined };
104
+ try {
105
+ const serverDid = (await this.runtime?.getIdentity())?.did;
106
+ if (!serverDid) {
107
+ logger.error("[ConsentService] Delegation mandate configured but the server identity is unavailable");
108
+ return { ok: false };
109
+ }
110
+ const mandate = provider({ scopes: [...scopes], serverDid, projectId });
111
+ return { ok: true, mandate };
112
+ }
113
+ catch (error) {
114
+ logger.error("[ConsentService] Delegation mandate provider failed:", error);
115
+ return { ok: false };
116
+ }
117
+ }
118
+ mandateErrorResponse() {
119
+ return new Response(JSON.stringify({ success: false, ...MANDATE_ERROR }), {
120
+ status: 500,
121
+ headers: { "Content-Type": "application/json" },
122
+ });
123
+ }
82
124
  /**
83
125
  * Get or initialize audit service (lazy initialization)
84
126
  *
@@ -182,7 +224,7 @@ export class ConsentService {
182
224
  }
183
225
  }
184
226
  /**
185
- * Fetch CloudflareRuntimeConfig from remote API (AgentShield)
227
+ * Fetch CloudflareRuntimeConfig from remote API (Checkpoint)
186
228
  *
187
229
  * ⚠️ CRITICAL: Config MUST be fetched from remote API, not constructed from env.
188
230
  *
@@ -193,7 +235,7 @@ export class ConsentService {
193
235
  * @returns Runtime config or undefined if unavailable
194
236
  */
195
237
  async getConfigFromRemoteAPI(projectId) {
196
- if (!this.env.AGENTSHIELD_API_KEY) {
238
+ if (!this.env.CHECKPOINT_API_KEY) {
197
239
  logger.warn("[ConsentService] No API key for runtime config fetch");
198
240
  return undefined;
199
241
  }
@@ -212,15 +254,15 @@ export class ConsentService {
212
254
  }
213
255
  : undefined;
214
256
  const config = await fetchRemoteConfig({
215
- apiUrl: this.env.AGENTSHIELD_API_URL || "https://kya.vouched.id",
216
- apiKey: this.env.AGENTSHIELD_API_KEY,
257
+ apiUrl: this.env.CHECKPOINT_API_URL || "https://kya.vouched.id",
258
+ apiKey: this.env.CHECKPOINT_API_KEY,
217
259
  projectId, // ✅ Use projectId from consent request
218
260
  cacheTtl: 300000, // 5 minutes
219
261
  fetchProvider: fetch,
220
262
  }, cache);
221
263
  // Populate serverDid from runtime identity if missing or empty
222
- // This prevents AgentShield validation errors (serverDid must be at least 1 character)
223
- // Note: MCPIConfig.identity is RuntimeIdentityConfig (no serverDid), but AgentShield
264
+ // This prevents Checkpoint validation errors (serverDid must be at least 1 character)
265
+ // Note: MCPIConfig.identity is RuntimeIdentityConfig (no serverDid), but Checkpoint
224
266
  // may return MCPIServerConfig format (has serverDid). We need to handle both.
225
267
  if (config && config.identity) {
226
268
  const identityConfig = config.identity; // Type assertion for serverDid field
@@ -374,11 +416,11 @@ export class ConsentService {
374
416
  /**
375
417
  * Update session with resolved identity (Phase 5)
376
418
  *
377
- * Called after AgentShield identity/resolve returns a persistent user DID.
419
+ * Called after Checkpoint identity/resolve returns a persistent user DID.
378
420
  * Stores the DID in session storage and creates OAuth mapping.
379
421
  *
380
422
  * @param sessionId - Session ID
381
- * @param userDid - Persistent user DID from AgentShield
423
+ * @param userDid - Persistent user DID from Checkpoint
382
424
  * @param oauthIdentity - OAuth identity for creating persistent mapping
383
425
  */
384
426
  async updateSessionWithIdentity(sessionId, userDid, oauthIdentity) {
@@ -537,8 +579,8 @@ export class ConsentService {
537
579
  * GENERATES a new key pair if one doesn't exist.
538
580
  *
539
581
  * NOTE: The generated key pair has its own did:key which may differ
540
- * from AgentShield's persistentUserDid. Both are tied to the same
541
- * OAuth identity. See docs/proposals/VC/AGENTSHIELD_SIGNING_ENDPOINT.md
582
+ * from Checkpoint's persistentUserDid. Both are tied to the same
583
+ * OAuth identity. See docs/proposals/VC/CHECKPOINT_SIGNING_ENDPOINT.md
542
584
  * for the future cleaner architecture.
543
585
  *
544
586
  * @param sessionId - Session ID for caching
@@ -889,7 +931,7 @@ export class ConsentService {
889
931
  *
890
932
  * Determines if OAuth flow should be used instead of direct delegation creation.
891
933
  * OAuth is required if:
892
- * - Project has OAuth provider configured in AgentShield
934
+ * - Project has OAuth provider configured in Checkpoint
893
935
  * - No OAuth identity is present in the request
894
936
  *
895
937
  * @param projectId - Project ID to check
@@ -960,14 +1002,14 @@ export class ConsentService {
960
1002
  }
961
1003
  // Check project config to see if OAuth is configured
962
1004
  try {
963
- const agentShieldUrl = this.env.AGENTSHIELD_API_URL || DEFAULT_AGENTSHIELD_URL;
964
- const apiKey = this.env.AGENTSHIELD_API_KEY;
1005
+ const checkpointUrl = this.env.CHECKPOINT_API_URL || DEFAULT_CHECKPOINT_URL;
1006
+ const apiKey = this.env.CHECKPOINT_API_KEY;
965
1007
  if (!apiKey) {
966
1008
  // No API key - can't check, assume OAuth not required
967
1009
  return false;
968
1010
  }
969
1011
  // Fetch project config to check for OAuth provider
970
- const configUrl = `${agentShieldUrl}/api/v1/bouncer/projects/${projectId}/config`;
1012
+ const configUrl = `${checkpointUrl}/api/v1/bouncer/projects/${projectId}/config`;
971
1013
  const response = await fetch(configUrl, {
972
1014
  headers: {
973
1015
  Authorization: `Bearer ${apiKey}`,
@@ -1007,15 +1049,15 @@ export class ConsentService {
1007
1049
  * @param serverUrl - Server URL for callback
1008
1050
  * @param provider - Optional provider name (resolved if not provided)
1009
1051
  * @param oauthSecurityService - Optional OAuthSecurityService for CSRF protection
1010
- * @param agentName - Optional human-readable agent name for AgentShield dashboard display
1052
+ * @param agentName - Optional human-readable agent name for Checkpoint dashboard display
1011
1053
  * @param tool - Optional tool name to preserve through OAuth flow for clickwrap redirect
1012
1054
  * @returns OAuth authorization URL
1013
1055
  */
1014
1056
  async buildOAuthUrl(projectId, agentDid, sessionId, scopes, serverUrl, provider, // Phase 2: Optional provider name (resolved if not provided)
1015
- oauthSecurityService, agentName, // Human-readable agent name for AgentShield dashboard
1057
+ oauthSecurityService, agentName, // Human-readable agent name for Checkpoint dashboard
1016
1058
  tool // Tool name to preserve through OAuth callback for clickwrap redirect
1017
1059
  ) {
1018
- const agentShieldUrl = this.env.AGENTSHIELD_API_URL || DEFAULT_AGENTSHIELD_URL;
1060
+ const checkpointUrl = this.env.CHECKPOINT_API_URL || DEFAULT_CHECKPOINT_URL;
1019
1061
  // Generate a temporary delegation ID for state (will be created after OAuth)
1020
1062
  const delegationId = `temp-${Date.now()}`;
1021
1063
  // ✅ Phase 1: Generate PKCE challenge if OAuthSecurityService is available
@@ -1086,17 +1128,17 @@ export class ConsentService {
1086
1128
  stateParam = btoa(JSON.stringify(stateData));
1087
1129
  }
1088
1130
  // Phase 3: Resolve provider config and extract custom parameters
1089
- // Load providers from AgentShield API if not already loaded
1131
+ // Load providers from Checkpoint API if not already loaded
1090
1132
  let providerConfig = null;
1091
1133
  if (provider && this.providerRegistry) {
1092
1134
  // Lazy load providers if registry is empty (first request for this project)
1093
1135
  if (this.providerRegistry.getProviderNames().length === 0) {
1094
- logger.debug("[ConsentService] Loading providers from AgentShield", {
1136
+ logger.debug("[ConsentService] Loading providers from Checkpoint", {
1095
1137
  projectId,
1096
1138
  provider,
1097
1139
  });
1098
1140
  try {
1099
- await this.providerRegistry.loadFromAgentShield(projectId);
1141
+ await this.providerRegistry.loadFromCheckpoint(projectId);
1100
1142
  logger.debug("[ConsentService] Providers loaded successfully", {
1101
1143
  projectId,
1102
1144
  providers: this.providerRegistry.getProviderNames(),
@@ -1104,7 +1146,7 @@ export class ConsentService {
1104
1146
  });
1105
1147
  }
1106
1148
  catch (loadError) {
1107
- logger.warn("[ConsentService] Failed to load providers from AgentShield", {
1149
+ logger.warn("[ConsentService] Failed to load providers from Checkpoint", {
1108
1150
  projectId,
1109
1151
  error: loadError instanceof Error
1110
1152
  ? loadError.message
@@ -1136,7 +1178,7 @@ export class ConsentService {
1136
1178
  : "using direct PKCE mode",
1137
1179
  });
1138
1180
  // Direct OAuth Mode: Use provider's authorization URL directly for PKCE-capable providers
1139
- // This bypasses AgentShield bouncer and redirects straight to GitHub/Google/etc.
1181
+ // This bypasses Checkpoint bouncer and redirects straight to GitHub/Google/etc.
1140
1182
  //
1141
1183
  // Flow: User → GitHub OAuth → MCP-I /oauth/callback → Success page
1142
1184
  //
@@ -1145,7 +1187,7 @@ export class ConsentService {
1145
1187
  // - proxyMode: false = no client secret needed, direct exchange with provider
1146
1188
  //
1147
1189
  // When proxyMode is true (e.g., GitHub OAuth Apps with client_secret), we must
1148
- // use AgentShield bouncer to exchange tokens because it has the client_secret.
1190
+ // use Checkpoint bouncer to exchange tokens because it has the client_secret.
1149
1191
  // See oauth-service.ts lines 82, 372 for the same pattern.
1150
1192
  //
1151
1193
  // Examples: GitHub Apps (PKCE, no proxy), Google (PKCE, no proxy)
@@ -1153,7 +1195,7 @@ export class ConsentService {
1153
1195
  if (providerConfig &&
1154
1196
  providerConfig.supportsPKCE &&
1155
1197
  !providerConfig.proxyMode) {
1156
- // Use providerConfig.clientId from AgentShield dashboard config
1198
+ // Use providerConfig.clientId from Checkpoint dashboard config
1157
1199
  const oauthClientId = providerConfig.clientId || projectId;
1158
1200
  // The `scopes` argument holds MCP-I delegation scopes (e.g. "greet:execute"),
1159
1201
  // which the identity provider has never heard of. Only the provider's own
@@ -1194,11 +1236,11 @@ export class ConsentService {
1194
1236
  }
1195
1237
  }
1196
1238
  }
1197
- // Build OAuth authorization URL (AgentShield bouncer endpoint)
1198
- // NOTE: This is a legacy proxy mode path that requires AgentShield to act as an OAuth proxy.
1239
+ // Build OAuth authorization URL (Checkpoint bouncer endpoint)
1240
+ // NOTE: This is a legacy proxy mode path that requires Checkpoint to act as an OAuth proxy.
1199
1241
  // Most providers (GitHub, Google) use direct PKCE mode instead (see buildDirectOAuthUrl).
1200
1242
  // If this path is hit and results in a 404, check if the provider supports PKCE.
1201
- const oauthUrl = new URL(`${agentShieldUrl}/bouncer/oauth/authorize`);
1243
+ const oauthUrl = new URL(`${checkpointUrl}/bouncer/oauth/authorize`);
1202
1244
  oauthUrl.searchParams.set("response_type", "code");
1203
1245
  oauthUrl.searchParams.set("client_id", projectId); // Use projectId as client_id
1204
1246
  oauthUrl.searchParams.set("redirect_uri", `${serverUrl}/oauth/callback`);
@@ -1206,14 +1248,14 @@ export class ConsentService {
1206
1248
  // delegation scopes, not what belongs in the IdP-facing `scope` param.
1207
1249
  oauthUrl.searchParams.set("scope", resolveIdpScopes(providerConfig).join(" "));
1208
1250
  oauthUrl.searchParams.set("state", stateParam);
1209
- // ✅ Pass provider to AgentShield bouncer so it selects the correct auth method
1251
+ // ✅ Pass provider to Checkpoint bouncer so it selects the correct auth method
1210
1252
  // This is critical when multiple providers are configured for a project
1211
1253
  // e.g., "credentials" for credential login or "github" for GitHub OAuth
1212
- // Without this, AgentShield picks the first provider which may be incorrect
1254
+ // Without this, Checkpoint picks the first provider which may be incorrect
1213
1255
  if (provider) {
1214
1256
  oauthUrl.searchParams.set("provider", provider);
1215
1257
  }
1216
- // ✅ Add agent_name for AgentShield dashboard display
1258
+ // ✅ Add agent_name for Checkpoint dashboard display
1217
1259
  // This provides a human-readable name instead of just the agent DID
1218
1260
  if (agentName) {
1219
1261
  oauthUrl.searchParams.set("agent_name", agentName);
@@ -1232,7 +1274,7 @@ export class ConsentService {
1232
1274
  return oauthUrl.toString();
1233
1275
  }
1234
1276
  /**
1235
- * Build direct OAuth URL (bypasses AgentShield bouncer endpoint)
1277
+ * Build direct OAuth URL (bypasses Checkpoint bouncer endpoint)
1236
1278
  *
1237
1279
  * Used for custom providers that need direct OAuth URLs (e.g., self-hosted OAuth servers).
1238
1280
  * Triggered when providerConfig.proxyMode === true && !providerConfig.supportsPKCE
@@ -1264,7 +1306,7 @@ export class ConsentService {
1264
1306
  * Link OAuth identity to User DID
1265
1307
  *
1266
1308
  * Maps OAuth provider identity (provider + subject) to a persistent User DID.
1267
- * Phase 5: Requires existing userDid from AgentShield /identity/resolve endpoint.
1309
+ * Phase 5: Requires existing userDid from Checkpoint /identity/resolve endpoint.
1268
1310
  * Throws error if session has no user DID (enforces OAuth → identity flow).
1269
1311
  *
1270
1312
  * @param oauthIdentity - OAuth provider identity
@@ -1297,8 +1339,8 @@ export class ConsentService {
1297
1339
  // Phase 5: Check for existing user DID in session
1298
1340
  const userDid = await this.getUserDidForSession(sessionId);
1299
1341
  if (!userDid) {
1300
- // Phase 5: No existing DID - user must complete OAuth via AgentShield first
1301
- throw new Error("[ConsentService] No user DID found for session - OAuth identity resolution required via AgentShield");
1342
+ // Phase 5: No existing DID - user must complete OAuth via Checkpoint first
1343
+ throw new Error("[ConsentService] No user DID found for session - OAuth identity resolution required via Checkpoint");
1302
1344
  }
1303
1345
  // Store OAuth identity mapping (persistent - 90 days)
1304
1346
  try {
@@ -1323,14 +1365,14 @@ export class ConsentService {
1323
1365
  return userDid;
1324
1366
  }
1325
1367
  /**
1326
- * Cache an externally-resolved User DID (from AgentShield identity resolution)
1368
+ * Cache an externally-resolved User DID (from Checkpoint identity resolution)
1327
1369
  *
1328
- * Phase 5: When AgentShield provides a persistent user_did, we cache the
1370
+ * Phase 5: When Checkpoint provides a persistent user_did, we cache the
1329
1371
  * OAuth identity → userDid mapping in KV so future lookups don't require
1330
- * calling AgentShield again.
1372
+ * calling Checkpoint again.
1331
1373
  *
1332
1374
  * @param oauthIdentity - OAuth identity from provider
1333
- * @param userDid - Persistent user DID from AgentShield
1375
+ * @param userDid - Persistent user DID from Checkpoint
1334
1376
  * @returns void
1335
1377
  */
1336
1378
  async cacheExternalUserDid(oauthIdentity, userDid) {
@@ -1351,7 +1393,7 @@ export class ConsentService {
1351
1393
  existingUserDid: existingUserDid.substring(0, 30) + "...",
1352
1394
  newUserDid: userDid.substring(0, 30) + "...",
1353
1395
  });
1354
- // Don't overwrite - AgentShield is source of truth
1396
+ // Don't overwrite - Checkpoint is source of truth
1355
1397
  }
1356
1398
  return;
1357
1399
  }
@@ -1370,11 +1412,11 @@ export class ConsentService {
1370
1412
  await this.env.DELEGATION_STORAGE.put(oauthIdentityKey, JSON.stringify(oauthIdentity), {
1371
1413
  expirationTtl: 90 * 24 * 60 * 60, // 90 days
1372
1414
  });
1373
- logger.debug("[ConsentService] Cached external User DID from AgentShield:", {
1415
+ logger.debug("[ConsentService] Cached external User DID from Checkpoint:", {
1374
1416
  provider: oauthIdentity.provider,
1375
1417
  subject: oauthIdentity.subject.substring(0, 20) + "...",
1376
1418
  userDid: userDid.substring(0, 30) + "...",
1377
- source: "agentshield_identity_resolution",
1419
+ source: "checkpoint_identity_resolution",
1378
1420
  });
1379
1421
  }
1380
1422
  catch (error) {
@@ -1530,7 +1572,7 @@ export class ConsentService {
1530
1572
  // e.g., ?provider_type=password indicates credential auth is expected
1531
1573
  const urlProviderType = params.get("provider_type");
1532
1574
  // ✅ Extract agent_name from URL query param (passed from buildConsentUrl)
1533
- // Human-readable name for AgentShield dashboard display
1575
+ // Human-readable name for Checkpoint dashboard display
1534
1576
  const agentName = params.get("agent_name");
1535
1577
  // ✅ Extract credential auth params for 3-screen flow (Auth → Clickwrap → Success)
1536
1578
  // These are set when credential auth completes and redirects to clickwrap page
@@ -1542,7 +1584,7 @@ export class ConsentService {
1542
1584
  // This ensures storeDelegationToken can use the correct user+agent key immediately
1543
1585
  const userDidFromParams = params.get("user_did");
1544
1586
  // ✅ Extract user info from credential provider for delegation metadata
1545
- // These are used for human-readable display in AgentShield dashboard
1587
+ // These are used for human-readable display in Checkpoint dashboard
1546
1588
  const credentialUserEmail = params.get("credential_user_email");
1547
1589
  const credentialProviderUserId = params.get("credential_provider_user_id");
1548
1590
  // ✅ Extract mode param for post-auth clickwrap detection
@@ -1584,6 +1626,13 @@ export class ConsentService {
1584
1626
  const scopes = scopesParam
1585
1627
  ? scopesParam.split(",").map((s) => s.trim())
1586
1628
  : [];
1629
+ // Resolve the server-bound mandate before anything renders: with a
1630
+ // provider configured, a failure must never produce an approvable page.
1631
+ const mandateResolution = await this.resolveMandate(scopes, projectId);
1632
+ if (!mandateResolution.ok) {
1633
+ return this.mandateErrorResponse();
1634
+ }
1635
+ const mandate = mandateResolution.mandate;
1587
1636
  // Get tool description (try to fetch from runtime or use default)
1588
1637
  let toolDescription = `Execute ${tool}`;
1589
1638
  if (this.runtime) {
@@ -1596,7 +1645,7 @@ export class ConsentService {
1596
1645
  // Fallback to default
1597
1646
  }
1598
1647
  }
1599
- // Get consent config from AgentShield or defaults
1648
+ // Get consent config from Checkpoint or defaults
1600
1649
  const consentConfig = await this.configService.getConsentConfig(projectId);
1601
1650
  // Build server URL from request origin
1602
1651
  // Priority: 1) env var, 2) request origin, 3) error if neither available
@@ -1835,7 +1884,7 @@ export class ConsentService {
1835
1884
  // Note: We're building it here, but we'll pass it to the renderer
1836
1885
  // instead of redirecting immediately, so the user sees the consent page first.
1837
1886
  resolvedOAuthUrl = await this.buildOAuthUrl(projectId, agentDid, sessionId, scopes, serverUrl, provider, // Pass resolved provider for direct OAuth mode (PKCE)
1838
- oauthSecurityService, agentName || undefined, // Human-readable agent name for AgentShield dashboard
1887
+ oauthSecurityService, agentName || undefined, // Human-readable agent name for Checkpoint dashboard
1839
1888
  tool || undefined // Tool name to preserve through OAuth callback for clickwrap redirect
1840
1889
  );
1841
1890
  isOAuthRequired = true;
@@ -1937,7 +1986,7 @@ export class ConsentService {
1937
1986
  // Build consent page config
1938
1987
  // ✅ CRITICAL: Pass the FULL consent config (ui, credentials, etc.) not just branding/terms
1939
1988
  // Type assertion to handle updated schema before rebuild
1940
- // The remote consent config (from agent-shield) carries fields that
1989
+ // The remote consent config (from checkpoint) carries fields that
1941
1990
  // aren't in the trimmed contracts ConsentPageConfig type. Read them off
1942
1991
  // a Record<string, unknown> view so we can plumb them through without
1943
1992
  // touching contracts.
@@ -2058,8 +2107,13 @@ export class ConsentService {
2058
2107
  ui: consentConfig.ui,
2059
2108
  // ✅ FIX: Pass credentials config (username/password labels, forgot password, etc.)
2060
2109
  credentials: consentConfig.credentials,
2061
- // ✅ FIX: Pass expiration days
2062
- expirationDays: consentConfig.expirationDays,
2110
+ // ✅ FIX: Pass expiration days. A mandate's fixed lifetime wins, and is
2111
+ // also stated as fixedExpiryDays so the footer says "N days after
2112
+ // approval" instead of the (false, for these grants) inactivity copy.
2113
+ expirationDays: mandate?.expiresInDays ?? consentConfig.expirationDays,
2114
+ ...(mandate?.expiresInDays !== undefined
2115
+ ? { fixedExpiryDays: mandate.expiresInDays }
2116
+ : {}),
2063
2117
  // Pass OAuth details for client-side handling
2064
2118
  oauthRequired: isOAuthRequired,
2065
2119
  oauthUrl: resolvedOAuthUrl,
@@ -2083,12 +2137,14 @@ export class ConsentService {
2083
2137
  // from the bouncer config; falls back to the @kya-os/consent default
2084
2138
  // registry resolved from the URL's scopes, so the new layout renders
2085
2139
  // even when the dashboard hasn't pushed capability metadata yet.
2086
- capabilities: resolvedCapabilities,
2140
+ // A mandate's capabilities describe the limits the grant will carry,
2141
+ // so they replace remote and registry rows.
2142
+ capabilities: mandate?.capabilities ?? resolvedCapabilities,
2087
2143
  agentMetadata: resolvedAgentMetadata,
2088
2144
  consentTheme: remoteConsentTheme,
2089
2145
  orgName: resolvedOrgName,
2090
2146
  headlineVerb: remoteHeadlineVerb,
2091
- revocationPath: remoteRevocationPath,
2147
+ revocationPath: mandate?.revocationPath ?? remoteRevocationPath,
2092
2148
  inactivityDays: remoteInactivityDays,
2093
2149
  howItWorksUrl: remoteHowItWorksUrl,
2094
2150
  // Bind operator-authored Cedar fragment placeholders at render time
@@ -2109,7 +2165,7 @@ export class ConsentService {
2109
2165
  // 2. Must NOT be in post-credential clickwrap mode (would cause infinite loop)
2110
2166
  if (credentialProviderConfig && !isPostCredentialClickwrap) {
2111
2167
  // Determine authorization type from tool protection config
2112
- // Default to 'password' for credential flows (matches AgentShield expectations)
2168
+ // Default to 'password' for credential flows (matches Checkpoint expectations)
2113
2169
  // The authorization.type from tool protection tells us what type of auth the tool expects
2114
2170
  const authorizationType = protection?.authorization?.type ?? "password";
2115
2171
  logger.debug("[ConsentService] Credential provider detected, rendering credential page", {
@@ -3269,7 +3325,7 @@ export class ConsentService {
3269
3325
  /**
3270
3326
  * Handle consent approval
3271
3327
  *
3272
- * Validates request, creates delegation via AgentShield API,
3328
+ * Validates request, creates delegation via Checkpoint API,
3273
3329
  * stores token in KV, and returns success response.
3274
3330
  *
3275
3331
  * @param request - Approval request
@@ -3303,7 +3359,7 @@ export class ConsentService {
3303
3359
  let trustedUserDid;
3304
3360
  // CRED-003: Check for credential provider submission
3305
3361
  // Credential submissions include `provider_type: 'credential'` or 'password' and are handled separately
3306
- // IMPORTANT: AgentShield tool protection uses 'password' for credential-based auth validation,
3362
+ // IMPORTANT: Checkpoint tool protection uses 'password' for credential-based auth validation,
3307
3363
  // but we also accept 'credential' for backward compatibility
3308
3364
  if (providerType === CONSENT_PROVIDER_TYPES.CREDENTIAL ||
3309
3365
  providerType === CONSENT_PROVIDER_TYPES.PASSWORD) {
@@ -3632,7 +3688,7 @@ export class ConsentService {
3632
3688
  headers: { "Content-Type": "application/json" },
3633
3689
  });
3634
3690
  }
3635
- // Create delegation via AgentShield API
3691
+ // Create delegation via Checkpoint API
3636
3692
  logger.debug("[ConsentService] Creating delegation...");
3637
3693
  const delegationResult = await this.createDelegation(approvalRequest, resolveExpirationDays(consentConfig.expirationDays), resolvedUserDid ?? null);
3638
3694
  if (!delegationResult.success) {
@@ -3732,14 +3788,21 @@ export class ConsentService {
3732
3788
  }
3733
3789
  }
3734
3790
  /**
3735
- * Create delegation via AgentShield API
3791
+ * Create delegation via Checkpoint API
3736
3792
  *
3737
3793
  * @param request - Approval request
3738
3794
  * @returns Delegation creation result
3739
3795
  */
3740
3796
  async createDelegation(request, expirationDaysOverride, resolvedUserDid) {
3741
- const agentShieldUrl = this.env.AGENTSHIELD_API_URL || DEFAULT_AGENTSHIELD_URL;
3742
- const apiKey = this.env.AGENTSHIELD_API_KEY;
3797
+ // Resolve the mandate from the APPROVED scopes first: nothing (no VC, no
3798
+ // POST) may happen when a configured provider cannot answer.
3799
+ const mandateResolution = await this.resolveMandate(request.scopes ?? [], request.project_id);
3800
+ if (!mandateResolution.ok) {
3801
+ return { success: false, ...MANDATE_ERROR };
3802
+ }
3803
+ const mandate = mandateResolution.mandate;
3804
+ const checkpointUrl = this.env.CHECKPOINT_API_URL || DEFAULT_CHECKPOINT_URL;
3805
+ const apiKey = this.env.CHECKPOINT_API_KEY;
3743
3806
  if (!apiKey) {
3744
3807
  logger.warn("[ConsentService] No API key configured, cannot create delegation");
3745
3808
  return {
@@ -3806,7 +3869,11 @@ export class ConsentService {
3806
3869
  logger.debug("[ConsentService] No session_id provided - skipping User DID generation");
3807
3870
  }
3808
3871
  }
3809
- const expiresInDays = expirationDaysOverride ?? DEFAULT_EXPIRATION_DAYS;
3872
+ // A mandate's fixed lifetime is part of the grant the user saw, so it
3873
+ // wins over the consent-config expiry.
3874
+ const expiresInDays = mandate?.expiresInDays ??
3875
+ expirationDaysOverride ??
3876
+ DEFAULT_EXPIRATION_DAYS;
3810
3877
  // Phase 2 VC-Only: Issue Delegation VC if we have a session and userDid
3811
3878
  let credentialJwt;
3812
3879
  if (request.session_id && userDid) {
@@ -3827,6 +3894,8 @@ export class ConsentService {
3827
3894
  scopes,
3828
3895
  notAfter,
3829
3896
  notBefore: nowSec,
3897
+ // The user signs the same limits Checkpoint is asked to hold.
3898
+ ...(mandate ? { crisp: mandate.constraints.crisp } : {}),
3830
3899
  },
3831
3900
  signature: "vc-jwt-signed", // Placeholder - actual signature is in the VC-JWT
3832
3901
  status: "active",
@@ -3848,9 +3917,14 @@ export class ConsentService {
3848
3917
  }
3849
3918
  // Build delegation request with error-based format detection
3850
3919
  // Try full format first, fallback to simplified format on error
3851
- const delegationRequest = await this.buildDelegationRequest(request, userDid, expiresInDays, fieldName, credentialJwt);
3920
+ const builtRequest = await this.buildDelegationRequest(request, userDid, expiresInDays, fieldName, credentialJwt);
3921
+ // Every body tryAPICall may POST carries the mandate's constraints, so
3922
+ // a format fallback can never create an unconstrained grant.
3923
+ const delegationRequest = mandate
3924
+ ? withMandateConstraints(builtRequest, mandate.constraints)
3925
+ : builtRequest;
3852
3926
  logger.debug("[ConsentService] Creating delegation:", {
3853
- url: `${agentShieldUrl}${AGENTSHIELD_ENDPOINTS.DELEGATIONS_CREATE}`,
3927
+ url: `${checkpointUrl}${CHECKPOINT_ENDPOINTS.DELEGATIONS_CREATE}`,
3854
3928
  agentDid: request.agent_did.substring(0, 20) + "...",
3855
3929
  scopes: request.scopes,
3856
3930
  expiresInDays,
@@ -3859,12 +3933,12 @@ export class ConsentService {
3859
3933
  hasUserDid: !!userDid,
3860
3934
  });
3861
3935
  // Error-based format detection: try request format, fallback on error
3862
- const response = await this.tryAPICall(agentShieldUrl, apiKey, delegationRequest);
3936
+ const response = await this.tryAPICall(checkpointUrl, apiKey, delegationRequest);
3863
3937
  if (!response.success) {
3864
3938
  return response;
3865
3939
  }
3866
3940
  const responseData = response.data;
3867
- // Canonical format per @kya-os/contracts/agentshield-api:
3941
+ // Canonical format per @kya-os/contracts/checkpoint-api:
3868
3942
  // Wrapped: { success: true, data: { delegation_id: string, agent_did: string, ... } }
3869
3943
  // Unwrapped: { delegation_id: string, agent_did: string, ... }
3870
3944
  //
@@ -3877,7 +3951,7 @@ export class ConsentService {
3877
3951
  const wrappedResponse = wrappedParse.data;
3878
3952
  const delegationId = wrappedResponse.data.delegation_id;
3879
3953
  // Extract delegation_token (JWT) if present in response
3880
- // AgentShield now returns delegation_token for stateless verification
3954
+ // Checkpoint now returns delegation_token for stateless verification
3881
3955
  const responseDataObj = wrappedResponse.data;
3882
3956
  const delegationToken = responseDataObj.delegation_token;
3883
3957
  logger.info("[ConsentService] ✅ Delegation created successfully:", {
@@ -4236,11 +4310,11 @@ export class ConsentService {
4236
4310
  // Ignore cache errors
4237
4311
  }
4238
4312
  }
4239
- // If we have a cached preference, use it directly
4240
- if (cachedFormat === "full") {
4241
- return this.buildFullFormatRequest(request, userDid, expiresInDays, credentialJwt);
4242
- }
4243
- else if (cachedFormat === "simplified") {
4313
+ // A cached "simplified" skips the full POST. A cached "full" gets the same
4314
+ // try-both request as no cache: a stale or wrongly cached "full" must never
4315
+ // remove the simplified fallback, because an API that rejects the full
4316
+ // format would then fail every approval until the cache expired.
4317
+ if (cachedFormat === "simplified") {
4244
4318
  return this.buildSimplifiedFormatRequest(request, userDid, expiresInDays, fieldName, credentialJwt);
4245
4319
  }
4246
4320
  // No cache - return request that will be tried with error-based detection
@@ -4257,7 +4331,7 @@ export class ConsentService {
4257
4331
  * Handle credential-based authentication submission
4258
4332
  *
4259
4333
  * Authenticates user credentials against customer's endpoint, resolves identity
4260
- * via AgentShield, stores token with usage metadata, and creates delegation.
4334
+ * via Checkpoint, stores token with usage metadata, and creates delegation.
4261
4335
  *
4262
4336
  * @param body - Raw request body with credential fields
4263
4337
  * @param request - Original request (for URL derivation if MCP_SERVER_URL not set)
@@ -4327,7 +4401,7 @@ export class ConsentService {
4327
4401
  }
4328
4402
  }
4329
4403
  try {
4330
- // 1. Fetch credential provider config from AgentShield
4404
+ // 1. Fetch credential provider config from Checkpoint
4331
4405
  const providerConfig = await this.getCredentialProviderConfig(project_id, provider);
4332
4406
  if (!providerConfig) {
4333
4407
  logger.debug("[ConsentService] Credential provider config not found", {
@@ -4377,7 +4451,7 @@ export class ConsentService {
4377
4451
  }), { status: 401, headers: { "Content-Type": "application/json" } });
4378
4452
  }
4379
4453
  logger.info("[ConsentService] ✅ Credential authentication successful");
4380
- // 3. Resolve identity via AgentShield
4454
+ // 3. Resolve identity via Checkpoint
4381
4455
  const identityResult = await this.resolveCredentialIdentity({
4382
4456
  projectId: project_id,
4383
4457
  provider: provider,
@@ -4578,7 +4652,7 @@ export class ConsentService {
4578
4652
  // credential_provider_user_id → user_id (provider's internal ID for business reference)
4579
4653
  // user_did → user_did (cryptographic identity, always present)
4580
4654
  if (authResult.userEmail) {
4581
- // Email for human-readable display in AgentShield dashboard
4655
+ // Email for human-readable display in Checkpoint dashboard
4582
4656
  clickwrapUrl.searchParams.set("credential_user_email", authResult.userEmail);
4583
4657
  }
4584
4658
  if (authResult.userId) {
@@ -4680,7 +4754,7 @@ export class ConsentService {
4680
4754
  return [];
4681
4755
  }
4682
4756
  /**
4683
- * Fetch credential provider config from AgentShield
4757
+ * Fetch credential provider config from Checkpoint
4684
4758
  *
4685
4759
  * Uses the V1 /providers endpoint which returns all configured providers
4686
4760
  * including credential providers with type: 'credential'.
@@ -4688,17 +4762,17 @@ export class ConsentService {
4688
4762
  * @see OAuthConfigService.getOAuthConfig() for the same endpoint usage
4689
4763
  */
4690
4764
  async getCredentialProviderConfig(projectId, providerName) {
4691
- const agentShieldUrl = this.env.AGENTSHIELD_API_URL || DEFAULT_AGENTSHIELD_URL;
4692
- const apiKey = this.env.AGENTSHIELD_API_KEY;
4765
+ const checkpointUrl = this.env.CHECKPOINT_API_URL || DEFAULT_CHECKPOINT_URL;
4766
+ const apiKey = this.env.CHECKPOINT_API_KEY;
4693
4767
  if (!apiKey) {
4694
- logger.warn("[ConsentService] No AgentShield API key configured");
4768
+ logger.warn("[ConsentService] No Checkpoint API key configured");
4695
4769
  return null;
4696
4770
  }
4697
4771
  try {
4698
4772
  // Use the /providers endpoint - same as OAuthConfigService.getOAuthConfig()
4699
4773
  // This endpoint was updated in fix/provider-config to read from oauthProviders JSONB
4700
4774
  // Authentication: Use Authorization: Bearer header for consistency with OAuthConfigService
4701
- const response = await fetch(`${agentShieldUrl}/api/v1/bouncer/projects/${encodeURIComponent(projectId)}/providers`, {
4775
+ const response = await fetch(`${checkpointUrl}/api/v1/bouncer/projects/${encodeURIComponent(projectId)}/providers`, {
4702
4776
  headers: {
4703
4777
  Authorization: `Bearer ${apiKey}`,
4704
4778
  "Content-Type": "application/json",
@@ -4726,13 +4800,13 @@ export class ConsentService {
4726
4800
  if (providerName in providers) {
4727
4801
  const rawProvider = providers[providerName];
4728
4802
  // AUTH-TYPE-UNIFICATION: 'password' is the canonical type for form-based auth
4729
- // AgentShield now returns type: 'password' (updated 2025-12-29)
4803
+ // Checkpoint now returns type: 'password' (updated 2025-12-29)
4730
4804
  // Also accept 'credential' for backward compatibility with existing configurations
4731
4805
  // See: proposals/2025-01-delegation-schema-fix/AUTH-TYPE-UNIFICATION.md
4732
4806
  if (rawProvider.type === CONSENT_PROVIDER_TYPES.PASSWORD ||
4733
4807
  rawProvider.type === CONSENT_PROVIDER_TYPES.CREDENTIAL) {
4734
4808
  // Map snake_case fields to camelCase (same pattern as OAuthConfigService)
4735
- // AgentShield API may return snake_case fields from database
4809
+ // Checkpoint API may return snake_case fields from database
4736
4810
  const provider = this.mapCredentialProviderFields(rawProvider, providerName);
4737
4811
  if (!provider) {
4738
4812
  logger.debug("[ConsentService] Credential provider validation failed:", {
@@ -4759,7 +4833,7 @@ export class ConsentService {
4759
4833
  /**
4760
4834
  * Map snake_case fields to camelCase for CredentialProviderConfig
4761
4835
  *
4762
- * AgentShield API may return snake_case fields from the database.
4836
+ * Checkpoint API may return snake_case fields from the database.
4763
4837
  * This follows the same pattern as OAuthConfigService.getOAuthConfig().
4764
4838
  *
4765
4839
  * @param raw - Raw provider object from API response
@@ -4845,17 +4919,17 @@ export class ConsentService {
4845
4919
  };
4846
4920
  }
4847
4921
  /**
4848
- * Resolve identity via AgentShield for credential-based auth
4922
+ * Resolve identity via Checkpoint for credential-based auth
4849
4923
  */
4850
4924
  async resolveCredentialIdentity(params) {
4851
- const agentShieldUrl = this.env.AGENTSHIELD_API_URL || DEFAULT_AGENTSHIELD_URL;
4852
- const apiKey = this.env.AGENTSHIELD_API_KEY;
4925
+ const checkpointUrl = this.env.CHECKPOINT_API_URL || DEFAULT_CHECKPOINT_URL;
4926
+ const apiKey = this.env.CHECKPOINT_API_KEY;
4853
4927
  if (!apiKey) {
4854
- logger.warn("[ConsentService] No AgentShield API key for identity resolution");
4928
+ logger.warn("[ConsentService] No Checkpoint API key for identity resolution");
4855
4929
  return { success: false };
4856
4930
  }
4857
4931
  try {
4858
- const response = await fetch(`${agentShieldUrl}/api/v1/bouncer/identity/resolve`, {
4932
+ const response = await fetch(`${checkpointUrl}/api/v1/bouncer/identity/resolve`, {
4859
4933
  method: "POST",
4860
4934
  headers: {
4861
4935
  "X-API-Key": apiKey,
@@ -5289,12 +5363,12 @@ export class ConsentService {
5289
5363
  //
5290
5364
  // Fallback Strategy:
5291
5365
  // 1. If email available → user_identifier = email (preferred)
5292
- // 2. If no email → user_identifier = userDid (fallback so AgentShield has something to display)
5293
- // AgentShield will also check metadata.user_email for extraction
5366
+ // 2. If no email → user_identifier = userDid (fallback so Checkpoint has something to display)
5367
+ // Checkpoint will also check metadata.user_email for extraction
5294
5368
  if (userDid) {
5295
5369
  // user_did is the primary identifier - the actual DID
5296
5370
  result.user_did = userDid;
5297
- // user_identifier: Human-readable identifier (email/username) for AgentShield dashboard display
5371
+ // user_identifier: Human-readable identifier (email/username) for Checkpoint dashboard display
5298
5372
  // user_id: Provider's internal ID (e.g., customer ID 696395) for business reference
5299
5373
  // user_did: Cryptographic DID for identity verification
5300
5374
  if (request.credential_user_email) {
@@ -5303,7 +5377,7 @@ export class ConsentService {
5303
5377
  }
5304
5378
  else {
5305
5379
  // No email - fall back to DID so dashboard has something to display
5306
- // AgentShield will detect this is a DID and may display differently
5380
+ // Checkpoint will detect this is a DID and may display differently
5307
5381
  result.user_identifier = userDid;
5308
5382
  }
5309
5383
  // user_id is the provider's internal ID (separate from email)
@@ -5331,7 +5405,7 @@ export class ConsentService {
5331
5405
  }
5332
5406
  // MCP-I Compatibility: Include user_email and user_id in custom_fields (becomes metadata)
5333
5407
  // This matches buildSimplifiedFormatRequest behavior for consistency
5334
- // AgentShield extracts these from metadata when user_identifier is not provided
5408
+ // Checkpoint extracts these from metadata when user_identifier is not provided
5335
5409
  //
5336
5410
  // user_email = human-readable email (for display)
5337
5411
  // user_id = provider's internal ID (for business reference)
@@ -5351,7 +5425,7 @@ export class ConsentService {
5351
5425
  format_version: "full_v1",
5352
5426
  ...(providerType ? { provider_type: providerType } : {}),
5353
5427
  // MCP-I Compatibility: Include user_email and user_id in metadata
5354
- // AgentShield will extract these if user_identifier is not provided
5428
+ // Checkpoint will extract these if user_identifier is not provided
5355
5429
  // user_email = human-readable email (for display)
5356
5430
  // user_id = provider's internal ID (for business reference, e.g., customer ID 696395)
5357
5431
  ...(fullFormatUserEmail ? { user_email: fullFormatUserEmail } : {}),
@@ -5362,7 +5436,7 @@ export class ConsentService {
5362
5436
  ...customFieldsFromRequest,
5363
5437
  };
5364
5438
  if (fullFormatUserEmail || fullFormatProviderUserId) {
5365
- logger.debug("[ConsentService] Full format: Including user info in custom_fields for AgentShield compatibility:", {
5439
+ logger.debug("[ConsentService] Full format: Including user info in custom_fields for Checkpoint compatibility:", {
5366
5440
  user_email: fullFormatUserEmail
5367
5441
  ? fullFormatUserEmail.substring(0, 20) + "..."
5368
5442
  : undefined,
@@ -5398,14 +5472,14 @@ export class ConsentService {
5398
5472
  * Build simplified format request with proper field name
5399
5473
  *
5400
5474
  * CRITICAL: This method MUST NOT include session_id or project_id in the request body.
5401
- * These fields are NOT part of AgentShield's createDelegationSchema:
5402
- * - project_id is extracted from API key context by AgentShield middleware
5475
+ * These fields are NOT part of Checkpoint's createDelegationSchema:
5476
+ * - project_id is extracted from API key context by Checkpoint middleware
5403
5477
  * - session_id is not needed for delegation creation
5404
5478
  *
5405
5479
  * Including these fields will cause validation errors (400 Bad Request).
5406
5480
  */
5407
5481
  buildSimplifiedFormatRequest(request, userDid, expiresInDays, fieldName, credentialJwt) {
5408
- // Build request with ONLY fields that are in AgentShield's schema
5482
+ // Build request with ONLY fields that are in Checkpoint's schema
5409
5483
  // Defensive check: ensure scopes is always defined (should be guaranteed by validation)
5410
5484
  const scopes = request.scopes ?? [];
5411
5485
  const simplifiedRequest = {
@@ -5420,12 +5494,12 @@ export class ConsentService {
5420
5494
  //
5421
5495
  // Fallback Strategy:
5422
5496
  // 1. If email available → user_identifier = email (preferred)
5423
- // 2. If no email → user_identifier = userDid (fallback so AgentShield has something to display)
5424
- // AgentShield will also check metadata.user_email for extraction
5497
+ // 2. If no email → user_identifier = userDid (fallback so Checkpoint has something to display)
5498
+ // Checkpoint will also check metadata.user_email for extraction
5425
5499
  if (userDid) {
5426
5500
  // user_did is the primary identifier - the actual DID
5427
5501
  simplifiedRequest.user_did = userDid;
5428
- // user_identifier: Human-readable identifier (email/username) for AgentShield dashboard display
5502
+ // user_identifier: Human-readable identifier (email/username) for Checkpoint dashboard display
5429
5503
  // user_id: Provider's internal ID (e.g., customer ID 696395) for business reference
5430
5504
  // user_did: Cryptographic DID for identity verification
5431
5505
  if (request.credential_user_email) {
@@ -5434,7 +5508,7 @@ export class ConsentService {
5434
5508
  }
5435
5509
  else {
5436
5510
  // No email - fall back to DID so dashboard has something to display
5437
- // AgentShield will detect this is a DID and may display differently
5511
+ // Checkpoint will detect this is a DID and may display differently
5438
5512
  simplifiedRequest.user_identifier = userDid;
5439
5513
  }
5440
5514
  // user_id is the provider's internal ID (separate from email)
@@ -5465,7 +5539,7 @@ export class ConsentService {
5465
5539
  simplifiedRequest.credential_jwt = credentialJwt;
5466
5540
  logger.debug("[ConsentService] Including credential_jwt in delegation request");
5467
5541
  }
5468
- // AgentShield API only accepts "custom_fields", not "metadata"
5542
+ // Checkpoint API only accepts "custom_fields", not "metadata"
5469
5543
  // Always use "custom_fields" regardless of Day0 config
5470
5544
  // Include provider_type and provider for dashboard authorization display
5471
5545
  //
@@ -5480,7 +5554,7 @@ export class ConsentService {
5480
5554
  const provider = customFieldsFromRequest.provider;
5481
5555
  const hasProviderInfo = providerType || provider;
5482
5556
  // MCP-I Compatibility: Include user_email and user_id in custom_fields (becomes metadata)
5483
- // AgentShield extracts these from metadata when user_identifier is a DID or not provided
5557
+ // Checkpoint extracts these from metadata when user_identifier is a DID or not provided
5484
5558
  // This ensures human-readable identifiers are always available for dashboard display
5485
5559
  //
5486
5560
  // user_email = human-readable email (for display)
@@ -5490,8 +5564,8 @@ export class ConsentService {
5490
5564
  .credential_provider_user_id || null;
5491
5565
  if (userDid || hasProviderInfo || userEmail) {
5492
5566
  // Include issuer_did and subject_did in custom_fields when we have userDid
5493
- // Also include provider_type and provider for AgentShield dashboard
5494
- // CRITICAL: AgentShield derives authorization from metadata.provider_type:
5567
+ // Also include provider_type and provider for Checkpoint dashboard
5568
+ // CRITICAL: Checkpoint derives authorization from metadata.provider_type:
5495
5569
  // - provider_type === 'password' → authorization.type = 'password' (credential auth)
5496
5570
  // - provider_type === 'none' → authorization.type = 'none' (consent-only)
5497
5571
  // - oauth_provider present → authorization.type = 'oauth'
@@ -5504,10 +5578,10 @@ export class ConsentService {
5504
5578
  }
5505
5579
  : {}),
5506
5580
  format_version: "simplified_v1",
5507
- // Include provider_type at the custom_fields level (where AgentShield reads it)
5581
+ // Include provider_type at the custom_fields level (where Checkpoint reads it)
5508
5582
  ...(providerType ? { provider_type: providerType } : {}),
5509
5583
  // MCP-I Compatibility: Include user_email and user_id in metadata
5510
- // AgentShield will extract these if user_identifier is not provided or is a DID
5584
+ // Checkpoint will extract these if user_identifier is not provided or is a DID
5511
5585
  // user_email = human-readable email (for display)
5512
5586
  // user_id = provider's internal ID (for business reference, e.g., customer ID 696395)
5513
5587
  ...(userEmail ? { user_email: userEmail } : {}),
@@ -5525,7 +5599,7 @@ export class ConsentService {
5525
5599
  });
5526
5600
  }
5527
5601
  if (userEmail || providerUserId) {
5528
- logger.debug("[ConsentService] Including user info in custom_fields for AgentShield compatibility:", {
5602
+ logger.debug("[ConsentService] Including user info in custom_fields for Checkpoint compatibility:", {
5529
5603
  user_email: userEmail
5530
5604
  ? userEmail.substring(0, 20) + "..."
5531
5605
  : undefined,
@@ -5550,35 +5624,39 @@ export class ConsentService {
5550
5624
  /**
5551
5625
  * Try API call with error-based format detection
5552
5626
  */
5553
- async tryAPICall(agentShieldUrl, apiKey, request) {
5627
+ async tryAPICall(checkpointUrl, apiKey, request) {
5554
5628
  // Handle format detection
5555
5629
  if (request._tryFormats && request.fullFormat && request.simplifiedFormat) {
5556
5630
  // Try full format first
5557
- const fullResponse = await this.makeAPICall(agentShieldUrl, apiKey, request.fullFormat);
5558
- if (fullResponse.success ||
5559
- fullResponse.error_code !== "validation_error") {
5560
- // Full format worked or failed for non-format reasons
5631
+ const fullResponse = await this.makeAPICall(checkpointUrl, apiKey, request.fullFormat);
5632
+ if (fullResponse.success) {
5561
5633
  await this.cacheFormatPreference("full");
5562
5634
  return fullResponse;
5563
5635
  }
5636
+ // A failure that says nothing about the format (5xx, 401, network) is
5637
+ // returned as is and never cached, so the next approval tries both
5638
+ // formats again instead of skipping the simplified fallback for an hour.
5639
+ if (fullResponse.error_code !== "validation_error") {
5640
+ return fullResponse;
5641
+ }
5564
5642
  // Full format failed with validation error, try simplified
5565
5643
  logger.debug("[ConsentService] Full format failed, trying simplified format...");
5566
- const simplifiedResponse = await this.makeAPICall(agentShieldUrl, apiKey, request.simplifiedFormat);
5644
+ const simplifiedResponse = await this.makeAPICall(checkpointUrl, apiKey, request.simplifiedFormat);
5567
5645
  if (simplifiedResponse.success) {
5568
5646
  await this.cacheFormatPreference("simplified");
5569
5647
  }
5570
5648
  return simplifiedResponse;
5571
5649
  }
5572
5650
  // Direct call (format already determined)
5573
- return this.makeAPICall(agentShieldUrl, apiKey, request);
5651
+ return this.makeAPICall(checkpointUrl, apiKey, request);
5574
5652
  }
5575
5653
  /**
5576
5654
  * Make API call and parse response
5577
5655
  *
5578
- * CRITICAL: This method ensures session_id and project_id are never sent to AgentShield.
5656
+ * CRITICAL: This method ensures session_id and project_id are never sent to Checkpoint.
5579
5657
  * These fields are NOT part of the createDelegationSchema and will cause validation errors.
5580
5658
  */
5581
- async makeAPICall(agentShieldUrl, apiKey, requestBody) {
5659
+ async makeAPICall(checkpointUrl, apiKey, requestBody) {
5582
5660
  try {
5583
5661
  // FINAL SAFEGUARD: Remove session_id and project_id if they somehow got added
5584
5662
  // This provides defense-in-depth protection
@@ -5591,7 +5669,7 @@ export class ConsentService {
5591
5669
  logger.warn("[ConsentService] ⚠️ project_id detected in request body - removing (not in schema)");
5592
5670
  delete sanitizedBody.project_id;
5593
5671
  }
5594
- const response = await fetch(`${agentShieldUrl}${AGENTSHIELD_ENDPOINTS.DELEGATIONS_CREATE}`, {
5672
+ const response = await fetch(`${checkpointUrl}${CHECKPOINT_ENDPOINTS.DELEGATIONS_CREATE}`, {
5595
5673
  method: "POST",
5596
5674
  headers: {
5597
5675
  Authorization: `Bearer ${apiKey}`,