maple-agent-tracing-ag…
Trace Agno agents with Maple: installs the OpenInference Agno instrumentor with an OTLP…
Effect-TS OpenTelemetry style for Maple via @maple-dev/effect-sdk: Maple.layer() bootstrap, Effect.withSpan / Effect.annotateCurrentSpan call sites, Effect.log for trace-correlated logging, server / browser / Cloudflare entry points.
$ npx -y skills add mapletechlabs/maple --skill maple-effect-style --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/maple-effect-styleContext preview
The summary Claude sees to decide when to auto-load this skill.
Effect-TS OpenTelemetry style for Maple via @maple-dev/effect-sdk: Maple.layer() bootstrap, Effect.withSpan / Effect.annotateCurrentSpan call sites, Effect.log for trace-correlated logging, server / browser / Cloudflare entry points.
name: maple-effect-style description: "Effect-TS OpenTelemetry style for Maple via @maple-dev/effect-sdk: Maple.layer() bootstrap, Effect.withSpan / Effect.annotateCurrentSpan call sites, Effect.log for trace-correlated logging, server / browser / Cloudflare entry points."
For Effect apps, use `@maple-dev/effect-sdk`. It wraps Effect's built-in `Otlp.layerJson` exporter and handles batching, shutdown, and resource attributes.
npm install @maple-dev/effect-sdk effect
The current release requires Effect 4 (`effect >= 4.0.0-rc.113`). For Effect 3, install `@maple-dev/effect-sdk@effect-v3 effect @effect/platform @effect/opentelemetry`. The API and import paths are the same.
Pick the entry point per runtime. Each has different lifecycle requirements:
import { Maple } from "@maple-dev/effect-sdk"
import { Effect } from "effect"
const TracerLive = Maple.layer({
serviceName: "orders-api",
endpoint: "https://ingest.maple.dev", // EU: https://ingest.eu.maple.dev
ingestKey: "MAPLE_TEST", // public ingest key (maple_pk_…), or MAPLE_TEST until the user has one
repositoryUrl: "https://github.com/acme/orders-api",
})
const program = Effect.gen(function* () {
yield* Effect.log("Order received")
}).pipe(Effect.withSpan("order.submit"))
Effect.runPromise(program.pipe(Effect.provide(TracerLive)))The default import resolves to the server build under Node.js. Import `@maple-dev/effect-sdk/server` explicitly when needed.
If `endpoint` is omitted, the server layer reads `MAPLE_ENDPOINT`, then `OTEL_EXPORTER_OTLP_ENDPOINT`, then falls back to the public ingest for the region (`https://ingest.maple.dev`, or `https://ingest.eu.maple.dev` with `region: "eu"` / `MAPLE_REGION=eu`). The key falls back to `MAPLE_INGEST_KEY`. `Maple.layer` always exports. A missing ingest key does not disable it, so keyless local-mode and self-hosted-collector setups keep working. Inline the key when telemetry must flow regardless of env (the maple-onboard inline-key pattern). `MapleFlush.make` and the Cloudflare `make()` differ: they no-op without a key.
The server layer also auto-fills `vcs.ref.head.revision` from `COMMIT_SHA` / `RAILWAY_GIT_COMMIT_SHA` / `VERCEL_GIT_COMMIT_SHA` / `CF_PAGES_COMMIT_SHA` / `RENDER_GIT_COMMIT` (first match wins). For `vcs.repository.url.full`, use the `repositoryUrl` option or `MAPLE_REPOSITORY_URL`. Do not hand-write the attribute. The layer also dual-emits `deployment.environment` and `deployment.environment.name` from the `environment` option or `MAPLE_ENVIRONMENT`.
The Cloudflare entry point exports `make()`, not a `Maple` namespace. Build the telemetry object **once at module scope**. It buffers in-isolate and resolves `env` lazily on the first flush:
import * as MapleCloudflareSDK from "@maple-dev/effect-sdk/cloudflare"
import { Effect } from "effect"
const telemetry = MapleCloudflareSDK.make({
serviceName: "orders-edge",
endpoint: "https://ingest.maple.dev", // EU: https://ingest.eu.maple.dev
ingestKey: "MAPLE_TEST",
})
export default {
async fetch(req: Request, env: Env, ctx: ExecutionContext) {
const program = Effect.gen(function* () {
yield* Effect.log("edge request")
return new Response("ok")
}).pipe(Effect.withSpan("edge.handle"))
const response = await Effect.runPromise(program.pipe(Effect.provide(telemetry.layer)))
ctx.waitUntil(telemetry.flush(env))
return response
},
}Call `ctx.waitUntil(telemetry.flush(env))` on every request so telemetry survives the isolate exit. `flush` takes `env`. A missing `waitUntil` is the most common reason Worker traces never arrive. When routes go through `HttpRouter.toWebHandler`, provide `telemetry.layer` to the layer you pass it (`Layer.provideMerge(telemetry.layer)`), not to a separate per-request runtime.
import { Maple } from "@maple-dev/effect-sdk/client"
const TracerLive = Maple.layer({
serviceName: "web-client",
endpoint: "https://ingest.maple.dev", // EU: https://ingest.eu.maple.dev
ingestKey: "MAPLE_TEST",
})The browser entry point has no env-var fallback. Pass all config explicitly. An explicit `endpoint` wins over `region`, so EU organizations change the endpoint itself (or drop `endpoint` and set `region: "eu"`). It records session replays by default; opt out with `replay: { enabled: false }`.
Use `Effect.withSpan` to trace operations and `Effect.annotateCurrentSpan` for attributes. Do not reach for the raw `@opentelemetry/api` tracer when an Effect-native primitive exists.
const processOrder = (orderId: string) =>
Effect.gen(function* () {
yield* Effect.annotateCurrentSpan("order.id", orderId)
const result = yield* chargePayment(orderId)
return result
}).pipe(Effect.withSpan("order.process"))Maple's service map draws a service-to-service edge by joining a Client span to the downstream service's child Server span. `peer.service` does not draw edges. Make outgoing calls through Effect's `HttpClient`: it creates the Client span and injects `traceparent` by default. Calls to uninstrumented dependencies appear as external nodes keyed on `server.address`.
`Effect.fail` and uncaught defects end the span with status `Error` and an `exception` event. Do not wrap with `try` / `catch` / `finally`.
In Effect code, use the Effect-native span primitives, not the raw `@opentelemetry/api` tracer.
`Effect.log` inside a span carries the trace context. No extra setup is needed:
const program = Effect.gen(function* () {
yield* Effect.log("Processing started")
yield* doWork()
yield* Effect.log("Processing complete")
}).pipe(Effect.wRepo: mapletechlabs/maple
Trace Agno agents with Maple: installs the OpenInference Agno instrumentor with an OTLP…
Trace Claude Agent SDK agents (TypeScript and Python) and Claude Code CLI sessions with…
Trace Cloudflare Agents SDK agents (AIChatAgent, Agent on Durable Objects, npm `agents` /…
Trace CrewAI crews and flows with Maple: OpenInference CrewAI instrumentor plus the model-SDK…
Trace DSPy programs and ReAct agents with Maple: OpenInference DSPy instrumentor with GenAI…
Trace Genkit (TypeScript/Node.js) agents with Maple: export Genkit's OpenTelemetry spans to…