Trace eve agents with OpenTelemetry
Send your eve agent's traces to Maple and see each conversation as one Agent Session.
Send your eve agent’s traces to Maple and see each conversation as an Agent Session, with its messages, model calls, tool calls, subagents and token usage. The setup is one package and two files.
To see message text from a deployed agent, you also mark its conversations public.
You need eve 0.62 or newer and Node.js 24 or newer. On eve 0.61 or older, upgrade first.
Quick setup with a coding agent
Copy this prompt into a coding agent that can run shell commands, such as Claude Code, Codex or Cursor. It loads the maple-agent-tracing-eve skill and follows it.
Set up Maple agent tracing for eve in this project.
Load the skill with `npx skills use MapleTechLabs/maple/skills --skill maple-agent-tracing-eve`, then follow it.
My Maple ingest key is maple_pk_... and my organization is in the US region.
Your ingest key is in Settings → Ingestion. If your organization is in the EU region, change US to EU in the prompt.
Install the exporter
npm install @vercel/otelpnpm add @vercel/otelbun add @vercel/otelSend traces to Maple
Create agent/instrumentation/maple.ts:
// agent/instrumentation/maple.ts
import { OTLPHttpProtoTraceExporter } from "@vercel/otel"
import { disableInstrumentation } from "eve/instrumentation"
import { otelIntegration } from "eve/instrumentation/otel"
// A missing key disables export; it never stops the agent.
const mapleKey = process.env.MAPLE_INGEST_KEY
if (!mapleKey) console.warn("MAPLE_INGEST_KEY is not set; Maple telemetry export is disabled")
export default mapleKey
? otelIntegration({
traceExporter: new OTLPHttpProtoTraceExporter({
url: "https://ingest.maple.dev/v1/traces", // EU: https://ingest.eu.maple.dev/v1/traces
headers: { authorization: `Bearer ${mapleKey}` },
}),
// Leave out eve's internal workflow spans
exportPolicy: { span: ({ attributes }) => ({ emit: "gen_ai.conversation.id" in attributes }) },
})
: disableInstrumentation()
In deployments, set MAPLE_INGEST_KEY in your host’s environment variables. For local runs, see Check that it works.
Keep the exportPolicy line. Without it, eve’s internal workflow spans make up most of the data you send.
Name the service
Without this step, the service in Maple is named after your package.json. To choose the name and add the environment, create agent/instrumentation/otel.ts:
// agent/instrumentation/otel.ts
import { otel } from "eve/instrumentation/otel"
export default otel({
resource: {
"service.name": "support-agent",
// Outside Vercel, use your host's environment variable
"deployment.environment.name": process.env.VERCEL_ENV ?? "development",
},
})
If the project already has an otel() file, add the two attributes to it instead. eve allows only one.
Show message text in production
In eve dev, Maple shows every message, reply and tool call. A deployed agent sends that text only for conversations eve treats as public. Other conversations still appear with their turns, model calls, tools and tokens, but without any text.
eve’s Slack channel treats public channels as public, and DMs and private channels as private. Its HTTP channel treats no conversation as public until you mark it:
// agent/channels/eve.ts
import { localDev, vercelOidc } from "eve/channels/auth"
import { eveChannel } from "eve/channels/eve"
export default eveChannel({
auth: [vercelOidc(), localDev()],
audience: "public",
})
Keep the auth list your project already uses. A public channel sends its message text to every trace destination, including Vercel Agent Runs, so mark it public only if storing those conversations is acceptable. For other channels, see eve’s audience docs.
Check that it works
-
Copy the public ingest key (
maple_pk_…) from Settings → Ingestion (US or EU) and add it to.env.localin your project:MAPLE_INGEST_KEY=maple_pk_... -
Start the agent with
eve devand send it two messages, one of which uses a tool. eve sends the traces to Maple a few seconds after each reply.
You should see one session for the conversation with framework eve, titled with its first message, with one turn per message and the messages, replies and tool calls in its transcript. Subagents show up as separate lanes inside the turn that called them.
Cost shows for models that run through Vercel AI Gateway. With a model called directly at its provider, cost shows as unpriced because eve doesn’t report it.
Upgrade from eve 0.61 or older
eve 0.61 and older ignore the files in this guide without showing an error. Upgrade eve:
npm install eve@latestpnpm add eve@latestbun add eve@latestThen:
- Delete
agent/instrumentation.ts. Newer eve versions refuse to build while it exists. - If
agent.tssetsexperimental: { instrumentationProviders: true }, remove it. - Add the two files from this guide.
- If the old file did more than send traces to Maple, move each part:
In the old agent/instrumentation.ts | After the upgrade |
|---|---|
Another exporter in registerOTel({ traceExporter }) | Its own file with otelIntegration({ traceExporter }) |
serviceName | service.name in otel.ts |
functionId | otel({ functionId }) |
recordInputs, recordOutputs | Not needed: message text depends on whether the conversation is public |
events["step.started"] returning runtimeContext | otelIntegration({ runtimeContext }) |
Troubleshooting
-
Nothing arrives. eve doesn’t report failed exports, so check the key. The startup log must not say
MAPLE_INGEST_KEY is not set. Then send this request with your key in place ofYOUR_INGEST_KEY.200means the key works;401says what is wrong, including a key from the other region:curl -s -w "%{http_code}\n" -X POST https://ingest.maple.dev/v1/traces \ -H "Authorization: Bearer YOUR_INGEST_KEY" \ -H "Content-Type: application/json" -d '{"resourceSpans":[]}' -
Sessions have no message text. The conversation isn’t public. Mark the channel public. Setting a
tracePolicyinotel()doesn’t change this. -
Thousands of
workflow.*,world.*andstep.*spans. TheexportPolicyline is missing frommaple.ts. -
A failed tool shows as successful. The tool returned an error instead of throwing one. Throw from
execute.