Repository navigation
feat(eve): add fetchFile to eveChannel and let resolvers give the model a reason - #4636
Conversation
| mediaType !== undefined && | ||
| !isMediaTypeAllowed(mediaType.split(";", 1)[0]!.trim(), policy) | ||
| ) { | ||
| throw new EveAttachmentError({ |
There was a problem hiding this comment.
The type check here runs on a declared string, not on the bytes, so it misses the cases that matter:
- If
fetchFilereturns a bareBuffer, nothing checks the type at all. The only check was the route'sevaluateFilePart, and that looked at the client's declaredpart.mediaType. - Staging then calls
verifyMediaType(bytes, declared)inwriteSandboxRef(harness/attachment-staging.ts), which swaps in the sniffed type. WithallowedMediaTypes: ["application/pdf"], a part declaredapplication/pdfwhose bytes are a PNG passes both checks and gets staged asimage/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.
There was a problem hiding this comment.
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.
| function withUploadPolicy(fetchFile: FetchFileFunction, policy: UploadPolicy): FetchFileFunction { | ||
| return async (url, context) => { | ||
| const result = await fetchFile(url, context); | ||
| if (result === null) return null; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Bundle + Package Summary:
|
| 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 |
207726e to
84ebf45
Compare
…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>
a640415 to
585470f
Compare
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 nofetchFileto load a URL instead (#4194). Second, a customfetchFilecould not tell the model why a file failed. Only built-in channels could pass a reason through (#4304).eveChannel({ fetchFile })takes the same resolver asdefineChannel.defineChanneltakesuploadPolicy.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 aneveChannelwithuploadPolicyorfetchFilereports instrumentation kindchannel:eveinstead ofhttp, as one witheventsalready does.attachmentError(message, { cause })fromeve/channelsgives the model a safe reason, and the cause stays in operator logs. eve records the channel that ran the resolver in operator logs.FetchFileContexttypes thesessionthat staging already passes.eve/channelsexportsFetchFileFunction,FetchFileContext, andFetchFileResult. The channel extension contract moves to epoch 59 and retains 58.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 --retaingenerated the epoch metadata.pnpm guard:invariantspasses.pnpm --filter eve run typecheck,pnpm fmt, andpnpm docs:checkpass.Checklist
evepackagegit commit --signoff)