Skip to content

feat(providers): support large agent-block attachments via Files APIs and remote URLs#5092

Merged
waleedlatif1 merged 16 commits into
stagingfrom
worktree-provider-files-api
Jun 16, 2026
Merged

feat(providers): support large agent-block attachments via Files APIs and remote URLs#5092
waleedlatif1 merged 16 commits into
stagingfrom
worktree-provider-files-api

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • Agent-block file attachments were inlined as base64 with a hard 10MB cap. Files above the threshold now route through each provider's native large-file path instead.
  • OpenAI / Gemini: upload to the provider Files API, reference by file_id / fileUri (with processing-state polling for Gemini, expires_after auto-expiry for OpenAI).
  • Anthropic: GA url content-block source (URLImageSource / URLPDFSource) — no Files API beta header, no upload, no lifecycle.
  • OpenRouter / Groq / Together / Baseten / xAI / vLLM: pass a short-lived signed HTTPS URL in image_url (and OpenRouter file.file_data for PDFs) instead of base64.
  • Per-provider limits + strategy live in models.ts at the provider level; the agent block's file input and the /models provider pages now reflect the real ceiling.
  • Files ≤10MB keep the identical base64 path — zero regression. Providers without a large-file path (Azure, Vertex, Bedrock, Mistral, Ollama, LiteLLM, DeepSeek, Cerebras) are unchanged.

Architecture

  • agent-handler mints a presigned download URL onto large files (has execution context + access checks); base64 hydration stays capped at the 10MB inline threshold so a 512MB file is never base64-encoded.
  • providers/index.ts (after the API key is resolved) uploads files-api providers' large files from that URL — so hosted and BYOK keys both work.
  • Builders branch on the file handle (providerFileId / providerFileUri / remoteUrl), else fall back to base64.
  • Server-only handle fields are stripped from untrusted file input to prevent SSRF.

Type of Change

  • New feature

Testing

  • Extended providers/attachments.test.ts (capability table, strategy/ceiling per provider, handle-based builder output, oversize rejection) and added an SSRF-strip test in file-utils.test.ts — 34 passing.
  • Each provider implementation validated against live API docs (endpoints, part shapes, size limits, expiry) via research agents; ceilings tuned to documented limits.
  • bunx tsc --noEmit clean; bun run check:api-validation passes.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

… and remote URLs

Agent-block file uploads were inlined as base64 with a hard 10MB cap. Files
above the threshold now use each provider's native large-file path:

- OpenAI / Gemini: upload to the provider Files API, reference by file_id/uri
- Anthropic: GA url content-block source (no Files API beta, no upload)
- OpenRouter/Groq/Together/Baseten/xAI/vLLM: remote signed URL in image_url/file
- Limits live per-provider in models.ts; the agent block + /models page reflect them

Files <=10MB keep the identical base64 path (zero regression). Server-only file
handles are stripped from untrusted input to prevent SSRF.
@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jun 16, 2026 6:58am

Request Review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor

cursor Bot commented Jun 16, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Touches authenticated file access, presigned URLs, and third-party file uploads with new server-only handle fields stripped from untrusted input; mistakes could enable SSRF or cross-user file reads.

Overview
Agent-block attachments above the 10MB inline threshold now follow each provider’s configured strategy (files-api, remote-url, or unchanged inline) instead of being rejected or forced through base64.

Provider pipeline: executeProviderRequest clears untrusted providerFileId / providerFileUri / remoteUrl, mints presigned URLs with access checks for oversized files, then uploads to OpenAI/Gemini Files APIs when needed. Message builders reference file_id, Gemini fileUri, or signed URLs instead of data URLs. The agent handler still base64-hydrates only up to the inline cap; larger files skip base64 when the provider supports a large-file path.

Product surface: Per-provider maxBytes lives in models.ts; the agent file upload control and /models provider pages reflect the real ceiling (including formatFileSize on provider pages). The providers API forwards userId so large-file resolution can authorize storage reads.

Tests cover strategy/ceiling behavior, handle-based builder output, and stripping forged handles from client file input.

Reviewed by Cursor Bugbot for commit 68849dd. Configure here.

Comment thread apps/sim/providers/file-attachments.server.ts
@greptile-apps

greptile-apps Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR extends the agent block to support large file attachments (>10 MB) by routing them through each provider's native large-file delivery mechanism — Files API upload for OpenAI and Gemini, presigned-URL pass-through for Anthropic/Groq/xAI/Together/Baseten/OpenRouter/vLLM, and unchanged base64 inlining for all other providers. Small files (\u226410 MB) take the identical base64 path, so existing behaviour is preserved.

  • Introduces file-attachments.server.ts to mint presigned download URLs and drive Files API uploads; server-only handle fields (providerFileId, providerFileUri, remoteUrl) are stripped from untrusted request bodies in convertToUserFile and again at the top of attachLargeFileRemoteUrls.
  • Provider-level attachment ceilings and strategies are centralised in models.ts (ProviderFileAttachment), surfaced to the /models UI and used to compute the file-upload widget's per-file size limit.
  • The OpenAI upload retains raw-fetch multipart (not the SDK) with bracket-notation expires_after fields; Gemini uses the @google/genai SDK with a processing-state polling loop guarded by a 5-minute deadline.

Confidence Score: 5/5

Safe to merge. The large-file routing is well-isolated behind the existing executeProviderRequest boundary, all SSRF and handle-forging vectors are explicitly guarded, and the base64 path for files at or below 10 MB is unchanged.

The implementation correctly handles access control, SSRF prevention, and handle stripping. Small files take the pre-existing base64 path. The two findings are both efficiency and resilience nits in the new Gemini upload path that do not affect correctness or security.

apps/sim/providers/file-attachments.server.ts — specifically the Gemini polling loop and the double verifyFileAccess pattern for files-api providers.

Important Files Changed

Filename Overview
apps/sim/providers/file-attachments.server.ts New server-only module; access checks, SSRF guards, and upload logic are present. Double verifyFileAccess for files-api providers and a minor abort-signal gap in the Gemini polling loop are the only concerns.
apps/sim/providers/attachments.ts Builders correctly branch on providerFileId/providerFileUri/remoteUrl before falling back to base64; prepareProviderAttachments validates size against the provider ceiling and guards the no-base64/no-handle case.
apps/sim/providers/models.ts Adds ProviderFileAttachment per-provider config; limits and strategies are consistent with documented API ceilings.
apps/sim/executor/handlers/agent/agent-handler.ts The missingFile guard is correctly updated to pass files that will take the large-file path; hydration cap stays at the inline threshold.
apps/sim/providers/index.ts Calls attachLargeFileRemoteUrls then uploadLargeFilesToProvider after API-key resolution, so BYOK and hosted keys both work; userId forwarded from the providers API route.
apps/sim/lib/uploads/utils/file-utils.ts Handle fields stripped via omit in convertToUserFile; SSRF test covers the IMDS URL case.
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-upload/file-upload.tsx Switches from cumulative-batch size check to per-file check against the provider ceiling; intentional given that each file is independently routed.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant AH as AgentHandler
    participant PI as providers/index.ts
    participant ALFR as attachLargeFileRemoteUrls
    participant ULTP as uploadLargeFilesToProvider
    participant SS as StorageService
    participant OAI as OpenAI Files API
    participant GEM as Gemini Files API
    participant PR as Provider executeRequest

    AH->>PI: executeProviderRequest(request)
    PI->>PI: resolveApiKey()
    PI->>ALFR: attachLargeFileRemoteUrls(request, providerId)
    ALFR->>ALFR: clear providerFileId/providerFileUri/remoteUrl SSRF guard
    alt strategy not inline AND file.size above threshold
        ALFR->>ALFR: verifyFileAccess()
        ALFR->>SS: generatePresignedDownloadUrl()
        SS-->>ALFR: presignedUrl
        ALFR->>ALFR: "file.remoteUrl = presignedUrl"
    end
    ALFR-->>PI: done
    PI->>ULTP: uploadLargeFilesToProvider(request, providerId)
    alt strategy is files-api
        ULTP->>ULTP: groupUploadableFiles() dedupe by key
        loop per group
            ULTP->>ULTP: assertFileAccessForUpload()
            alt providerId is openai
                ULTP->>SS: downloadFileFromStorage() via storage SDK
                SS-->>ULTP: bytes
                ULTP->>OAI: POST /v1/files multipart
                OAI-->>ULTP: file id
                ULTP->>ULTP: "file.providerFileId = id"
            else providerId is google
                ULTP->>SS: downloadFileFromStorage()
                SS-->>ULTP: bytes
                ULTP->>GEM: ai.files.upload()
                GEM-->>ULTP: name and state
                loop poll while PROCESSING
                    ULTP->>GEM: ai.files.get(name)
                    GEM-->>ULTP: state and uri
                end
                ULTP->>ULTP: "file.providerFileUri = uri"
            end
        end
    end
    ULTP-->>PI: done
    PI->>PR: provider.executeRequest(sanitizedRequest)
    Note over PR: builders branch on providerFileId / providerFileUri / remoteUrl / base64
    PR-->>PI: response
    PI-->>AH: response
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant AH as AgentHandler
    participant PI as providers/index.ts
    participant ALFR as attachLargeFileRemoteUrls
    participant ULTP as uploadLargeFilesToProvider
    participant SS as StorageService
    participant OAI as OpenAI Files API
    participant GEM as Gemini Files API
    participant PR as Provider executeRequest

    AH->>PI: executeProviderRequest(request)
    PI->>PI: resolveApiKey()
    PI->>ALFR: attachLargeFileRemoteUrls(request, providerId)
    ALFR->>ALFR: clear providerFileId/providerFileUri/remoteUrl SSRF guard
    alt strategy not inline AND file.size above threshold
        ALFR->>ALFR: verifyFileAccess()
        ALFR->>SS: generatePresignedDownloadUrl()
        SS-->>ALFR: presignedUrl
        ALFR->>ALFR: "file.remoteUrl = presignedUrl"
    end
    ALFR-->>PI: done
    PI->>ULTP: uploadLargeFilesToProvider(request, providerId)
    alt strategy is files-api
        ULTP->>ULTP: groupUploadableFiles() dedupe by key
        loop per group
            ULTP->>ULTP: assertFileAccessForUpload()
            alt providerId is openai
                ULTP->>SS: downloadFileFromStorage() via storage SDK
                SS-->>ULTP: bytes
                ULTP->>OAI: POST /v1/files multipart
                OAI-->>ULTP: file id
                ULTP->>ULTP: "file.providerFileId = id"
            else providerId is google
                ULTP->>SS: downloadFileFromStorage()
                SS-->>ULTP: bytes
                ULTP->>GEM: ai.files.upload()
                GEM-->>ULTP: name and state
                loop poll while PROCESSING
                    ULTP->>GEM: ai.files.get(name)
                    GEM-->>ULTP: state and uri
                end
                ULTP->>ULTP: "file.providerFileUri = uri"
            end
        end
    end
    ULTP-->>PI: done
    PI->>PR: provider.executeRequest(sanitizedRequest)
    Note over PR: builders branch on providerFileId / providerFileUri / remoteUrl / base64
    PR-->>PI: response
    PI-->>AH: response
Loading

Reviews (10): Last reviewed commit: "fix(providers): fail clearly when a larg..." | Re-trigger Greptile

Comment thread apps/sim/providers/attachments.ts
Comment thread apps/sim/providers/file-attachments.server.ts
Comment thread apps/sim/providers/file-attachments.server.ts Outdated
attachLargeFileRemoteUrls early-returned for inline-strategy providers before
clearing server-only handle fields, so a forged remoteUrl on an inline-provider
file could still reach a builder (e.g. buildOpenAICompatibleChatContent for
mistral/ollama). Clear the handles for every provider before the strategy check.
@greptile-apps

greptile-apps Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR upgrades agent-block file attachments from a flat base64 model (hard-capped at 10 MB) to a tiered system: files ≤10 MB continue to inline as base64, while larger files are routed through each provider's native large-file path — OpenAI/Gemini via Files API upload, Anthropic via URL content-block sources, and the remaining supported providers via presigned HTTPS URLs in image_url / file_data fields.

  • Adds fileAttachment config (strategy + ceiling) to eight providers in models.ts; providers without a config default to inline/10 MB unchanged.
  • Introduces file-attachments.server.ts to mint presigned URLs and upload to provider Files APIs; SSRF-strip protection is layered in file-utils.ts to prevent untrusted workflow input from injecting provider handles.
  • Updates all per-provider message builders to branch on providerFileId, providerFileUri, or remoteUrl handles before falling back to base64.

Confidence Score: 3/5

The core tiered-attachment pipeline is architecturally sound and the SSRF defense is thorough, but the new Gemini upload path contains an unsafe type assertion that will produce a confusing, hard-to-diagnose failure if the file API response is incomplete.

The overall design is well-structured — presigned URL generation, handle-based deduplication, and per-provider ceiling validation all work together correctly. The SSRF strip in file-utils and the handle-clearing in attachLargeFileRemoteUrls are solid defense layers. The one clear defect is in uploadGeminiFile: the uploaded.name as string assertion silences TypeScript but lets undefined flow into ai.files.get(), which would surface as a cryptic Gemini API error in production rather than pointing at the missing name. This would silently block all large-file uploads to Gemini until diagnosed. The OpenAI expires_after bracket-notation encoding is an uncertainty about file lifecycle management rather than functional breakage, but worth verifying against the live API.

apps/sim/providers/file-attachments.server.ts — specifically the Gemini polling loop and the OpenAI expires_after field encoding.

Important Files Changed

Filename Overview
apps/sim/providers/file-attachments.server.ts New server-side module for large-file handling: presigned URL generation, OpenAI Files API upload, and Gemini file upload with polling. Has an unsafe uploaded.name as string cast in the Gemini polling loop (P1) and a potentially silent expires_after field encoding issue for OpenAI (P2).
apps/sim/providers/attachments.ts Updated all provider message builders to branch on providerFileId/providerFileUri/remoteUrl handles before falling back to base64; size validation now uses the provider-specific ceiling from models.ts. Logic looks correct; the ?? '' fallbacks in builder branches are defensive and never reachable via the validated flow.
apps/sim/executor/handlers/agent/agent-handler.ts Switches base64 hydration cap to the fixed 10 MB inline threshold and calls attachLargeFileRemoteUrls after hydration; missingFile check correctly updated to allow remoteUrl as an acceptable handle.
apps/sim/providers/models.ts Adds ProviderFileAttachment interface and per-provider fileAttachment config (strategy + maxBytes) for 8 providers; providers without config default to inline/10 MB, preserving backward compatibility for Azure, Vertex, Bedrock, etc.
apps/sim/providers/index.ts Inserts uploadLargeFilesToProvider call after API key resolution and before executeRequest — ensuring both hosted and BYOK keys are used for file uploads. Correct placement in the request pipeline.
apps/sim/lib/uploads/utils/file-utils.ts Strips providerFileId, providerFileUri, and remoteUrl from untrusted RawFileInput using omit(), preventing SSRF via injected provider handles. Well-tested with a dedicated SSRF strip test.
apps/sim/providers/attachments.test.ts Adds 6 new test cases covering per-provider strategy/ceiling table, large-file handle-based builder output for OpenAI/Anthropic/Gemini/OpenAI-compatible providers, and oversize rejection. Good coverage of the new branching paths.
apps/sim/executor/types.ts Adds providerFileId, providerFileUri, and remoteUrl optional fields to UserFile; well-documented with inline JSDoc explaining their server-only lifecycle.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant AH as AgentHandler
    participant FAS as file-attachments.server
    participant SS as StorageService
    participant PI as providers/index
    participant OAI as OpenAI Files API
    participant GM as Gemini Files API
    participant PR as Provider (execute)

    AH->>AH: "hydrateUserFilesWithBase64(maxBytes=10MB)"
    AH->>FAS: attachLargeFileRemoteUrls(files, providerId)
    FAS->>FAS: Clear providerFileId/Uri/remoteUrl (SSRF defense)
    loop each file over 10MB on capable provider
        FAS->>FAS: verifyFileAccess(key, userId)
        FAS->>SS: generatePresignedDownloadUrl(key, 1h)
        SS-->>FAS: presignedUrl
        FAS->>FAS: "file.remoteUrl = presignedUrl"
    end
    AH->>AH: missingFile check (base64 OR remoteUrl required)
    AH->>PI: executeProviderRequest(request)
    PI->>FAS: uploadLargeFilesToProvider(request, providerId)
    alt files-api strategy (openai/google)
        loop each uploadable file group
            FAS->>FAS: fetchRemoteFileBlob(file.remoteUrl)
            alt OpenAI
                FAS->>OAI: POST /v1/files (purpose, expires_after, blob)
                OAI-->>FAS: file id
                FAS->>FAS: "file.providerFileId = id"
            else Gemini
                FAS->>GM: ai.files.upload(blob, mimeType)
                loop poll until ACTIVE
                    FAS->>GM: ai.files.get(name)
                    GM-->>FAS: state + uri
                end
                FAS->>FAS: "file.providerFileUri = uri"
            end
        end
    end
    PI->>PR: provider.executeRequest(sanitizedRequest)
    note over PR: Builders use file_id / fileData / remoteUrl / base64
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant AH as AgentHandler
    participant FAS as file-attachments.server
    participant SS as StorageService
    participant PI as providers/index
    participant OAI as OpenAI Files API
    participant GM as Gemini Files API
    participant PR as Provider (execute)

    AH->>AH: "hydrateUserFilesWithBase64(maxBytes=10MB)"
    AH->>FAS: attachLargeFileRemoteUrls(files, providerId)
    FAS->>FAS: Clear providerFileId/Uri/remoteUrl (SSRF defense)
    loop each file over 10MB on capable provider
        FAS->>FAS: verifyFileAccess(key, userId)
        FAS->>SS: generatePresignedDownloadUrl(key, 1h)
        SS-->>FAS: presignedUrl
        FAS->>FAS: "file.remoteUrl = presignedUrl"
    end
    AH->>AH: missingFile check (base64 OR remoteUrl required)
    AH->>PI: executeProviderRequest(request)
    PI->>FAS: uploadLargeFilesToProvider(request, providerId)
    alt files-api strategy (openai/google)
        loop each uploadable file group
            FAS->>FAS: fetchRemoteFileBlob(file.remoteUrl)
            alt OpenAI
                FAS->>OAI: POST /v1/files (purpose, expires_after, blob)
                OAI-->>FAS: file id
                FAS->>FAS: "file.providerFileId = id"
            else Gemini
                FAS->>GM: ai.files.upload(blob, mimeType)
                loop poll until ACTIVE
                    FAS->>GM: ai.files.get(name)
                    GM-->>FAS: state + uri
                end
                FAS->>FAS: "file.providerFileUri = uri"
            end
        end
    end
    PI->>PR: provider.executeRequest(sanitizedRequest)
    note over PR: Builders use file_id / fileData / remoteUrl / base64
Loading

Reviews (2): Last reviewed commit: "fix(providers): clear forged file handle..." | Re-trigger Greptile

Comment thread apps/sim/providers/file-attachments.server.ts
Comment thread apps/sim/providers/file-attachments.server.ts
Comment thread apps/sim/providers/file-attachments.server.ts
…ge-text-doc handling

- OpenAI upload now uses the SDK (client.files.create) so expires_after is
  serialized as a real nested object; the prior expires_after[anchor] bracket
  FormData keys were ignored by OpenAI's server, leaving files un-expiring.
- Anthropic url document source only supports PDFs/images; large non-PDF text
  docs now throw a clear error instead of emitting an unsupported url source.
- Warn when an oversized file can't be sent because cloud storage is unavailable.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

…-file UI limit)

- Download files for OpenAI/Gemini uploads via validateUrlWithDNS + IP-pinned
  fetch so a forged URL can't reach internal addresses (covers all callers).
- Reject files above the provider ceiling before downloading/uploading.
- UI now validates each file against the provider's per-file ceiling instead of
  summing all files against it, matching server-side per-file validation.
- Lower Anthropic ceiling to 50MB (documented 32MB request cap / page limits).
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Read OpenAI/Gemini upload bytes through downloadFileFromStorage instead of
HTTP-fetching the presigned URL. Removes any server-side URL fetch (no SSRF
vector) and works with internal object storage (e.g. self-hosted MinIO), which
an IP-pinned URL fetch would have blocked.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

Comment thread apps/sim/providers/index.ts
…oad path

uploadLargeFilesToProvider runs on raw request messages for every caller (incl.
the internal providers passthrough), so harden it independently of the agent path:
- verifyFileAccess on each file's storage key before reading its bytes, so a forged
  key can't exfiltrate another user's file.
- clear any inbound providerFileId/providerFileUri up front (legit ids are only set
  by the upload itself), so a forged id can't reference a file in a hosted account.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 9c4ad83. Configure here.

Comment thread apps/sim/providers/file-attachments.server.ts
attachments.ts now reads getProviderFileAttachment / INLINE_ATTACHMENT_MAX_BYTES
from @/providers/models; the provider unit tests that fully mock that module need
both exports or attachments.ts fails to load.
ai.files.upload returns name as string | undefined; guard it (instead of an
as-string cast) so a missing name surfaces a clear error at the upload site
rather than an opaque files.get failure on the first poll.
…e fields

The 'as const' readonly tuple widened omit's K to all keys, collapsing
Omit<UserFile, K> to {} and failing the production build's type check. Declare
the array as Array<keyof handle fields> so K is the precise literal union.
…quest for all callers

Move attachLargeFileRemoteUrls out of the agent handler and into
executeProviderRequest (right before uploadLargeFilesToProvider), so every entry
point — including the internal providers passthrough — clears forged handles and
mints/access-checks large-file URLs uniformly. The agent handler now only hydrates
base64; its missing-file guard exempts large files (resolved downstream).
…part

PreparedProviderAttachment.dataUrl is now optional (large files carry a handle
instead); azure-openai builds chat content inline and assigned it directly to a
required url field, failing the production build's type check.
… part

The installed openai SDK (4.104) does not type expires_after on files.create, so
upload via POST /v1/files directly with the documented expires_after[...] form
fields (gives the file an auto-expiry). Also wrap the storage Buffer in a
Uint8Array for the Blob, which the production build's stricter lib types require.

These two type errors were masked locally because tsc was OOMing silently without
the type-check script's --max-old-space-size flag.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/providers/index.ts
…derRequest

Large-attachment prep now needs request.userId for presigned URLs and access
checks; the authenticated providers proxy has auth.userId but wasn't passing it,
so oversized attachments failed for logged-in callers. Forwarding it makes large
files work there and keeps the access check (verifyFileAccess) intact.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/providers/file-attachments.server.ts Outdated
…rage

The doc claimed a base64 fallback that doesn't exist — above the inline cap there
is no base64, so without cloud storage the file previously reached the builder and
died with a generic read error. Throw a clear 'requires cloud file storage' error
at the point of detection and correct the doc.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 68849dd. Configure here.

@waleedlatif1 waleedlatif1 merged commit e1f22bd into staging Jun 16, 2026
16 checks passed
@waleedlatif1 waleedlatif1 deleted the worktree-provider-files-api branch June 16, 2026 16:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant