( request: ProviderRequest, providerId: ProviderId | string )
| 45 | * handle on an untrusted request body can never survive to a builder or trigger a fetch. |
| 46 | */ |
| 47 | export async function attachLargeFileRemoteUrls( |
| 48 | request: ProviderRequest, |
| 49 | providerId: ProviderId | string |
| 50 | ): Promise<void> { |
| 51 | for (const file of iterateRequestFiles(request.messages)) { |
| 52 | file.providerFileId = undefined |
| 53 | file.providerFileUri = undefined |
| 54 | file.remoteUrl = undefined |
| 55 | } |
| 56 | |
| 57 | if (getProviderFileStrategy(providerId) === 'inline') return |
| 58 | |
| 59 | const requestId = request.workflowId ?? 'provider-request' |
| 60 | const maxBytes = getProviderAttachmentMaxBytes(providerId) |
| 61 | |
| 62 | for (const file of iterateRequestFiles(request.messages)) { |
| 63 | if (!file.key || !shouldUseLargeFilePath(file, providerId)) continue |
| 64 | |
| 65 | if (Number.isFinite(file.size) && file.size > maxBytes) { |
| 66 | const sizeMB = (file.size / (1024 * 1024)).toFixed(2) |
| 67 | const maxMB = (maxBytes / (1024 * 1024)).toFixed(0) |
| 68 | throw new Error( |
| 69 | `File "${file.name}" (${sizeMB}MB) exceeds the ${maxMB}MB agent attachment limit for provider "${providerId}"` |
| 70 | ) |
| 71 | } |
| 72 | |
| 73 | if (!StorageService.hasCloudStorage()) { |
| 74 | logger.warn( |
| 75 | `[${requestId}] "${file.name}" exceeds the inline limit for "${providerId}" but cloud storage is unavailable` |
| 76 | ) |
| 77 | throw new Error( |
| 78 | `File "${file.name}" exceeds the inline attachment limit and requires cloud file storage, which is not configured` |
| 79 | ) |
| 80 | } |
| 81 | |
| 82 | if (!request.userId) { |
| 83 | throw new Error( |
| 84 | `File "${file.name}" requires an authenticated user for provider "${providerId}"` |
| 85 | ) |
| 86 | } |
| 87 | |
| 88 | const context = (file.context as StorageContext) || inferContextFromKey(file.key) |
| 89 | const hasAccess = await verifyFileAccess(file.key, request.userId, undefined, context, false) |
| 90 | if (!hasAccess) { |
| 91 | throw new Error(`File "${file.name}" is not accessible for provider "${providerId}"`) |
| 92 | } |
| 93 | |
| 94 | file.remoteUrl = await StorageService.generatePresignedDownloadUrl( |
| 95 | file.key, |
| 96 | context, |
| 97 | PRESIGNED_URL_EXPIRY_SECONDS |
| 98 | ) |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | /** |
| 103 | * For `files-api` providers, uploads each large attachment (already carrying a signed |
no test coverage detected