Skip to content

feat(eve): keep local tracing on with authored instrumentation - #1306

Draft
chadhietala wants to merge 3 commits into
mainfrom
feat/always-on-local-tracing
Draft

feat(eve): keep local tracing on with authored instrumentation#1306
chadhietala wants to merge 3 commits into
mainfrom
feat/always-on-local-tracing

Conversation

@chadhietala

Copy link
Copy Markdown
Contributor

Local dev tracing was gated on the absence of agent/instrumentation.ts. Adding an exporter for Honeycomb or Datadog cost you .eve/traces/v1, eve trace ls, and eve trace <id> — the whole local inspection loop.

This makes the two coexist in eve dev.

What changed

eve adopts the authored provider instead of competing with it. The global that trace.setGlobalTracerProvider registers is a ProxyTracerProvider, so eve swaps its delegate for one that observes every tracer it hands out. The authored exporter keeps receiving everything it received before, and additionally sees eve's agent.* spans, which it never got. eve spools the same spans to .eve/traces/v1.

When no provider is registered — no authored file, or an authored setup() that registers nothing — eve falls through to registerOTel exactly as today.

The proxy is matched structurally rather than with instanceof: authored instrumentation.ts resolves @opentelemetry/api from the agent's own dependencies while eve uses its vendored copy, so the two hold different classes. Only the globalThis registry is shared.

  • harness/adopt-global-tracer-provider.ts (new) — wraps the delegate's tracers and spans; startActiveSpan is reimplemented over startSpan so span creation is always interceptable, and the shadowed span.end is idempotent and runs the real end first so the author's own processors see a finished span.
  • create-application-nitro.ts — the dev gate is gone. The local plugin is unshifted when there is no authored plugin (today's ordering, unchanged) and pushed after it when there is, so a provider exists to adopt.
  • local-instrumentation-runtime.tsadoptGlobalTracerProvider(processor) or else registerOTel. A failed registration probe now warns and installs nothing instead of throwing: a dev-only convenience should not take down a dev server whose authored instrumentation is otherwise fine.
  • @opentelemetry/api.d.ts — the vendored declaration widens by the minimum: TracerProvider, ProxyTracerProvider, trace.getTracerProvider(), Span.isRecording?(), end(endTime?).

tool-loop.ts already composed integrations: [bridgeIntegration, createOtelIntegration()] for the both-present case, so the turn-time path needed no change. AgentTraceSpanProcessor is duck-typed and already filters the workflow instrumentation scope, so local traces stay free of Workflow spans even though the adopted provider may carry auto-instrumentation.

Scope

eve dev only. Production is untouched, and the authored public API (defineInstrumentation, setup() calling registerOTel) does not change. EVE_TRACES=off remains the opt-out.

Tests

  • Unit adopt-global-tracer-provider.test.ts — spans reach onStart/onEnd; the inner end runs exactly once and before onEnd; sampled-out spans are skipped; startActiveSpan returns the callback's value; one tracer per name@version; a non-proxy global declines.
  • Scenario local-instrumentation-runtime-conflict.scenario.test.ts — inverted from asserting the throw. Registers via the agent's own @vercel/otel first, then asserts the authored processor still receives agent.session and eve writes a segment under .eve/traces/v1/<traceId>.
  • Scenario create-application-nitro.scenario.test.ts — dev plugin ordering: the local plugin follows the authored one when present.

`eve dev` gated its zero-config trace writer on the absence of
`agent/instrumentation.ts`, so adding an exporter for Honeycomb or
Datadog cost you `.eve/traces/v1` and the whole `eve trace` loop.

eve now adopts the authored provider instead of competing with it. The
global registered by `setGlobalTracerProvider` is a `ProxyTracerProvider`,
so eve swaps its delegate for one that observes every tracer it hands
out. The authored exporter keeps receiving everything it did before and
additionally sees eve's `agent.*` spans; eve spools the same spans to
disk. When nothing is registered, eve registers its own provider exactly
as before.

The dev plugin is ordered after the authored instrumentation plugin so a
provider exists to adopt, and a failed registration probe now warns and
installs nothing rather than throwing — a development convenience must
not take down a dev server whose instrumentation is otherwise fine.

Production is untouched.

Signed-off-by: Chad Hietala <chadhietala@gmail.com>
@vercel

vercel Bot commented Jul 29, 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, Comment, Open in v0 Jul 29, 2026 1:40am
eve-docs-4759 Ready Ready Preview, Comment, Open in v0 Jul 29, 2026 1:40am

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

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

Key takeaways

  • No notable deltas vs main (c27b44a).

Delta vs main (c27b44a)

Area Metric Baseline Current Delta
Package Packed tarball 7.59 MB 7.61 MB +16.1 kB ⚠️
Package Unpacked publish size 28.70 MB 28.75 MB +52.3 kB ⚠️
Package Installed footprint 69.82 MB 69.88 MB +52.3 kB ⚠️
Package Published files 2854 2863 +9
Package Installed files 6327 6336 +9
Runtime Unique function payloads 2 2 0
Runtime Total function bytes 16.70 MB 16.72 MB +26.6 kB ⚠️
Runtime Public routes 11 11 0
Changed function payloads vs main (c27b44a) (2)
Function Status Baseline Current Delta Route changes
functions/__server.func changed 8.35 MB 8.36 MB +13.3 kB ⚠️ none
functions/.well-known/workflow/v1/flow.func changed 8.35 MB 8.36 MB +13.3 kB ⚠️ none

eve init install

Metric Baseline Current Delta
Installed footprint 108.23 MB 108.28 MB +52.3 kB ⚠️
Installed packages 123 123 0
dependencies 4 4 0
devDependencies 2 2 0
Dependency package bytes 43.02 MB 43.07 MB +52.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-07-29T01:41:10.886Z
  • Route aliases: 11 public, 1 internal (12 total aliases)
  • Vercel routes in config: 14
  • Severity legend: 🔴 dominant/large, 🟠 notable, 🟡 watch, ⚪ small
Package Drill-Down

Package Details

  • Package: eve@0.27.11
  • Package directory: packages/eve
  • Tarball: 7.61 MB (eve-0.27.11.tgz)
  • Unpacked payload: 28.75 MB across 2863 published files
  • Installed footprint: 69.88 MB across 6336 installed files
  • Installed root package: 27.39 MB
  • Installed dependencies: 42.48 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.39 MB (39.2%)
  • @rolldown/binding-linux-x64-gnu: 18.96 MB (27.1%)
  • ai: 6.52 MB (9.3%)
  • zod: 5.07 MB (7.3%)
  • nitro: 2.41 MB (3.4%)
Publish payload breakdown
Published file size
🔴 dist/src/compiled/shadcn-registry/index.js       [########################] 13.15 MB 45.7%
🟠 dist/src/compiled/experimental-ai-sdk-code-mo... [###.....................] 1.51 MB 5.2%
🟡 dist/src/compiled/@vercel/sandbox/index.js       [#.......................] 632.5 kB 2.2%
🟡 dist/src/compiled/_chunks/workflow/undici-DWL... [#.......................] 502.4 kB 1.7%
🟡 dist/src/compiled/@chat-adapter/slack/index.js   [#.......................] 440.5 kB 1.5%
🔴 Other published files                            [#######################.] 12.52 MB 43.5%
Installed footprint breakdown
Installed package size
🔴 eve                             [########################] 27.39 MB 39.2%
🔴 @rolldown/binding-linux-x64-gnu [#################.......] 18.96 MB 27.1%
🔴 ai                              [######..................] 6.52 MB 9.3%
🔴 zod                             [####....................] 5.07 MB 7.3%
🟠 nitro                           [##......................] 2.41 MB 3.4%
🟠 undici                          [##......................] 1.86 MB 2.7%
🔴 Other installed packages        [#######.................] 7.67 MB 11.0%
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: 108.28 MB across 8204 installed files
  • Installed packages: 123 total (117 transitive-only)
  • dependencies: 4 direct packages totaling 43.07 MB
  • devDependencies: 2 direct packages totaling 5.04 MB
  • Other transitive package files: 60.17 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.8%)
  • eve: 27.39 MB (25.3%)
  • @rolldown/binding-linux-x64-gnu: 18.96 MB (17.5%)
  • zod: 9.02 MB (8.3%)
  • ai: 6.52 MB (6.0%)
Installed footprint breakdown
Installed package size
🔴 @typescript/typescript-linux-x64 [########################] 27.95 MB 25.8%
🔴 eve                              [########################] 27.39 MB 25.3%
🔴 @rolldown/binding-linux-x64-gnu  [################........] 18.96 MB 17.5%
🔴 zod                              [########................] 9.02 MB 8.3%
🔴 ai                               [######..................] 6.52 MB 6.0%
🟠 @types/node                      [##......................] 2.54 MB 2.3%
🔴 Other installed packages         [##############..........] 15.90 MB 14.7%
dependencies (4)
Package Range Installed size Share
@vercel/connect 0.4.2 135.8 kB 0.1%
ai ^7.0.38 6.52 MB 6.0%
eve file:eve-0.27.11.tgz 27.39 MB 25.3%
zod 4.4.3 9.02 MB 8.3%
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.36 MB 50.0%
🔴 functions/__server.func                         [########################] 8.36 MB 50.0%

Top Function Payloads

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

🟠 🔎 Dependency Analysis

📦 Bundled files:

Bundled file size
🟠 index.mjs                       [#######################.] 2.26 MB 27.1%
🟠 _chunks/runtime-artifacts.mjs   [################........] 1.59 MB 19.0%
🟡 _libs/undici.mjs                [##########..............] 980.5 kB 11.7%
🟡 _chunks/sandbox.mjs             [########................] 768.8 kB 9.2%
🟡 _libs/@ai-sdk/gateway+[...].mjs [####....................] 432.8 kB 5.2%
🟠 Other bundled files             [########################] 2.33 MB 27.8%

🧾 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 • 10 public routes, 1 internal alias • 8.36 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]/stream
/eve/v1/session/reset
Internal aliases /__server
Runtime nodejs24.x
Handler index.mjs
Payload 8.36 MB
Function files 8.36 MB across 44 files
Traced dependencies 0 B
Signal 🟠 Bundled file index.mjs is 2.26 MB (27.1%)

🟠 🔎 Dependency Analysis

📦 Bundled files:

Bundled file size
🟠 index.mjs                       [#######################.] 2.26 MB 27.1%
🟠 _chunks/runtime-artifacts.mjs   [################........] 1.59 MB 19.0%
🟡 _libs/undici.mjs                [##########..............] 980.5 kB 11.7%
🟡 _chunks/sandbox.mjs             [########................] 768.8 kB 9.2%
🟡 _libs/@ai-sdk/gateway+[...].mjs [####....................] 432.8 kB 5.2%
🟠 Other bundled files             [########################] 2.32 MB 27.8%

🧾 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: 1.95 s -> 1.79 s (-158.0 ms) vs main (c27b44a).
  • Timing is informational: shared GitHub runners are too variable for a hard timing budget.
Detailed phase timings vs `main (c27b44a)`
Phase Baseline Current Delta
extension.check 1.1 ms 11.1 ms +10.0 ms
project.resolve 0.6 ms 1.0 ms +0.4 ms
workspace.create 0.7 ms 5.2 ms +4.5 ms
host.prepare 282.2 ms 173.9 ms -108.3 ms
vercel.service-prefix.resolve 2.0 ms 1.7 ms -0.3 ms
nitro.create 203.1 ms 177.5 ms -25.6 ms
sandbox.prewarm 172.5 ms 183.0 ms +10.5 ms
nitro.cache.prepare 0.3 ms 0.3 ms 0.0 ms
nitro.prepare 0.8 ms 0.8 ms 0.0 ms
nitro.public-assets 0.7 ms 0.7 ms 0.0 ms
nitro.prerender 0.5 ms 0.6 ms +0.1 ms
nitro.bundle 1.25 s 1.20 s -49.8 ms
nitro.cache.write 0.4 ms 0.3 ms -0.1 ms
vercel.workflow-function.materialize 21.4 ms 22.1 ms +0.7 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.3 ms 3.3 ms 0.0 ms
workspace.remove 2.0 ms 2.0 ms 0.0 ms

`trace.getTracerProvider()` returns a `ProxyTracerProvider` whether or
not anyone registered one: with nothing registered the API hands back
its own standby proxy rather than a no-op. The duck-typed check saw
`setDelegate` on that standby, wrapped the no-op delegate, and skipped
`registerOTel` — so on the zero-config path, which is the common one,
no processor was ever attached and no context manager registered. Every
`eve dev` run without an authored `instrumentation.ts` logged "eve could
not observe OpenTelemetry spans" and wrote nothing.

Compare delegates instead. A fresh proxy's delegate is the shared no-op
singleton, so a delegate that differs from it is the one an author
registered.

The unit test for the no-provider case passed over a fake that had no
`getDelegate` at all, which is not what an unclaimed process looks like.
It now also covers a real `ProxyTracerProvider` with no delegate set.

Signed-off-by: Chad Hietala <chadhietala@gmail.com>
Review of the always-on local tracing change surfaced five defects. Adoption
swapped a proxy's delegate after authored `setup()` had run, which is too
late: `ProxyTracerProvider.getTracer` hands out the delegate's concrete
tracer once a delegate exists, so any tracer the authored setup created —
an auto-instrumentation's, say — kept a direct reference eve could never
reach.

eve now claims the global `@opentelemetry/api` registry slot from a plugin
that runs *before* authored instrumentation, so every tracer the author's
provider hands out is observed from the start. It does not create the
registry object: that object carries the version of whichever API instance
registers first and `registerGlobal` refuses a mismatched write, so eve
decorates the author's own object when it arrives. Adoption stays as the
fallback for a slot eve cannot claim.

Also fixed:

- Startup no longer depends on the authored sampler. The old probe started a
  real span and required its processor to see it, so an `always_off` or
  ratio sampler made `eve dev` boot probabilistically. eve now checks only
  for a context manager, using a span the API owns outright.
- Spans are observed through a `Proxy` rather than by assigning over
  `span.end`, which threw on a frozen or accessor-backed span. A span whose
  `end` cannot be substituted at all is declined rather than crashed on.
- `setup` may be `async` and is awaited before anything else looks for a
  tracer provider. It typechecked as `async` before but its promise was
  dropped, letting eve's own setup win the race and starve the authored
  exporter.
- Observed tracers are no longer cached on an interpolated `name@version`
  key, which ignored `options.schemaUrl` and could collide.

Signed-off-by: Chad Hietala <chadhietala@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.

2 participants