Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/inbound-http-channel-trace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eve": patch
---

Add an opt-in OpenTelemetry `SERVER` span around each inbound HTTP channel request. Enable it with `defineInstrumentation({ traceChannelRequests: true })` — it is off by default. When enabled, the span is named for the registered route (e.g. `POST /eve/v1/session/:sessionId`), respects an incoming `traceparent` (becoming a child of the upstream span, or a trace root when there is none), and becomes the parent of nested channel and Workflow spans such as `hook.resume` and outgoing HTTP calls. It records only low-cardinality, non-sensitive attributes (`http.request.method`, `http.route` — the path template alone, per the OTel HTTP convention — `http.response.status_code`, `url.scheme`, `server.address`, `eve.channel.name`, `eve.channel.kind`) — never session ids, tokens, headers, bodies, or query parameters. This is observability-only: it does not change request handling and performs no synchronous span export in the request path, adding only minimal in-process tracing overhead.
11 changes: 11 additions & 0 deletions docs/guides/instrumentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,17 @@ ai.eve.turn {eve.session.id}

eve creates the `ai.eve.turn` parent span per turn and passes enriched telemetry to the AI SDK so model calls and tool executions are traced automatically. Session, turn, step, and channel context is injected as the framework half of the runtime context (`eve.version`, `eve.session.id`, `eve.environment`, `eve.turn.id`, `eve.turn.sequence`, `eve.step.index`, `eve.channel.kind`) and rides onto the spans alongside any values your `events["step.started"]` callback returns under `runtimeContext`.

Set `traceChannelRequests: true` on `defineInstrumentation` to also wrap each inbound channel HTTP request in a single OpenTelemetry `SERVER` span named for the registered route, which parents the turn tree above (and any `hook.resume` and outgoing HTTP spans):

```text
POST /eve/v1/session/:sessionId
└── hook.resume
├── GET hooks/by-token
└── POST hook_received
```

The span stays low-cardinality (route template in `http.route`, method in `http.request.method`, never the concrete URL) and records no session ids, tokens, headers, bodies, or query parameters. It adopts an incoming `traceparent` as its parent when present, so eve requests correlate with upstream traces. It defaults to `false`; enable it only when you want these request spans.

## Workflow run tags

Separately from OpenTelemetry, eve tags every workflow run with reserved `$eve.*` attributes. These live on the Vercel Workflow run, queryable in the Workflow dashboard, not on OTel spans, and you do not configure them: they are framework-owned and emitted automatically on every session, turn, and subagent run, whether or not an `instrumentation.ts` file is present. Authored code cannot set or override the `$eve.` namespace.
Expand Down
1 change: 1 addition & 0 deletions packages/eve/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@
"@chat-adapter/twilio": "4.34.0",
"@clack/core": "1.3.1",
"@nuxt/kit": "^4.0.0",
"@opentelemetry/context-async-hooks": "catalog:",
"@opentelemetry/otlp-transformer": "0.214.0",
"@opentelemetry/sdk-trace-base": "catalog:",
"@standard-schema/spec": "1.1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ export interface Span {
export interface Tracer {
startSpan(
name: string,
options?: { attributes?: Attributes | undefined; root?: boolean | undefined },
options?: {
attributes?: Attributes | undefined;
kind?: SpanKind | undefined;
root?: boolean | undefined;
},
context?: Context,
): Span;
}
Expand All @@ -40,6 +44,20 @@ export declare const context: {
with<T>(context: Context, fn: () => T): T;
};

/**
* Reads carrier values during context extraction. `Carrier` is the
* transport-specific container (e.g. a `Headers` instance for inbound
* HTTP requests).
*/
export interface TextMapGetter<Carrier = unknown> {
get(carrier: Carrier, key: string): string | string[] | undefined;
keys(carrier: Carrier): string[];
}

export declare const propagation: {
extract<Carrier>(context: Context, carrier: Carrier, getter: TextMapGetter<Carrier>): Context;
};

export declare const trace: {
getActiveSpan(): Span | undefined;
getTracer(name: string, version?: string): Tracer;
Expand Down
Loading
Loading