Repository navigation
fix(eve): never persist an unresolved attachment - #4626
AndrewBarba wants to merge 2 commits into
Conversation
Bundle + Package Summary:
|
| Area | Metric | Baseline | Current | Delta |
|---|---|---|---|---|
| Package | Packed tarball | 7.58 MB | 7.58 MB | -170 B ✅ |
| Package | Unpacked publish size | 26.69 MB | 26.69 MB | -1.7 kB ✅ |
| Package | Installed footprint | 74.49 MB | 74.49 MB | -1.7 kB ✅ |
| Package | Published files | 4115 | 4113 | -2 |
| Package | Installed files | 8120 | 8118 | -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.38 MB | 20.38 MB | +156 B |
| Runtime | Public routes | 18 | 18 | 0 |
Changed function payloads vs barba/attachments-research (54246c6) (2)
| Function | Status | Baseline | Current | Delta | Route changes |
|---|---|---|---|---|---|
functions/__server.func |
changed | 10.19 MB | 10.19 MB | +78 B |
none |
functions/.well-known/workflow/v1/flow.func |
changed | 10.19 MB | 10.19 MB | +78 B |
none |
eve init install
| Metric | Baseline | Current | Delta |
|---|---|---|---|
| Installed footprint | 112.79 MB | 112.79 MB | -1.7 kB ✅ |
| Installed packages | 97 | 97 | 0 |
| dependencies | 4 | 4 | 0 |
| devDependencies | 2 | 2 | 0 |
| Dependency package bytes | 44.78 MB | 44.77 MB | -1.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-10T16:57:59.901Z
- 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.2 - Package directory:
packages/eve - Tarball: 7.58 MB (
eve-0.76.2.tgz) - Unpacked payload: 26.69 MB across 4113 published files
- Installed footprint: 74.49 MB across 8118 installed files
- Installed root package: 26.69 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.69 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.40 MB 68.9%
Installed footprint breakdown
Installed package size
🔴 eve [########################] 26.69 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.79 MB across 10013 installed files
- Installed packages: 97 total (91 transitive-only)
- dependencies: 4 direct packages totaling 44.77 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.69 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.69 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.2.tgz |
26.69 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.19 MB 50.0%
🔴 functions/__server.func [########################] 10.19 MB 50.0%
Top Function Payloads
🟠 functions/.well-known/workflow/v1/flow.func • 1 public route • 10.19 MB
| Metric | Value |
|---|---|
| Public routes | /.well-known/workflow/v1/flow |
| Runtime | nodejs24.x |
| Handler | index.mjs |
| Payload | 10.19 MB |
| Function files | 10.19 MB across 116 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.19 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.19 MB |
| Function files | 10.19 MB across 116 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.59 s -> 3.61 s (+18.8 ms) vs
barba/attachments-research (54246c6). - Timing is informational: shared GitHub runners are too variable for a hard timing budget.
Detailed phase timings vs `barba/attachments-research (54246c6)`
| Phase | Baseline | Current | Delta |
|---|---|---|---|
extension.check |
0.5 ms | 16.6 ms | +16.1 ms |
project.resolve |
0.4 ms | 0.4 ms | 0.0 ms |
workspace.create |
0.6 ms | 0.6 ms | 0.0 ms |
host.prepare |
817.9 ms | 793.2 ms | -24.7 ms |
vercel.service-prefix.resolve |
2.2 ms | 2.1 ms | -0.1 ms |
nitro.create |
675.7 ms | 650.8 ms | -24.9 ms |
sandbox.prewarm |
233.3 ms | 207.7 ms | -25.6 ms |
nitro.cache.prepare |
0.2 ms | 0.3 ms | +0.1 ms |
nitro.prepare |
0.8 ms | 0.8 ms | 0.0 ms |
nitro.public-assets |
0.8 ms | 0.8 ms | 0.0 ms |
nitro.prerender |
0.6 ms | 0.6 ms | 0.0 ms |
nitro.bundle |
1.71 s | 1.80 s | +83.9 ms |
nitro.cache.write |
0.4 ms | 0.4 ms | 0.0 ms |
vercel.workflow-function.materialize |
54.8 ms | 55.5 ms | +0.7 ms |
agent-summary.emit |
0.6 ms | 0.7 ms | +0.1 ms |
connect-manifest.emit |
0.3 ms | 0.3 ms | 0.0 ms |
nitro.close |
0.2 ms | 0.1 ms | -0.1 ms |
output.publish |
3.5 ms | 3.5 ms | 0.0 ms |
workspace.remove |
2.8 ms | 2.9 ms | +0.1 ms |
| return decodeDataUrl(data); | ||
| } | ||
| if (SCHEME_PREFIX.test(data)) { | ||
| if (hasInternalRefScheme(data)) return UNREADABLE; |
There was a problem hiding this comment.
hasInternalRefScheme uses a case-sensitive startsWith, but URL.parse lowercases the scheme. So EVE-SANDBOX:?path=/x&size=1&type=image/png gets past this check (and past the same guard in eve-channel/request.ts:484), then parses to a URL with protocol eve-sandbox:. The eve channel has no fetchFile, so staging persists that URL (attachment-staging.ts:479-480). On the next model call, hydration treats it as a real sandbox ref (isSandboxRefFilePart -> decodeSandboxRef -> readSandboxRefBytes), so the path the caller supplied gets read. A malformed one throws inside hydrateMessageContent instead. Before this PR, such a string was decoded as base64 and never turned into a URL.
Suggest checking the protocol after parsing (for example url.protocol === SANDBOX_URL_SCHEME || url.protocol === "eve-url:" -> unreadable), or making hasInternalRefScheme case-insensitive so the HTTP boundary rejects these too.
| } | ||
| return stageResolvedBytes(part, { bytes }, sandbox); | ||
| if (resolved === null) { | ||
| return { ...part, data: data.url }; |
There was a problem hiding this comment.
This still persists an unresolved attachment, which is what the PR is meant to stop. Now that readFileData classifies any scheme as a link, a myapp-file:abc string with no fetchFile (or a fetchFile that returns null for it, the usual "not mine" answer in the Slack, Teams, and Telegram resolvers) lands in history as URL("myapp-file:abc"). Providers can't download that, so every later model call fails, which is the #3419 failure mode again. The new test only covers a resolver that succeeds.
Keep the pass-through for http:/https: only, which is what the comment at line 60 already says, and return attachmentNote(part, "could not be retrieved.") for any other scheme.
| const scoped = await withContextScope(ctx, session, async (current) => ({ | ||
| result: undefined, | ||
| session: await preserveCancelledTurnMessage(current, preserved), | ||
| })); |
There was a problem hiding this comment.
Cancellation used to be side-effect free. Now it can start a sandbox, and nothing here catches a failure. If ensureSandboxAccess or writeSandboxRef's sandbox.writeBinaryFile rejects, the error escapes cancelledWithoutCheckpoint, so the step throws instead of returning action: "cancelled". withContextScope only rolls back providers that define rollback, and sandboxProvider doesn't define one (context/providers/sandbox.ts:10-43). That means a failure after the sandbox starts also skips commit, and the started sandbox's state never reaches the session.
Suggest catching here and falling back to the no-sandbox path (preserveCancelledTurnMessage under plain contextStorage.run, which now turns each file into a note), so a cancel always resolves as a cancel.
899abf9 to
c150fff
Compare
1905484 to
d5cc259
Compare
Signed-off-by: Andrew Barba <barba@hey.com>
…taging fails Signed-off-by: Andrew Barba <barba@hey.com>
c150fff to
6f4a055
Compare
|
Collapsed into #4635. |
Summary
A turn cancelled before its first model call saved its attachment as a raw
eve-url:marker. Every later model call then failed withAI_DownloadError, and the session stayed broken until reset (#3419). The cause is that the cancelled-turn epilogue ran before the framework providers existed, so staging found no sandbox and kept the unresolved part. The epilogue now stages inside the framework providers, so the photo survives. Staging also never lets an unstaged file into history. Without a sandbox, each file becomes a note. Data that is not bytes, base64, adata:URL, or a URL also becomes a note, instead of reaching the provider. So does a link whose scheme no channel resolves, such asmyapp-file:abc. If staging fails while a cancel settles, the cancel still settles and the attachments become notes.The same rule closes three related gaps:
data:URLs, so no world serializer has to round-trip aUint8Array. This is the failure File parts fail the model call withimage.source.base64.data: Input should be a valid stringunder @workflow/world-postgres #497 suspects. I couldn't run world-postgres here, so File parts fail the model call withimage.source.base64.data: Input should be a valid stringunder @workflow/world-postgres #497 stays open.myapp-file:abc, reaches the channel'sfetchFile. Before, eve decoded it as base64 and staged junk bytes. A comment on eveChannel: support fetchFile for URL file parts (large web uploads) #4194 reports this.message.receivedreports an inline file's size instead of echoing itsdata:URL. That event is stored with the session stream, so the echo kept every uploaded file's bytes there. The web template now shows a file icon instead of a thumbnail for user-sent images.This PR also removes the unused
eve-attachment:ref codec. Part of the stack from the research plan in #4625.Closes #3419. Related to #497.
Validation
turn-step.test.tsregression fails without the epilogue change and passes with it.pnpm --filter eve exec vitest run --config vitest.unit.config.ts: 9438 passed, 1 skipped.pnpm --filter eve exec vitest run --config vitest.integration.config.ts src/harness src/execution: 338 passed.pnpm --filter eve run typecheck,pnpm lint,pnpm fmt,pnpm guard:invariants, andpnpm docs:checkpass.Checklist
evepackagegit commit --signoff)