Skip to content

feat(eve): add fetchFile to eveChannel and let resolvers give the model a reason - #4636

Merged
AndrewBarba merged 3 commits into
mainfrom
barba/attachments-channel-apis
Oct 10, 2026
Merged

AndrewBarba merged 3 commits into
mainfrom
barba/attachments-channel-apis

Conversation

@AndrewBarba

@AndrewBarba AndrewBarba commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Two gaps on the custom-channel side of attachments. First, a browser that sends a file as base64 hits the 4.5 MB Vercel Functions body limit at about 3.3 MB. eveChannel() had no fetchFile to load a URL instead (#4194). Second, a custom fetchFile could not tell the model why a file failed. Only built-in channels could pass a reason through (#4304).

  • eveChannel({ fetchFile }) takes the same resolver as defineChannel.
  • defineChannel takes uploadPolicy. eveChannel, Slack, Telegram, and Teams pass their own through, but only when one is configured. Staging enforces it on every file, on the final bytes and verified media type, including URLs eve downloads itself (fix(eve): resolve every attachment to a staged file or a note #4635). A file over the cap or of a disallowed type becomes a note. A policy counts as adapter behavior, so an eveChannel with uploadPolicy or fetchFile reports instrumentation kind channel:eve instead of http, as one with events already does.
  • attachmentError(message, { cause }) from eve/channels gives the model a safe reason, and the cause stays in operator logs. eve records the channel that ran the resolver in operator logs.
  • FetchFileContext types the session that staging already passes. eve/channels exports FetchFileFunction, FetchFileContext, and FetchFileResult. The channel extension contract moves to epoch 59 and retains 58.
  • The web chat conformance driver now attaches files, so the HTTP channel joins the attachment rows of the conformance matrix.

Closes #4194 and #4304. Stacked on #4635.

Validation

  • pnpm --filter eve exec vitest run --config vitest.unit.config.ts: 9445 passed, 1 skipped.
  • pnpm --filter eve exec vitest run --config vitest.browser.config.ts -t "a person sends|sent earlier in the conversation": the three web chat attachment rows pass in headless Chromium.
  • pnpm update:extension-contracts --update channel --retain generated the epoch metadata. pnpm guard:invariants passes.
  • pnpm --filter eve run typecheck, pnpm fmt, and pnpm docs:check pass.

Checklist

  • I added tests and documentation where relevant
  • I added a changeset if this touches the published eve package
  • DCO sign-off passes for every commit (git commit --signoff)

@AndrewBarba
AndrewBarba requested a review from a team as a code owner October 10, 2026 17:15
@vercel

vercel Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated
eve-docs Ready Ready Preview, v0 Oct 10, 2026 10:35pm UTC
eve-pkg Ready Ready Preview, v0 Oct 10, 2026 10:35pm UTC

Comment thread packages/eve/src/eve-channel/index.ts Outdated
mediaType !== undefined &&
!isMediaTypeAllowed(mediaType.split(";", 1)[0]!.trim(), policy)
) {
throw new EveAttachmentError({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The type check here runs on a declared string, not on the bytes, so it misses the cases that matter:

  • If fetchFile returns a bare Buffer, nothing checks the type at all. The only check was the route's evaluateFilePart, and that looked at the client's declared part.mediaType.
  • Staging then calls verifyMediaType(bytes, declared) in writeSandboxRef (harness/attachment-staging.ts), which swaps in the sniffed type. With allowedMediaTypes: ["application/pdf"], a part declared application/pdf whose bytes are a PNG passes both checks and gets staged as image/png.

The wrapper can't fix this because it never sees the declared type or the verified type. I think the simpler cut is to drop withUploadPolicy and enforce the policy once in staging, on the final bytes and the verified media type, for every resolved link. That probably means exposing the channel's uploadPolicy on the adapter. It also covers the null fallback below and removes the eveChannel-only wrapper.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done as suggested. I dropped withUploadPolicy. defineChannel takes uploadPolicy, the adapter carries the merged policy, and staging enforces it once, on the final bytes and the verified media type (#4635). A declared PDF whose bytes are a PNG now becomes a note under allowedMediaTypes: ['application/pdf'], which the staging integration test covers. A policy counts as adapter behavior, so an eveChannel with uploadPolicy reports kind channel:eve, as one with events already does. The changeset says so.

Comment thread packages/eve/src/eve-channel/index.ts Outdated
function withUploadPolicy(fetchFile: FetchFileFunction, policy: UploadPolicy): FetchFileFunction {
return async (url, context) => {
const result = await fetchFile(url, context);
if (result === null) return null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If fetchFile returns null, the URL goes to createPublicDownloads, and that path caps at DEFAULT_UPLOAD_POLICY.maxBytes (25 MB) and never checks the channel's allowed types. So with uploadPolicy: { maxBytes: 1024 * 1024, allowedMediaTypes: ["image/*"] }, a client can declare image/png, point data at any public https: file, and eve stages up to 25 MB of whatever comes back (say video/mp4). This gap was already there in the base branch, but this PR is the one that documents fetched bytes as held to uploadPolicy (changeset, EveChannelInput.fetchFile JSDoc, docs/channels/eve.mdx:198). Someone who sets a cap will reasonably expect it to cover every URL part. Either enforce the policy in staging (see the comment above), or have the docs say plainly that the public fallback ignores uploadPolicy.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Covered by the same change. The public fallback download now caps at the channel's maxBytes, and staging checks the allowed types on the verified media type. The docs now say uploadPolicy covers every staged file, including URLs eve downloads itself.

@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Bundle + Package Summary: apps/fixtures/weather-agent

Key takeaways

  • No notable deltas vs main (0248e69).

Delta vs main (0248e69)

Area Metric Baseline Current Delta
Package Packed tarball 7.58 MB 7.58 MB +2.8 kB ⚠️
Package Unpacked publish size 26.69 MB 26.70 MB +6.7 kB ⚠️
Package Installed footprint 74.50 MB 74.50 MB +6.7 kB ⚠️
Package Published files 4115 4117 +2
Package Installed files 8120 8122 +2
Package Installed package instances 34 34 0
Package Distinct installed package names 32 32 0
Package Installed dependency edges 51 51 0
Package Installed optional peer edges 9 9 0
Runtime Unique function payloads 2 2 0
Runtime Total function bytes 20.39 MB 20.39 MB +632 B ⚠️
Runtime Public routes 18 18 0
Changed function payloads vs main (0248e69) (2)
Function Status Baseline Current Delta Route changes
functions/__server.func changed 10.20 MB 10.20 MB +316 B ⚠️ none
functions/.well-known/workflow/v1/flow.func changed 10.20 MB 10.20 MB +316 B ⚠️ none

eve init install

Metric Baseline Current Delta
Installed footprint 112.79 MB 112.80 MB +6.7 kB ⚠️
Installed packages 97 97 0
dependencies 4 4 0
devDependencies 2 2 0
Dependency package bytes 44.78 MB 44.79 MB +6.7 kB ⚠️
devDependency package bytes 5.11 MB 5.11 MB 0 B ➖
Build Metadata
  • Preset: vercel
  • Nitro: nitro@3.0.260903-beta
  • Output directory: apps/fixtures/weather-agent/.vercel/output
  • Build metadata timestamp: 2026-10-10T22:36:30.915Z
  • Route aliases: 18 public, 1 internal (19 total aliases)
  • Vercel routes in config: 21
  • Severity legend: 🔴 dominant/large, 🟠 notable, 🟡 watch, ⚪ small
Package Drill-Down

Package Details

  • Package: eve@0.76.3
  • Package directory: packages/eve
  • Tarball: 7.58 MB (eve-0.76.3.tgz)
  • Unpacked payload: 26.70 MB across 4117 published files
  • Installed footprint: 74.50 MB across 8122 installed files
  • Installed root package: 26.70 MB
  • Installed dependencies: 47.80 MB
  • Installed package instances: 34
  • Distinct installed package names: 32
  • Installed dependency edges: 51
  • Installed optional peer edges: 9
  • Runtime dependencies: 2
  • Peer dependencies: 7 (6 optional)

Installed footprint is measured from an isolated temporary npm install of the packed tarball.
Graph metrics read only package.json files in package directories directly beneath a node_modules boundary, including nested boundaries. Each directory is one package instance; distinct names come from those manifests. Dependency edges count each unique name in dependencies or optionalDependencies per instance; optional peer edges count peerDependencies marked optional.

Heavy installed dependencies

  • eve: 26.70 MB (35.8%)
  • @rolldown/binding-linux-x64-gnu: 19.62 MB (26.3%)
  • ai: 8.14 MB (10.9%)
  • zod: 6.14 MB (8.2%)
  • undici: 3.56 MB (4.8%)
Publish payload breakdown
Published file size
🟠 dist/src/compiled/shadcn-registry/index.js       [#####...................] 3.85 MB 14.4%
🟠 dist/src/compiled/@photon-ai/chat-adapter-ime... [###.....................] 2.29 MB 8.6%
🟠 dist/src/compiled/@ai-sdk/code-mode/index.js     [#.......................] 1.03 MB 3.8%
🟡 dist/src/compiled/@vercel/blob/index.js          [#.......................] 604.9 kB 2.3%
🟡 dist/src/compiled/_chunks/workflow/undici-D-I... [#.......................] 521.9 kB 2.0%
🔴 Other published files                            [########################] 18.41 MB 69.0%
Installed footprint breakdown
Installed package size
🔴 eve                             [########################] 26.70 MB 35.8%
🔴 @rolldown/binding-linux-x64-gnu [##################......] 19.62 MB 26.3%
🔴 ai                              [#######.................] 8.14 MB 10.9%
🔴 zod                             [######..................] 6.14 MB 8.2%
🟠 undici                          [###.....................] 3.56 MB 4.8%
🟠 nitro                           [##......................] 1.89 MB 2.5%
🔴 Other installed packages        [########................] 8.44 MB 11.3%
Runtime dependencies (2)
Package Range Notes
nitro 3.0.260903-beta
undici 8.10.2
Peer dependencies (7)
Package Range Notes
@opentelemetry/api ^1.0.0 optional peer
ai catalog:
braintrust ^3.0.0 optional peer
chat ^4.41.0 optional peer
dd-trace ^6.13.0 optional peer
just-bash ^3.1.0 optional peer
microsandbox ^0.5.0 optional peer
eve init install drill-down

eve init install details

  • Command: eve init my-agent
  • Package manager: npm
  • Installed footprint: 112.80 MB across 10017 installed files
  • Installed packages: 97 total (91 transitive-only)
  • dependencies: 4 direct packages totaling 44.79 MB
  • devDependencies: 2 direct packages totaling 5.11 MB
  • Other transitive package files: 62.90 MB

Installed footprint is measured from an isolated temporary eve init my-agent using the current packed eve tarball.

Heavy installed dependencies

  • @typescript/typescript-linux-x64: 27.95 MB (24.8%)
  • eve: 26.70 MB (23.7%)
  • @rolldown/binding-linux-x64-gnu: 19.62 MB (17.4%)
  • zod: 9.76 MB (8.6%)
  • ai: 8.14 MB (7.2%)
Installed footprint breakdown
Installed package size
🔴 @typescript/typescript-linux-x64 [########################] 27.95 MB 24.8%
🔴 eve                              [#######################.] 26.70 MB 23.7%
🔴 @rolldown/binding-linux-x64-gnu  [#################.......] 19.62 MB 17.4%
🔴 zod                              [########................] 9.76 MB 8.6%
🔴 ai                               [#######.................] 8.14 MB 7.2%
🟠 undici                           [###.....................] 3.56 MB 3.2%
🔴 Other installed packages         [###############.........] 17.07 MB 15.1%
dependencies (4)
Package Range Installed size Share
@vercel/connect 2.2.0 188.7 kB 0.2%
ai ^7.0.128 8.14 MB 7.2%
eve file:eve-0.76.3.tgz 26.70 MB 23.7%
zod 4.5.4 9.76 MB 8.6%
devDependencies (2)
Package Range Installed size Share
@types/node 24.x 2.61 MB 2.3%
typescript 7.0.2 2.50 MB 2.2%
Function Drill-Down

Payload Size Graph

Unique function payload size and share of total
🔴 functions/.well-known/workflow/v1/flow.func     [########################] 10.20 MB 50.0%
🔴 functions/__server.func                         [########################] 10.20 MB 50.0%

Top Function Payloads

🟠 functions/.well-known/workflow/v1/flow.func • 1 public route • 10.20 MB
Metric Value
Public routes /.well-known/workflow/v1/flow
Runtime nodejs24.x
Handler index.mjs
Payload 10.20 MB
Function files 10.20 MB across 118 files
Traced dependencies 0 B
Signal 🟠 Bundled file _chunks/vercel.web.mjs is 2.25 MB (22.1%)

🟠 🔎 Dependency Analysis

📦 Bundled files:

Bundled file size
🟠 _chunks/vercel.web.mjs                         [############............] 2.25 MB 22.1%
🟠 _libs/undici.mjs                               [#####...................] 1.01 MB 9.9%
🟡 _chunks/sandbox3.mjs                           [####....................] 815.4 kB 8.0%
🟡 _chunks/compiled-artifacts-instrumentation.mjs [####....................] 757.3 kB 7.4%
🟡 _chunks/token-util-Bla5Z0G9.mjs                [####....................] 704.7 kB 6.9%
🔴 Other bundled files                            [########################] 4.66 MB 45.7%

🧾 Vercel Config

{
  "handler": "index.mjs",
  "launcherType": "Nodejs",
  "shouldAddHelpers": false,
  "supportsResponseStreaming": true,
  "runtime": "nodejs24.x",
  "maxDuration": "max",
  "experimentalTriggers": [
    {
      "type": "queue/v2beta",
      "topic": "__eve776561746865722d6167656e74_wkf_workflow_*",
      "consumer": "default",
      "retryAfterSeconds": 5,
      "initialDelaySeconds": 0
    }
  ],
  "environment": {
    "WORKFLOW_PRECONDITION_GUARD": "1"
  }
}

🟠 functions/__server.func • 17 public routes, 1 internal alias • 10.20 MB
Metric Value
Public routes /
/.well-known/workflow/v1/webhook/[token]
/eve/v1/callback/[token]
/eve/v1/connections/[name]/callback/[attemptId]/[token]
/eve/v1/connections/[name]/callback/[token]
/eve/v1/health
/eve/v1/info
/eve/v1/session
/eve/v1/session/[parentSessionId]/subagents/[callId]/[childSessionId]/stream
/eve/v1/session/[sessionId]
/eve/v1/session/[sessionId]/cancel
/eve/v1/session/[sessionId]/clear
/eve/v1/session/[sessionId]/compact
/eve/v1/session/[sessionId]/reset
/eve/v1/session/[sessionId]/stream
/eve/v1/session/[sessionId]/stubs
/eve/v1/task-input/[token]
Internal aliases /__server
Runtime nodejs24.x
Handler index.mjs
Payload 10.20 MB
Function files 10.20 MB across 118 files
Traced dependencies 0 B
Signal 🟠 Bundled file _chunks/vercel.web.mjs is 2.25 MB (22.1%)

🟠 🔎 Dependency Analysis

📦 Bundled files:

Bundled file size
🟠 _chunks/vercel.web.mjs                         [############............] 2.25 MB 22.1%
🟠 _libs/undici.mjs                               [#####...................] 1.01 MB 9.9%
🟡 _chunks/sandbox3.mjs                           [####....................] 815.4 kB 8.0%
🟡 _chunks/compiled-artifacts-instrumentation.mjs [####....................] 757.3 kB 7.4%
🟡 _chunks/token-util-Bla5Z0G9.mjs                [####....................] 704.7 kB 6.9%
🔴 Other bundled files                            [########################] 4.66 MB 45.7%

🧾 Vercel Config

{
  "handler": "index.mjs",
  "launcherType": "Nodejs",
  "shouldAddHelpers": false,
  "supportsResponseStreaming": true,
  "runtime": "nodejs24.x"
}

Build Timing: e2e/fixtures/agent-tools-sandbox

This is an informational timing measurement inside eve build, from preflight through publication. Output-size measurement and profile writing are excluded.

Build mode: deployable Vercel build with sandbox template prewarm included.

  • Build pipeline: 3.48 s -> 3.35 s (-134.6 ms) vs main (0248e69).
  • Timing is informational: shared GitHub runners are too variable for a hard timing budget.
Detailed phase timings vs `main (0248e69)`
Phase Baseline Current Delta
extension.check 18.2 ms 18.2 ms 0.0 ms
project.resolve 0.5 ms 0.4 ms -0.1 ms
workspace.create 0.5 ms 0.7 ms +0.2 ms
host.prepare 719.8 ms 630.1 ms -89.7 ms
vercel.service-prefix.resolve 1.5 ms 1.3 ms -0.2 ms
nitro.create 598.7 ms 577.2 ms -21.5 ms
sandbox.prewarm 386.0 ms 279.4 ms -106.6 ms
nitro.cache.prepare 0.3 ms 0.2 ms -0.1 ms
nitro.prepare 0.8 ms 0.6 ms -0.2 ms
nitro.public-assets 0.7 ms 0.7 ms 0.0 ms
nitro.prerender 0.5 ms 0.5 ms 0.0 ms
nitro.bundle 1.59 s 1.67 s +74.9 ms
nitro.cache.write 0.3 ms 0.3 ms 0.0 ms
vercel.workflow-function.materialize 75.2 ms 80.1 ms +4.9 ms
agent-summary.emit 0.7 ms 0.7 ms 0.0 ms
connect-manifest.emit 0.3 ms 0.3 ms 0.0 ms
nitro.close 0.1 ms 0.1 ms 0.0 ms
output.publish 4.1 ms 4.0 ms -0.1 ms
workspace.remove 2.4 ms 2.3 ms -0.1 ms

…el a reason

Signed-off-by: Andrew Barba <barba@hey.com>
Signed-off-by: Andrew Barba <barba@hey.com>
Signed-off-by: Andrew Barba <barba@hey.com>
@AndrewBarba
AndrewBarba force-pushed the barba/attachments-channel-apis branch from a640415 to 585470f Compare October 10, 2026 22:33
@AndrewBarba
AndrewBarba merged commit b6b353f into main Oct 10, 2026
243 of 248 checks passed
@AndrewBarba
AndrewBarba deleted the barba/attachments-channel-apis branch October 10, 2026 23:38

This branch was successfully deployed

2 active deployments
Preview – eve-docs — 585470fe Deployed Oct 10, 2026 by vercel[bot]
Preview – eve-pkg — 585470fe Deployed Oct 10, 2026 by vercel[bot]
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.

eveChannel: support fetchFile for URL file parts (large web uploads)

2 participants