feat(eve): versioned wire schema for session inbox hook payloads - #1767
feat(eve): versioned wire schema for session inbox hook payloads#1767ruiconti wants to merge 5 commits into
Conversation
Research plan plus prototype for #1765. Session inbox payloads now cross durable hooks through a single wire module: encodeSessionSend emits the v1 envelope (the deliver hybrid plus an explicit `v` field, byte- compatible with every live cohort) and decodeSessionInbox is the only consumer-side interpretation, normalizing legacy shapes and throwing SessionInboxWireError on anything unrecognized instead of reinterpreting it. Drops are surfaced through a recorded reporting step because the workflow driver bundle cannot import the logging module's Node builtins. Enforcement: guard rule 37 bans inline send/deliver literals at resumeHook outside execution/wire/ (proven red on a raw literal), and a frozen byte snapshot pins the v1 fixture so editing a shipped shape requires editing a file that says so. Signed-off-by: Rui Conti <ruiconti@gmail.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Bundle + Package Summary:
|
| Area | Warning | Details |
|---|---|---|
| Runtime | Total function bytes | 17.00 MB -> 19.85 MB; +2.85 MB |
Delta vs rui/hook-deliver-wire-compat (39c20ce)
| Area | Metric | Baseline | Current | Delta |
|---|---|---|---|---|
| Package | Packed tarball | 7.66 MB | 7.66 MB | +4.6 kB |
| Package | Unpacked publish size | 28.70 MB | 28.71 MB | +9.3 kB |
| Package | Installed footprint | 72.13 MB | 72.14 MB | +9.3 kB |
| Package | Published files | 3039 | 3045 | +6 |
| Package | Installed files | 6747 | 6753 | +6 |
| Runtime | Unique function payloads | 2 | 2 | 0 |
| Runtime | Total function bytes | 17.00 MB | 19.85 MB | +2.85 MB |
| Runtime | Public routes | 13 | 13 | 0 |
Changed function payloads vs rui/hook-deliver-wire-compat (39c20ce) (2)
| Function | Status | Baseline | Current | Delta | Route changes |
|---|---|---|---|---|---|
functions/__server.func |
changed | 8.50 MB | 9.93 MB | +1.43 MB |
none |
functions/.well-known/workflow/v1/flow.func |
changed | 8.50 MB | 9.93 MB | +1.43 MB |
none |
eve init install
| Metric | Baseline | Current | Delta |
|---|---|---|---|
| Installed footprint | 110.54 MB | 110.55 MB | +9.3 kB |
| Installed packages | 121 | 121 | 0 |
| dependencies | 4 | 4 | 0 |
| devDependencies | 2 | 2 | 0 |
| Dependency package bytes | 43.18 MB | 43.19 MB | +9.3 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:43:22.173Z
- 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.71 MB across 3045 published files
- Installed footprint: 72.14 MB across 6753 installed files
- Installed root package: 27.36 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.36 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.7%
🟡 dist/src/compiled/_chunks/workflow/undici-DWL... [#.......................] 502.4 kB 1.7%
🔴 Other published files [########################] 14.10 MB 49.1%
Installed footprint breakdown
Installed package size
🔴 eve [########################] 27.36 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.55 MB across 8629 installed files
- Installed packages: 121 total (115 transitive-only)
- dependencies: 4 direct packages totaling 43.19 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.36 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.36 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.36 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 [########################] 9.93 MB 50.0%
🔴 functions/__server.func [########################] 9.93 MB 50.0%
Top Function Payloads
🟠 functions/.well-known/workflow/v1/flow.func • 1 public route • 9.93 MB
| Metric | Value |
|---|---|
| Public routes | /.well-known/workflow/v1/flow |
| Runtime | nodejs24.x |
| Handler | index.mjs |
| Payload | 9.93 MB |
| Function files | 9.93 MB across 43 files |
| Traced dependencies | 0 B |
| Signal | 🟠 Bundled file index.mjs is 3.75 MB (37.7%) |
🟠 🔎 Dependency Analysis
📦 Bundled files:
Bundled file size
🟠 index.mjs [########################] 3.75 MB 37.7%
🟠 _chunks/runtime-artifacts.mjs [##########..............] 1.62 MB 16.3%
🟡 _libs/undici.mjs [######..................] 980.5 kB 9.9%
🟡 _chunks/sandbox.mjs [#####...................] 769.0 kB 7.7%
🟡 _libs/@ai-sdk/gateway+[...].mjs [###.....................] 432.8 kB 4.4%
🟠 Other bundled files [###############.........] 2.38 MB 24.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 • 9.93 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 | 9.93 MB |
| Function files | 9.93 MB across 43 files |
| Traced dependencies | 0 B |
| Signal | 🟠 Bundled file index.mjs is 3.75 MB (37.7%) |
🟠 🔎 Dependency Analysis
📦 Bundled files:
Bundled file size
🟠 index.mjs [########################] 3.75 MB 37.7%
🟠 _chunks/runtime-artifacts.mjs [##########..............] 1.62 MB 16.3%
🟡 _libs/undici.mjs [######..................] 980.5 kB 9.9%
🟡 _chunks/sandbox.mjs [#####...................] 769.0 kB 7.7%
🟡 _libs/@ai-sdk/gateway+[...].mjs [###.....................] 432.8 kB 4.4%
🟠 Other bundled files [###############.........] 2.38 MB 24.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.82 s -> 2.46 s (-353.9 ms) vs
rui/hook-deliver-wire-compat (39c20ce). - Timing is informational: shared GitHub runners are too variable for a hard timing budget.
Detailed phase timings vs `rui/hook-deliver-wire-compat (39c20ce)`
| Phase | Baseline | Current | Delta |
|---|---|---|---|
extension.check |
6.6 ms | 1.1 ms | -5.5 ms |
project.resolve |
5.4 ms | 0.6 ms | -4.8 ms |
workspace.create |
4.7 ms | 0.7 ms | -4.0 ms |
host.prepare |
907.4 ms | 212.0 ms | -695.4 ms |
vercel.service-prefix.resolve |
2.7 ms | 2.8 ms | +0.1 ms |
nitro.create |
213.5 ms | 442.1 ms | +228.6 ms |
sandbox.prewarm |
296.3 ms | 360.0 ms | +63.7 ms |
nitro.cache.prepare |
0.2 ms | 0.2 ms | 0.0 ms |
nitro.prepare |
0.9 ms | 0.8 ms | -0.1 ms |
nitro.public-assets |
0.8 ms | 0.7 ms | -0.1 ms |
nitro.prerender |
0.4 ms | 0.6 ms | +0.2 ms |
nitro.bundle |
1.35 s | 1.41 s | +58.3 ms |
nitro.cache.write |
0.3 ms | 0.4 ms | +0.1 ms |
vercel.workflow-function.materialize |
22.0 ms | 23.4 ms | +1.4 ms |
agent-summary.emit |
0.5 ms | 0.5 ms | 0.0 ms |
nitro.close |
0.1 ms | 0.2 ms | +0.1 ms |
output.publish |
3.6 ms | 6.8 ms | +3.2 ms |
workspace.remove |
2.2 ms | 2.5 ms | +0.3 ms |
…rsion Reshapes the prototype per review: the versioning discipline moves into a generic core (execution/wire/codec.ts) and the session inbox becomes its first family. Each wire version is exactly one pure structural zod schema (vendored #compiled/zod) with a separate normalizer; v0 is the unversioned era including control payloads. Decode validates instead of trusting: unknown versions, unrecognized kinds, and malformed shapes all throw a family-tagged WireDecodeError. Validation stops at the envelope — adapter- owned interiors cross as opaque objects. The contract test freezes each version's JSON Schema (editing a shipped schema goes red; proven), pins backwards compatibility with frozen payload fixtures per version, and round-trips encode through the current schema. The drop-report step and guard rule 37 are generalized to any wire family. Signed-off-by: Rui Conti <ruiconti@gmail.com>
…ntracts Encode now goes through the codec too: createWireCodec takes the family's build function and validates its output against the current version's schema before it persists (WireEncodeError), so producer drift dies at the producer instead of at a pinned consumer weeks later. Reference identity of the built envelope is preserved past validation. Guard rule 38 requires every execution/wire/*-wire.ts family to carry a colocated contract test with FROZEN_SHAPES and FROZEN_FIXTURES (proven red on a family without one), and rule 37's stale session-inbox-specific wording is generalized to wire families. The research doc records the encode rule, the layered enforcement story, and why protobuf was rejected for this boundary. 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>
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>
Summary
Related to #1765. Stacked on #1752 (base branch:
rui/hook-deliver-wire-compat); review only the commits above that base until it merges.Research plan (
research/session-inbox-wire-schema.md) plus a working prototype: durable hook payloads become declared, validated, versioned wire formats instead of an emergent property of whatever type flows intoresumeHook— the gap that let #1586 ship silent message loss as a "refactor".Design
execution/wire/codec.ts):createWireCodec({ family, versions })where each version is exactly one pure structural zod schema (vendored#compiled/zod) plus a separate normalizer. Version 0 is the unversioned era (legacy shapes and control payloads). Changing a shipped shape means adding a version — editing one is mechanically detectable (below).createWireCodectakes the family's build function and validates its output against the current version's schema before it persists (WireEncodeError) — producer drift dies at the producer, not at a pinned consumer weeks later. Reference identity of the built envelope survives validation.v, parse with that version's schema; unknown versions, unrecognized kinds, and malformed/truncated payloads all throw a family-taggedWireDecodeErrorat the boundary. Consumers drop the payload through a recorded reporting step (durable trace + structured error log; the driver bundle rejects direct logging's Node builtins) and keep the session alive.DeliverPayload,auth,caller) cross as opaque objects (z.custom<T>, object-ness only) — deep-validating them would turn every adapter field addition into a wire change. The frozen shapes render them as{}, making the opacity boundary visible in the contract itself.metadata: { eveVersion }, readable pre-resume viagetHookByToken, so future wire changes can be version-gated per consumer. The version comes from an fs-free stamp-based module (internal/package-version.ts) because the stamp site runs in workflow context. Markerless hooks mean the consumer predates the stamp.execution/wire/session-inbox-wire.ts): v0 = legacy deliver + raw 0.30.3–0.30.8 sends + controls; v1 = the fix(eve): persist session sends as the durable deliver envelope #1752 hybrid envelope +v: 1(byte-compatible with every live cohort, so no producer gating yet). The scatteredkind === "send"branches in both consumers collapse intodecodeSessionInbox.Backwards-compatibility checks
The contract test (
session-inbox-wire.test.ts) freezes three things:encodeoutput is byte-frozen, declarescurrentVersion, and parses under the current schema.Plus two mechanical guards in the lint job — rule 37 (inline
send/deliverliterals atresumeHookoutsideexecution/wire/are violations unless routed through anencode*wire function; proven red on a raw literal) and rule 38 (every*-wire.tsfamily must carry a colocatedFROZEN_SHAPES/FROZEN_FIXTUREScontract test; proven red on a family without one) — and the e2e byte gate asserting the emitted version end-to-end. The research doc records the layered enforcement story and why protobuf was rejected for this boundary (eve doesn't own the bytes —@workflow's devalue serde does; proto's silent unknown-field tolerance is the failure mode we're eliminating; no new runtime dependency).Validation
pnpm test:unit(579 files / 6050 passed, includes the wire contract and generic codec suites)pnpm --filter eve exec vitest run --config vitest.integration.config.ts src/execution/session-command-inbox.integration.test.ts src/execution/turn-cancellation.integration.test.ts(14 passed — frozen ≤0.30.2 and 0.30.7-style consumers accept the v1 envelope)EVE_E2E_MODEL=mock pnpm --filter agent-channels exec eve eval --strict --exclude-tag real-model(2 evals, 13 gates against rebuilt dist)pnpm typecheck— also proves vendored zod passes the workflow-bundle constraint that rejects Node builtinspnpm guard:invariants,pnpm fmt,pnpm lintgreen; rule 37 and the shape freeze both proven redReviewer attention
v, the mirror) and thesendshape never reach in-memory buffers.Checklist
CONTRIBUTING.mdevepackagegit commit --signoff)