npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@raindrop-ai/eve

v0.0.19

Published

Raindrop integration for Vercel's Eve agent framework (OTel traces to Raindrop + Workshop)

Readme

@raindrop-ai/eve

Raindrop integration for Vercel's Eve agent framework.

Drops into agent/instrumentation.ts and ships your Eve agent's OpenTelemetry traces to:

  • Raindrop (https://api.raindrop.ai/v1/traces by default), and
  • the local Workshop daemon (http://localhost:5899) — when one is reachable.

Both destinations receive the same OTLP/HTTP JSON payload, so an agent that works in production also works in raindrop workshop without changing any code.

Install

pnpm add @raindrop-ai/eve @vercel/otel @opentelemetry/api @opentelemetry/sdk-trace-base

eve is expected to already be in your agent project. The integration tracks Eve's fast-moving releases and is verified against the current latest (0.11.x), hooking Eve's defineInstrumentation surface from eve/instrumentation — so a single defineRaindropInstrumentation() call is all you need.

How it works:

  • Eve enables telemetry by the mere presence of an agent/instrumentation.ts default export. The object returned by defineRaindropInstrumentation() is that export: Eve reads its setup, events, recordInputs, recordOutputs, and functionId fields and ignores the rest.
  • Per-step runtime context is resolved through Eve's events["step.started"] hook (returns { runtimeContext }); see Identifying users.
  • Cross-sandbox sub-agent linkage is sourced from the step.started session.parent lineage Eve exposes; sub-agents carry parent linkage with no extra wiring.

Usage

// agent/instrumentation.ts
import { registerOTel } from "@vercel/otel";
import { defineRaindropInstrumentation } from "@raindrop-ai/eve";

export default defineRaindropInstrumentation({
  registerOTel,
  writeKey: process.env.RAINDROP_WRITE_KEY,
});

That's it. The framework auto-discovers agent/instrumentation.ts and runs it at server startup before any agent code. Pass registerOTel from @vercel/otel so Eve's own OTel spans (workflow/step/fetch) are exported — without it the integration still wires up Raindrop's AI SDK telemetry but skips the underlying OTel registration.

Configuration

defineRaindropInstrumentation({
  registerOTel,                             // from @vercel/otel
  writeKey: process.env.RAINDROP_WRITE_KEY,
  endpoint: "https://api.raindrop.ai/v1/", // default
  projectId: "support-prod",                // route to a named project; omit for default Production
  localWorkshopUrl: undefined,              // env + auto-detect by default
  serviceName: "support-agent",             // defaults to the Eve agent name
  staticMetadata: { team: "support", tier: "prod" }, // static, every event
  events: {                                 // Eve per-step runtime context
    "step.started"(input) {
      return {
        runtimeContext: {
          "raindrop.userId": String(input.channel.metadata.triggeringUserId),
        },
      };
    },
  },
  recordInputs: true,                       // forwarded to Eve
  recordOutputs: true,                      // forwarded to Eve
  maxTextFieldChars: 1_000_000,             // per-attribute cap for exported span text
  debug: false,                             // or RAINDROP_AI_DEBUG=1
});

Projects

If your org has multiple projects, route telemetry to a specific one by passing its slug as projectId (or set RAINDROP_PROJECT_ID):

defineRaindropInstrumentation({
  registerOTel,
  writeKey: process.env.RAINDROP_WRITE_KEY,
  projectId: "support-prod",
});

This sets the X-Raindrop-Project-Id header on every trace export and AI SDK event. Omit it (or pass "default") to use your org's default Production project — the existing behavior. Single-project orgs need nothing new.

Payload size limits

Exported string span attributes (full prompts/completions recorded by the AI SDK under recordInputs / recordOutputs) are capped at 1,000,000 characters per value by default and truncated with a ...[truncated by raindrop] marker (the result, marker included, never exceeds the limit). The cap is enforced before the OTLP batch is serialized, so multi-MB attributes cost the cap — not the payload — and land truncated instead of being dropped at the ingest size limit. Tune it via maxTextFieldChars; a stricter OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT env var is also honored.

staticMetadata is the static authored lane — keys are merged onto every shipped event. events is Eve's InstrumentationEvents config: its step.started(input) callback runs once per model call, returns { runtimeContext }, and that runtime context is bridged onto the event too (see below). A flat metadata: Record<string,string> is still accepted and treated the same as staticMetadata for convenience.

Identifying users

Static identity. Pass raindrop.userId (and optionally raindrop.convoId) via staticMetadata and they are attached to every event from this Eve session:

defineRaindropInstrumentation({
  registerOTel,
  writeKey: process.env.RAINDROP_WRITE_KEY,
  staticMetadata: {
    "raindrop.userId": "user_123",
    "raindrop.convoId": "convo_456",
    team: "support",
  },
});

Dynamic per-turn identity. Use the events step.started callback. Eve invokes it once per model call with the live channel/session/turn/step input, so you can derive identity from the triggering request — e.g. map the Slack user id onto the Raindrop event userId:

defineRaindropInstrumentation({
  registerOTel,
  writeKey: process.env.RAINDROP_WRITE_KEY,
  staticMetadata: { app: "demo-data-agent", team: "data" },
  events: {
    "step.started"(input) {
      const m = input.channel.metadata;
      return {
        runtimeContext: {
          // reserved keys -> Raindrop event identity
          "raindrop.userId": m.triggeringUserId, // becomes the event userId
          "raindrop.convoId": m.threadTs,        // becomes the event convoId
          // any other key -> Raindrop event properties
          "slack.user_id": m.triggeringUserId,
          "slack.channel_id": m.channelId,
          "slack.team_id": m.teamId,
          "slack.thread_ts": m.threadTs,
        },
      };
    },
  },
});

The callback's output is both attached to the AI SDK telemetry spans (as ai.settings.context.*) and routed onto the shipped Raindrop event:

  • reserved raindrop.* control keys set the event identity. A per-step raindrop.userId (e.g. the Slack user id) wins over the Eve session.id for the event's userId; raindrop.convoId sets the convoId.
  • every other key becomes an entry under event properties.

Precedence for any given key (highest first): per-call metadata on the AI SDK call itself > the step.started callback > staticMetadata. So a per-step key always overrides the static default of the same name. When the callback omits raindrop.userId, the integration falls back to the Eve session.id, then to opts.userId ?? agentName.

Using the Slack user's name / email / channel name

Whatever string you return as raindrop.userId becomes the event's userId — so to make Raindrop track e.g. the Slack email instead of the user id, just return that string. What's reachable from step.started:

  • User id (Uxxxx) — directly on input.channel.metadata.triggeringUserId.
  • User's name — already resolved, no API call: read input.session.auth.current?.attributes and use full_name / user_name (Eve's default Slack auth puts them there).
  • Email / channel namenot in any Slack mention payload, so resolve them once on the async inbound side (onAppMention) via the Slack Web API (users.infouser.profile.email, needs the users:read.email scope; conversations.infochannel.name), stash them on the session auth attributes, then read them back in step.started (which is synchronous and can't call Slack itself):
// agent/channels/slack.ts — runs on the inbound webhook (async ok)
export default slackChannel({
  async onAppMention(ctx, message) {
    const auth = defaultSlackAuth(message, ctx);
    const who = await ctx.slack.request("users.info", { user: message.author.userId });
    return auth && {
      auth: { ...auth, attributes: { ...auth.attributes, email: who?.user?.profile?.email ?? "" } },
    };
  },
});

// instrumentation.ts — synchronous, just reads what you stashed
"step.started"(input) {
  const a = input.session.auth.current?.attributes ?? {};
  return {
    runtimeContext: { "raindrop.userId": String(a.email || a.user_id || "") },
  }; // userId will be the email
},

Workshop / production mirroring

localWorkshopUrl controls Workshop mirroring:

| Value | Behavior | | -------------- | --------------------------------------------------------------------- | | string | Force-enable; mirror every export to that URL. | | false | Opt out entirely (skip env vars and auto-detect). | | undefined | Honor RAINDROP_WORKSHOP / RAINDROP_LOCAL_DEBUGGER, then auto-detect localhost during development. |

Run raindrop workshop locally to get a daemon at http://localhost:5899 — the integration will start mirroring as soon as the daemon is reachable.

Production-only mode

defineRaindropInstrumentation({
  registerOTel,
  writeKey: process.env.RAINDROP_WRITE_KEY,
  localWorkshopUrl: false,
});

Workshop-only mode (no Raindrop account)

Leave writeKey undefined. Spans still flow to Workshop:

defineRaindropInstrumentation({
  registerOTel,
  localWorkshopUrl: "http://localhost:5899/v1/",
});

Sub-agents

Eve runs each sub-agent in its own V8 sandbox. The integration reads the session.parent lineage Eve exposes on the events["step.started"] input to detect when the current sandbox was dispatched as a sub-agent, and lifts the parent's turn identity onto every sub-agent event:

| Attribute | Description | | ------------------------------- | ------------------------------------------------------------------------------------------ | | raindrop.agent.role | "subagent" when the current sandbox is a sub-agent dispatched by another agent; "root" otherwise. | | raindrop.subagent.name | The sub-agent's agentName (e.g. weatherResearcher). | | raindrop.parent.sessionId | The parent agent's (bare) Eve session id. | | raindrop.parent.eventId | The cross-sandbox link key. The dispatching parent turn's per-turn raindrop.eventId (the eve:<sessionId>:<turnId> composite); the dashboard stitches a sub-agent under its parent by matching this against that turn's own raindrop.eventId. | | raindrop.parent.turnId | The parent's turn id that dispatched this sub-agent. | | raindrop.parent.turnSequence | The parent's turn sequence number. |

These power the AGENT block in Workshop's Overview tab and let the Raindrop dashboard stitch sub-agent events under the parent turn that dispatched them (matching on raindrop.parent.eventId).

When Eve exposes no session.parent (a root session), events ship as root events in the feed with raindrop.agent.role = "root".

What gets traced

Eve automatically creates rich trace hierarchies per turn:

eve.turn                                      // session/turn metadata
  +-- ai.streamText                           // step
  |     +-- ai.streamText.doStream            // model call
  |     +-- ai.toolCall  { toolName: search } // tool exec
  +-- ai.streamText

These flow through Raindrop's standard ingestion and show up in both the Raindrop UI and Workshop with full nesting + AI SDK attributes.

Notes

  • This package depends on @raindrop-ai/core for OTLP serialization and the Workshop mirroring helper.
  • The exporter implements the SpanExporter interface structurally, so it works against both @opentelemetry/sdk-trace-base 1.x and 2.x.

Examples

  • examples/eve-basic — single-agent example with one tool (get_weather) and an end-to-end dashboard verifier.
  • examples/eve-subagents — parent agent dispatching weatherResearcher + attractionsFinder sub-agents, verifying cross-sandbox nesting against the production dashboard.