Skip to content

fix(eve): persist session sends as the durable deliver envelope - #1752

Open
ruiconti wants to merge 4 commits into
mainfrom
rui/hook-deliver-wire-compat
Open

fix(eve): persist session sends as the durable deliver envelope#1752
ruiconti wants to merge 4 commits into
mainfrom
rui/hook-deliver-wire-compat

Conversation

@ruiconti

@ruiconti ruiconti commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #1751.

Durable session hooks outlive deployments, but #1586 started persisting the caller-side SessionCommand ({ kind: "send", payload }) as the hook payload. A workflow pinned to eve ≤0.30.2 only understands the delivery envelope ({ kind: "deliver", payloads }) and silently ignores anything else, so after an eve upgrade, every follow-up into a pre-upgrade session recorded hook_received and then nothing — no turn, no failure, no log. This wedged v's production Slack threads (run wrun_41KZ9GJSZQ0GV6RDB9CKX0KFY9, thread in #1751).

This PR is deliberately the minimal forward fix. The long-term wire discipline (versioned, validated schemas plus a consumer-version stamp on hooks) lands separately in #1767 / #1765.

The wire format

Restoring pure deliver would trade one silent-loss cohort for another: drivers pinned to 0.30.3–0.30.8 cast any non-control inbox payload to a send command and read its payload field, so a payload without that field coalesces to {} and the message content is lost — silently, on the unconditional parked-session path every channel session uses. v production is pinned to 0.30.7 today.

The encoder therefore emits the delivery envelope with a transitional single-payload mirror:

             { kind: "deliver", payloads: [P], payload: P }
≤0.30.2        reads payloads ✓
0.30.3–0.30.8  casts to send, reads payload ✓
≥ this PR      reads payloads ✓
  • sendCommandToDelivery() (execution/session-command-wire.ts) is the single encoder; both dispatch aliases, both receiver-side conversions, and now routeProxiedDeliverStep (parent→child proxied deliveries, which previously persisted a raw send) go through it. send never crosses the durable boundary anymore.
  • The mirror is removable once runs created on 0.30.3–0.30.8 age out (30-day default session timeout); the removal condition is written at the encoder, the inbox decode, and the e2e gate.
  • Current inboxes keep decoding send payloads persisted by 0.30.3–0.30.8, version-stamped with the same removal condition.

Regression guards

Same-version tests can't catch this class: producer and consumer change together and everything passes. Three guards pin the wire:

  • Integration, old cohort: a frozen delivery-only consumer (legacy-session-delivery-workflow.ts) resumed through the real dispatchContinuation. Encoder bypassed → timeout; encoded → completes.
  • Integration, mid cohort: a frozen copy of the 0.30.3–0.30.8 decode (mid-cohort-session-delivery-workflow.ts, the commandToDelivery miscast at the 0.30.7 tag). Mirror stripped → the message is lost and the assertion fails; mirrored → completes.
  • E2E (agent-channels/evals/custom-channels/continuation-wire.eval.ts): decodes the persisted hook_received payload from the local event log and requires both the deliver envelope and the payload mirror at the byte level.

Validation

  • pnpm test:unit (full tier: 578 files / 6026 passed after the trim)
  • pnpm --filter eve exec vitest run --config vitest.integration.config.ts src/execution/session-command-inbox.integration.test.ts (5 passed; mid-cohort test proven red with the mirror stripped)
  • EVE_E2E_MODEL=mock pnpm --filter agent-channels exec eve eval --strict --exclude-tag real-model (2 evals, 13 gates; the wire eval's deliver gate was proven red/green in the first commit, and the mirror gate against rebuilt dist — its red path is covered by the mid-cohort integration test rather than a second rebuild)
  • pnpm fmt, pnpm lint, pnpm typecheck, pnpm guard:invariants

Checklist

  • I linked an issue with prior discussion confirming this change is wanted
  • I ran the relevant checks from CONTRIBUTING.md
  • 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)

Session hooks outlive deployments: 0.30.3 started persisting the caller-side
SessionCommand ({kind:"send"}) directly, so a newer channel route resuming a
workflow pinned to <=0.30.2 delivered a payload the old driver silently
ignored. Encode every send through one sendCommandToDelivery helper before
resumeHook, keep accepting the send shape persisted by 0.30.3-0.30.8, and pin
the persisted bytes with a mixed-version integration test plus an e2e eval
that decodes the durable hook_received payload.

Closes #1751

Signed-off-by: Rui Conti <ruiconti@gmail.com>
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
eve-docs Ready Ready Preview Aug 7, 2026 4:39am
eve-docs-4759 Ready Ready Preview, v0 Aug 7, 2026 4:39am

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

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

Key takeaways

  • No notable deltas vs main (99982f7).

Delta vs main (99982f7)

Area Metric Baseline Current Delta
Package Packed tarball 7.66 MB 7.66 MB -350 B ✅
Package Unpacked publish size 28.70 MB 28.70 MB +3.2 kB ⚠️
Package Installed footprint 72.13 MB 72.13 MB +3.2 kB ⚠️
Package Published files 3037 3039 +2
Package Installed files 6745 6747 +2
Runtime Unique function payloads 2 2 0
Runtime Total function bytes 16.99 MB 17.00 MB +8.3 kB ⚠️
Runtime Public routes 13 13 0
Changed function payloads vs main (99982f7) (2)
Function Status Baseline Current Delta Route changes
functions/__server.func changed 8.49 MB 8.50 MB +4.1 kB ⚠️ none
functions/.well-known/workflow/v1/flow.func changed 8.50 MB 8.50 MB +4.1 kB ⚠️ none

eve init install

Metric Baseline Current Delta
Installed footprint 110.54 MB 110.54 MB +3.2 kB ⚠️
Installed packages 121 121 0
dependencies 4 4 0
devDependencies 2 2 0
Dependency package bytes 43.18 MB 43.18 MB +3.2 kB ⚠️
devDependency package bytes 5.04 MB 5.04 MB 0 B ➖
Build Metadata
  • Preset: vercel
  • Nitro: nitro@3.0.260610-beta
  • Output directory: apps/fixtures/weather-agent/.vercel/output
  • Build metadata timestamp: 2026-08-07T04:40:45.540Z
  • Route aliases: 13 public, 1 internal (14 total aliases)
  • Vercel routes in config: 16
  • Severity legend: 🔴 dominant/large, 🟠 notable, 🟡 watch, ⚪ small
Package Drill-Down

Package Details

  • Package: eve@0.31.0
  • Package directory: packages/eve
  • Tarball: 7.66 MB (eve-0.31.0.tgz)
  • Unpacked payload: 28.70 MB across 3039 published files
  • Installed footprint: 72.13 MB across 6747 installed files
  • Installed root package: 27.35 MB
  • Installed dependencies: 44.78 MB
  • Runtime dependencies: 2
  • Peer dependencies: 5 (4 optional)

Installed footprint is measured from an isolated temporary npm install of the packed tarball.

Heavy installed dependencies

  • eve: 27.35 MB (37.9%)
  • @rolldown/binding-linux-x64-gnu: 19.35 MB (26.8%)
  • ai: 6.67 MB (9.2%)
  • zod: 5.07 MB (7.0%)
  • undici: 3.50 MB (4.9%)
Publish payload breakdown
Published file size
🔴 dist/src/compiled/shadcn-registry/index.js       [################........] 9.68 MB 33.7%
🟠 dist/src/compiled/@photon-ai/chat-adapter-ime... [####....................] 2.42 MB 8.4%
🟠 dist/src/compiled/experimental-ai-sdk-code-mo... [###.....................] 1.51 MB 5.2%
🟡 dist/src/compiled/_chunks/node/undici-DWL_MYm... [#.......................] 502.4 kB 1.8%
🟡 dist/src/compiled/_chunks/workflow/undici-DWL... [#.......................] 502.4 kB 1.8%
🔴 Other published files                            [########################] 14.09 MB 49.1%
Installed footprint breakdown
Installed package size
🔴 eve                             [########################] 27.35 MB 37.9%
🔴 @rolldown/binding-linux-x64-gnu [#################.......] 19.35 MB 26.8%
🔴 ai                              [######..................] 6.67 MB 9.2%
🔴 zod                             [####....................] 5.07 MB 7.0%
🟠 undici                          [###.....................] 3.50 MB 4.9%
🟠 nitro                           [##......................] 2.41 MB 3.3%
🔴 Other installed packages        [#######.................] 7.79 MB 10.8%
Runtime dependencies (2)
Package Range Notes
nitro 3.0.260610-beta
undici 8.9.0
Peer dependencies (5)
Package Range Notes
@opentelemetry/api ^1.0.0 optional peer
ai catalog:
braintrust ^3.0.0 optional peer
just-bash ^3.0.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: 110.54 MB across 8623 installed files
  • Installed packages: 121 total (115 transitive-only)
  • dependencies: 4 direct packages totaling 43.18 MB
  • devDependencies: 2 direct packages totaling 5.04 MB
  • Other transitive package files: 62.32 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 (25.3%)
  • eve: 27.35 MB (24.7%)
  • @rolldown/binding-linux-x64-gnu: 19.35 MB (17.5%)
  • zod: 9.02 MB (8.2%)
  • ai: 6.67 MB (6.0%)
Installed footprint breakdown
Installed package size
🔴 @typescript/typescript-linux-x64 [########################] 27.95 MB 25.3%
🔴 eve                              [#######################.] 27.35 MB 24.7%
🔴 @rolldown/binding-linux-x64-gnu  [#################.......] 19.35 MB 17.5%
🔴 zod                              [########................] 9.02 MB 8.2%
🔴 ai                               [######..................] 6.67 MB 6.0%
🟠 undici                           [###.....................] 3.50 MB 3.2%
🔴 Other installed packages         [##############..........] 16.70 MB 15.1%
dependencies (4)
Package Range Installed size Share
@vercel/connect 0.4.3 141.0 kB 0.1%
ai ^7.0.38 6.67 MB 6.0%
eve file:eve-0.31.0.tgz 27.35 MB 24.7%
zod 4.4.3 9.02 MB 8.2%
devDependencies (2)
Package Range Installed size Share
@types/node 24.x 2.54 MB 2.3%
typescript 7.0.2 2.50 MB 2.3%
Function Drill-Down

Payload Size Graph

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

Top Function Payloads

🟠 functions/.well-known/workflow/v1/flow.func • 1 public route • 8.50 MB
Metric Value
Public routes /.well-known/workflow/v1/flow
Runtime nodejs24.x
Handler index.mjs
Payload 8.50 MB
Function files 8.50 MB across 43 files
Traced dependencies 0 B
Signal 🟠 Bundled file index.mjs is 2.32 MB (27.3%)

🟠 🔎 Dependency Analysis

📦 Bundled files:

Bundled file size
🟠 index.mjs                       [#######################.] 2.32 MB 27.3%
🟠 _chunks/runtime-artifacts.mjs   [################........] 1.62 MB 19.0%
🟡 _libs/undici.mjs                [##########..............] 980.5 kB 11.5%
🟡 _chunks/sandbox.mjs             [########................] 769.0 kB 9.0%
🟡 _libs/@ai-sdk/gateway+[...].mjs [####....................] 432.8 kB 5.1%
🟠 Other bundled files             [########################] 2.38 MB 28.0%

🧾 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 • 12 public routes, 1 internal alias • 8.50 MB
Metric Value
Public routes /
/eve/v1/callback/[token]
/eve/v1/connections/[name]/callback/[token]
/eve/v1/health
/eve/v1/info
/eve/v1/session
/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
Internal aliases /__server
Runtime nodejs24.x
Handler index.mjs
Payload 8.50 MB
Function files 8.50 MB across 43 files
Traced dependencies 0 B
Signal 🟠 Bundled file index.mjs is 2.32 MB (27.3%)

🟠 🔎 Dependency Analysis

📦 Bundled files:

Bundled file size
🟠 index.mjs                       [#######################.] 2.32 MB 27.3%
🟠 _chunks/runtime-artifacts.mjs   [################........] 1.62 MB 19.0%
🟡 _libs/undici.mjs                [##########..............] 980.5 kB 11.5%
🟡 _chunks/sandbox.mjs             [########................] 769.0 kB 9.0%
🟡 _libs/@ai-sdk/gateway+[...].mjs [####....................] 432.8 kB 5.1%
🟠 Other bundled files             [########################] 2.38 MB 28.0%

🧾 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: 2.02 s -> 2.01 s (-14.6 ms) vs main (99982f7).
  • Timing is informational: shared GitHub runners are too variable for a hard timing budget.
Detailed phase timings vs `main (99982f7)`
Phase Baseline Current Delta
extension.check 1.1 ms 1.1 ms 0.0 ms
project.resolve 0.6 ms 0.7 ms +0.1 ms
workspace.create 0.7 ms 0.7 ms 0.0 ms
host.prepare 175.5 ms 149.8 ms -25.7 ms
vercel.service-prefix.resolve 2.6 ms 2.7 ms +0.1 ms
nitro.create 235.6 ms 240.8 ms +5.2 ms
sandbox.prewarm 254.1 ms 245.2 ms -8.9 ms
nitro.cache.prepare 0.2 ms 0.3 ms +0.1 ms
nitro.prepare 0.7 ms 0.9 ms +0.2 ms
nitro.public-assets 0.7 ms 0.8 ms +0.1 ms
nitro.prerender 0.4 ms 0.5 ms +0.1 ms
nitro.bundle 1.32 s 1.33 s +11.9 ms
nitro.cache.write 0.4 ms 0.4 ms 0.0 ms
vercel.workflow-function.materialize 22.6 ms 24.6 ms +2.0 ms
agent-summary.emit 0.5 ms 0.5 ms 0.0 ms
nitro.close 0.1 ms 0.1 ms 0.0 ms
output.publish 3.4 ms 3.7 ms +0.3 ms
workspace.remove 2.1 ms 2.2 ms +0.1 ms

@vercel vercel Bot left a comment

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.

Additional Suggestion:

routeProxiedDeliverStep persists a raw { kind: "send" } SessionCommand across a durable child-session-driver hook boundary, breaking the PR's single-wire-format (deliver) invariant so older/differently-versioned consumers may silently drop the proxied delivery.

Fix on Vercel

…d stamp hook versions

Sessions pinned to eve 0.30.3-0.30.8 decode any non-control inbox payload
by casting it to a send command and reading .payload, so a pure deliver
envelope silently empties their deliveries. The wire encoder now mirrors
the single payload alongside payloads; the mirror is removable once runs
created on those versions age out (30-day session timeout).

Proxied parent-to-child deliveries in routeProxiedDeliverStep now cross
the durable hook through the same encoder instead of persisting a raw
send command.

Session inbox hooks are stamped with the consumer's eve version via
createHook metadata, read from a stamp-based fs-free module so workflow
bundles stay clear of node:fs. Producers can read it pre-resume via
getHookByToken to version-gate future wire changes; markerless hooks
predate the stamp.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
Two suites outside the locally-run subset still asserted the unmirrored
deliver envelope.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
…to the follow-up

The eveVersion hook-metadata stamp (and its fs-free package-version
module) is future version-gating infrastructure, not part of the
mitigation. It re-lands with the versioned wire codec in #1767 so this
PR stays a pure forward fix: hybrid deliver envelope, both producers
routed through the encoder, legacy decodes, and the regression guards.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
ruiconti added a commit that referenced this pull request Aug 7, 2026
Re-lands the hook-metadata stamp moved out of #1752 to keep that PR a
pure mitigation. Producers read it pre-resume via getHookByToken to
version-gate future wire changes; the version comes from the fs-free
stamp-based module so workflow bundles stay clear of node:fs. Markerless
hooks mean the consumer predates the stamp.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
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.

Durable session hooks persisted kind: "send" (0.30.3+), silently wedging sessions pinned to older deployments

1 participant