@raindrop-ai/eve 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +287 -0
- package/package.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Raindrop AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# `@raindrop-ai/eve`
|
|
2
|
+
|
|
3
|
+
Raindrop integration for [Vercel's Eve agent framework](https://eve.labs.vercel.dev/).
|
|
4
|
+
|
|
5
|
+
Drops into `agent/instrumentation.ts` and ships your Eve agent's OpenTelemetry
|
|
6
|
+
traces to:
|
|
7
|
+
|
|
8
|
+
- **Raindrop** (`https://api.raindrop.ai/v1/traces` by default), and
|
|
9
|
+
- the local **Workshop** daemon (`http://localhost:5899`) — when one is reachable.
|
|
10
|
+
|
|
11
|
+
Both destinations receive the same OTLP/HTTP JSON payload, so an agent that
|
|
12
|
+
works in production also works in `raindrop workshop` without changing any code.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @raindrop-ai/eve @vercel/otel @opentelemetry/api @opentelemetry/sdk-trace-base
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`eve` is expected to already be in your agent project. The integration tracks
|
|
21
|
+
Eve's fast-moving releases and is verified against the current `latest`
|
|
22
|
+
(**0.11.x**), hooking Eve's `defineInstrumentation` surface from
|
|
23
|
+
`eve/instrumentation` — so a single `defineRaindropInstrumentation()` call is all
|
|
24
|
+
you need.
|
|
25
|
+
|
|
26
|
+
How it works:
|
|
27
|
+
|
|
28
|
+
- Eve enables telemetry by the mere presence of an `agent/instrumentation.ts`
|
|
29
|
+
default export. The object returned by `defineRaindropInstrumentation()` is
|
|
30
|
+
that export: Eve reads its `setup`, `events`, `recordInputs`, `recordOutputs`,
|
|
31
|
+
and `functionId` fields and ignores the rest.
|
|
32
|
+
- **Per-step runtime context** is resolved through Eve's `events["step.started"]`
|
|
33
|
+
hook (returns `{ runtimeContext }`); see [Identifying users](#identifying-users).
|
|
34
|
+
- **Cross-sandbox sub-agent linkage** is sourced from the `step.started`
|
|
35
|
+
`session.parent` lineage Eve exposes; sub-agents carry parent linkage with no
|
|
36
|
+
extra wiring.
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
// agent/instrumentation.ts
|
|
42
|
+
import { registerOTel } from "@vercel/otel";
|
|
43
|
+
import { defineRaindropInstrumentation } from "@raindrop-ai/eve";
|
|
44
|
+
|
|
45
|
+
export default defineRaindropInstrumentation({
|
|
46
|
+
registerOTel,
|
|
47
|
+
writeKey: process.env.RAINDROP_WRITE_KEY,
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
That's it. The framework auto-discovers `agent/instrumentation.ts` and runs it
|
|
52
|
+
at server startup before any agent code. Pass `registerOTel` from `@vercel/otel`
|
|
53
|
+
so Eve's own OTel spans (workflow/step/fetch) are exported — without it the
|
|
54
|
+
integration still wires up Raindrop's AI SDK telemetry but skips the underlying
|
|
55
|
+
OTel registration.
|
|
56
|
+
|
|
57
|
+
## Configuration
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
defineRaindropInstrumentation({
|
|
61
|
+
registerOTel, // from @vercel/otel
|
|
62
|
+
writeKey: process.env.RAINDROP_WRITE_KEY,
|
|
63
|
+
endpoint: "https://api.raindrop.ai/v1/", // default
|
|
64
|
+
localWorkshopUrl: undefined, // env + auto-detect by default
|
|
65
|
+
serviceName: "support-agent", // defaults to the Eve agent name
|
|
66
|
+
staticMetadata: { team: "support", tier: "prod" }, // static, every event
|
|
67
|
+
events: { // Eve per-step runtime context
|
|
68
|
+
"step.started"(input) {
|
|
69
|
+
return {
|
|
70
|
+
runtimeContext: {
|
|
71
|
+
"raindrop.userId": String(input.channel.metadata.triggeringUserId),
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
recordInputs: true, // forwarded to Eve
|
|
77
|
+
recordOutputs: true, // forwarded to Eve
|
|
78
|
+
maxTextFieldChars: 1_000_000, // per-attribute cap for exported span text
|
|
79
|
+
debug: false, // or RAINDROP_AI_DEBUG=1
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Payload size limits
|
|
84
|
+
|
|
85
|
+
Exported string span attributes (full prompts/completions recorded by the AI
|
|
86
|
+
SDK under `recordInputs` / `recordOutputs`) are capped at **1,000,000
|
|
87
|
+
characters per value by default** and truncated with a
|
|
88
|
+
`...[truncated by raindrop]` marker (the result, marker included, never
|
|
89
|
+
exceeds the limit). The cap is enforced before the OTLP batch is serialized,
|
|
90
|
+
so multi-MB attributes cost the cap — not the payload — and land truncated
|
|
91
|
+
instead of being dropped at the ingest size limit. Tune it via
|
|
92
|
+
`maxTextFieldChars`; a stricter `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` env
|
|
93
|
+
var is also honored.
|
|
94
|
+
|
|
95
|
+
`staticMetadata` is the static authored lane — keys are merged onto every
|
|
96
|
+
shipped event. `events` is Eve's
|
|
97
|
+
[`InstrumentationEvents`](https://beta.eve.dev/docs) config: its
|
|
98
|
+
`step.started(input)` callback runs once per model call, returns
|
|
99
|
+
`{ runtimeContext }`, and that runtime context is bridged onto the event too
|
|
100
|
+
(see below). A flat `metadata: Record<string,string>` is still accepted and
|
|
101
|
+
treated the same as `staticMetadata` for convenience.
|
|
102
|
+
|
|
103
|
+
### Identifying users
|
|
104
|
+
|
|
105
|
+
**Static identity.** Pass `raindrop.userId` (and optionally `raindrop.convoId`)
|
|
106
|
+
via `staticMetadata` and they are attached to every event from this Eve
|
|
107
|
+
session:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
defineRaindropInstrumentation({
|
|
111
|
+
registerOTel,
|
|
112
|
+
writeKey: process.env.RAINDROP_WRITE_KEY,
|
|
113
|
+
staticMetadata: {
|
|
114
|
+
"raindrop.userId": "user_123",
|
|
115
|
+
"raindrop.convoId": "convo_456",
|
|
116
|
+
team: "support",
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Dynamic per-turn identity.** Use the `events`
|
|
122
|
+
[`step.started`](https://beta.eve.dev/docs)
|
|
123
|
+
callback. Eve invokes it once per model call with the live
|
|
124
|
+
channel/session/turn/step `input`, so you can derive identity from the
|
|
125
|
+
triggering request — e.g. map the Slack user id onto the Raindrop event
|
|
126
|
+
`userId`:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
defineRaindropInstrumentation({
|
|
130
|
+
registerOTel,
|
|
131
|
+
writeKey: process.env.RAINDROP_WRITE_KEY,
|
|
132
|
+
staticMetadata: { app: "demo-data-agent", team: "data" },
|
|
133
|
+
events: {
|
|
134
|
+
"step.started"(input) {
|
|
135
|
+
const m = input.channel.metadata;
|
|
136
|
+
return {
|
|
137
|
+
runtimeContext: {
|
|
138
|
+
// reserved keys -> Raindrop event identity
|
|
139
|
+
"raindrop.userId": m.triggeringUserId, // becomes the event userId
|
|
140
|
+
"raindrop.convoId": m.threadTs, // becomes the event convoId
|
|
141
|
+
// any other key -> Raindrop event properties
|
|
142
|
+
"slack.user_id": m.triggeringUserId,
|
|
143
|
+
"slack.channel_id": m.channelId,
|
|
144
|
+
"slack.team_id": m.teamId,
|
|
145
|
+
"slack.thread_ts": m.threadTs,
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The callback's output is both attached to the AI SDK telemetry spans (as
|
|
154
|
+
`ai.settings.context.*`) and routed onto the shipped Raindrop event:
|
|
155
|
+
|
|
156
|
+
- reserved `raindrop.*` control keys set the event identity. A per-step
|
|
157
|
+
`raindrop.userId` (e.g. the Slack user id) wins over the Eve `session.id`
|
|
158
|
+
for the event's `userId`; `raindrop.convoId` sets the `convoId`.
|
|
159
|
+
- every other key becomes an entry under event `properties`.
|
|
160
|
+
|
|
161
|
+
Precedence for any given key (highest first): per-call metadata on the AI SDK
|
|
162
|
+
call itself > the `step.started` callback > `staticMetadata`. So a per-step key
|
|
163
|
+
always overrides the static default of the same name. When the callback omits
|
|
164
|
+
`raindrop.userId`, the integration falls back to the Eve `session.id`, then to
|
|
165
|
+
`opts.userId ?? agentName`.
|
|
166
|
+
|
|
167
|
+
#### Using the Slack user's name / email / channel name
|
|
168
|
+
|
|
169
|
+
Whatever string you return as `raindrop.userId` becomes the event's `userId` —
|
|
170
|
+
so to make Raindrop track e.g. the Slack email instead of the user id, just
|
|
171
|
+
return that string. What's reachable from `step.started`:
|
|
172
|
+
|
|
173
|
+
- **User id** (`Uxxxx`) — directly on `input.channel.metadata.triggeringUserId`.
|
|
174
|
+
- **User's name** — already resolved, no API call: read
|
|
175
|
+
`input.session.auth.current?.attributes` and use `full_name` / `user_name`
|
|
176
|
+
(Eve's default Slack auth puts them there).
|
|
177
|
+
- **Email / channel name** — *not* in any Slack mention payload, so resolve them
|
|
178
|
+
once on the async inbound side (`onAppMention`) via the Slack Web API
|
|
179
|
+
(`users.info` → `user.profile.email`, needs the `users:read.email` scope;
|
|
180
|
+
`conversations.info` → `channel.name`), stash them on the session auth
|
|
181
|
+
`attributes`, then read them back in `step.started` (which is synchronous and
|
|
182
|
+
can't call Slack itself):
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
// agent/channels/slack.ts — runs on the inbound webhook (async ok)
|
|
186
|
+
export default slackChannel({
|
|
187
|
+
async onAppMention(ctx, message) {
|
|
188
|
+
const auth = defaultSlackAuth(message, ctx);
|
|
189
|
+
const who = await ctx.slack.request("users.info", { user: message.author.userId });
|
|
190
|
+
return auth && {
|
|
191
|
+
auth: { ...auth, attributes: { ...auth.attributes, email: who?.user?.profile?.email ?? "" } },
|
|
192
|
+
};
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// instrumentation.ts — synchronous, just reads what you stashed
|
|
197
|
+
"step.started"(input) {
|
|
198
|
+
const a = input.session.auth.current?.attributes ?? {};
|
|
199
|
+
return {
|
|
200
|
+
runtimeContext: { "raindrop.userId": String(a.email || a.user_id || "") },
|
|
201
|
+
}; // userId will be the email
|
|
202
|
+
},
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Workshop / production mirroring
|
|
206
|
+
|
|
207
|
+
`localWorkshopUrl` controls Workshop mirroring:
|
|
208
|
+
|
|
209
|
+
| Value | Behavior |
|
|
210
|
+
| -------------- | --------------------------------------------------------------------- |
|
|
211
|
+
| `string` | Force-enable; mirror every export to that URL. |
|
|
212
|
+
| `false` | Opt out entirely (skip env vars and auto-detect). |
|
|
213
|
+
| `undefined` | Honor `RAINDROP_WORKSHOP` / `RAINDROP_LOCAL_DEBUGGER`, then auto-detect localhost during development. |
|
|
214
|
+
|
|
215
|
+
Run `raindrop workshop` locally to get a daemon at `http://localhost:5899` —
|
|
216
|
+
the integration will start mirroring as soon as the daemon is reachable.
|
|
217
|
+
|
|
218
|
+
### Production-only mode
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
defineRaindropInstrumentation({
|
|
222
|
+
registerOTel,
|
|
223
|
+
writeKey: process.env.RAINDROP_WRITE_KEY,
|
|
224
|
+
localWorkshopUrl: false,
|
|
225
|
+
});
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Workshop-only mode (no Raindrop account)
|
|
229
|
+
|
|
230
|
+
Leave `writeKey` undefined. Spans still flow to Workshop:
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
defineRaindropInstrumentation({
|
|
234
|
+
registerOTel,
|
|
235
|
+
localWorkshopUrl: "http://localhost:5899/v1/",
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Sub-agents
|
|
240
|
+
|
|
241
|
+
Eve runs each sub-agent in its own V8 sandbox. The integration reads the
|
|
242
|
+
`session.parent` lineage Eve exposes on the `events["step.started"]` input to
|
|
243
|
+
detect when the current sandbox was dispatched as a sub-agent, and lifts the
|
|
244
|
+
parent's turn identity onto every sub-agent event:
|
|
245
|
+
|
|
246
|
+
| Attribute | Description |
|
|
247
|
+
| ------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
248
|
+
| `raindrop.agent.role` | `"subagent"` when the current sandbox is a sub-agent dispatched by another agent; `"root"` otherwise. |
|
|
249
|
+
| `raindrop.subagent.name` | The sub-agent's `agentName` (e.g. `weatherResearcher`). |
|
|
250
|
+
| `raindrop.parent.sessionId` | The parent agent's (bare) Eve session id. |
|
|
251
|
+
| `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`. |
|
|
252
|
+
| `raindrop.parent.turnId` | The parent's turn id that dispatched this sub-agent. |
|
|
253
|
+
| `raindrop.parent.turnSequence` | The parent's turn sequence number. |
|
|
254
|
+
|
|
255
|
+
These power the **AGENT block** in Workshop's Overview tab and let the Raindrop
|
|
256
|
+
dashboard stitch sub-agent events under the parent turn that dispatched them
|
|
257
|
+
(matching on `raindrop.parent.eventId`).
|
|
258
|
+
|
|
259
|
+
When Eve exposes no `session.parent` (a root session), events ship as root
|
|
260
|
+
events in the feed with `raindrop.agent.role` = `"root"`.
|
|
261
|
+
|
|
262
|
+
## What gets traced
|
|
263
|
+
|
|
264
|
+
Eve automatically creates rich trace hierarchies per turn:
|
|
265
|
+
|
|
266
|
+
```
|
|
267
|
+
eve.turn // session/turn metadata
|
|
268
|
+
+-- ai.streamText // step
|
|
269
|
+
| +-- ai.streamText.doStream // model call
|
|
270
|
+
| +-- ai.toolCall { toolName: search } // tool exec
|
|
271
|
+
+-- ai.streamText
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
These flow through Raindrop's standard ingestion and show up in both the
|
|
275
|
+
Raindrop UI and Workshop with full nesting + AI SDK attributes.
|
|
276
|
+
|
|
277
|
+
## Notes
|
|
278
|
+
|
|
279
|
+
- This package depends on `@raindrop-ai/core` for OTLP serialization and the
|
|
280
|
+
Workshop mirroring helper.
|
|
281
|
+
- The exporter implements the `SpanExporter` interface structurally, so it
|
|
282
|
+
works against both `@opentelemetry/sdk-trace-base` 1.x and 2.x.
|
|
283
|
+
|
|
284
|
+
## Examples
|
|
285
|
+
|
|
286
|
+
- [`examples/eve-basic`](../../examples/eve-basic) — single-agent example with one tool (`get_weather`) and an end-to-end dashboard verifier.
|
|
287
|
+
- [`examples/eve-subagents`](../../examples/eve-subagents) — parent agent dispatching `weatherResearcher` + `attractionsFinder` sub-agents, verifying cross-sandbox nesting against the production dashboard.
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@raindrop-ai/eve",
|
|
3
|
+
"version": "0.0.10",
|
|
4
|
+
"description": "Raindrop integration for Vercel's Eve agent framework (OTel traces to Raindrop + Workshop)",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/raindrop-ai/raindrop-js.git",
|
|
12
|
+
"directory": "packages/eve"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/raindrop-ai/raindrop-js/tree/main/packages/eve#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/raindrop-ai/raindrop-js/issues"
|
|
17
|
+
},
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.mjs",
|
|
22
|
+
"require": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"files": [
|
|
27
|
+
"dist/**"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@raindrop-ai/ai-sdk": "0.0.36"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@opentelemetry/api": ">=1.9.0",
|
|
34
|
+
"@opentelemetry/sdk-trace-base": ">=1.30.0 || >=2.0.0",
|
|
35
|
+
"@vercel/otel": ">=1.10.0",
|
|
36
|
+
"ai": ">=7.0.0-canary.0 <8",
|
|
37
|
+
"eve": ">=0.11.0"
|
|
38
|
+
},
|
|
39
|
+
"peerDependenciesMeta": {
|
|
40
|
+
"ai": {
|
|
41
|
+
"optional": true
|
|
42
|
+
},
|
|
43
|
+
"eve": {
|
|
44
|
+
"optional": true
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@opentelemetry/api": "^1.9.0",
|
|
49
|
+
"@opentelemetry/sdk-trace-base": "^2.0.0",
|
|
50
|
+
"@types/node": "^20.11.17",
|
|
51
|
+
"@vercel/otel": "^2.1.2",
|
|
52
|
+
"tsup": "^8.4.0",
|
|
53
|
+
"typescript": "^5.3.3",
|
|
54
|
+
"vitest": "^2.1.9",
|
|
55
|
+
"@raindrop-ai/core": "0.0.4"
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public",
|
|
59
|
+
"provenance": false
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "tsup",
|
|
63
|
+
"dev": "tsup --watch",
|
|
64
|
+
"clean": "rm -rf .turbo && rm -rf dist",
|
|
65
|
+
"test": "vitest run"
|
|
66
|
+
}
|
|
67
|
+
}
|