@raindrop-ai/eve 0.0.10 → 0.0.11
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/dist/index.d.mts +455 -0
- package/dist/index.d.ts +455 -0
- package/dist/index.js +1089 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1079 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +1 -1
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1079 @@
|
|
|
1
|
+
import { trace } from '@opentelemetry/api';
|
|
2
|
+
import { createRaindropAISDK, getCurrentParentToolContext } from '@raindrop-ai/ai-sdk';
|
|
3
|
+
import { createRequire } from 'module';
|
|
4
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
5
|
+
|
|
6
|
+
// src/index.ts
|
|
7
|
+
function parseEveSessionParent(value) {
|
|
8
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
9
|
+
const p = value;
|
|
10
|
+
if (typeof p.sessionId !== "string") return void 0;
|
|
11
|
+
if (typeof p.turn !== "object" || p.turn === null) return void 0;
|
|
12
|
+
const t = p.turn;
|
|
13
|
+
if (typeof t.id !== "string" || typeof t.sequence !== "number") return void 0;
|
|
14
|
+
const parent = {
|
|
15
|
+
sessionId: p.sessionId,
|
|
16
|
+
turn: { id: t.id, sequence: t.sequence },
|
|
17
|
+
...typeof p.rootSessionId === "string" ? { rootSessionId: p.rootSessionId } : {}
|
|
18
|
+
};
|
|
19
|
+
return parent;
|
|
20
|
+
}
|
|
21
|
+
var cached = void 0;
|
|
22
|
+
function loadGetSession() {
|
|
23
|
+
if (cached !== void 0) return cached;
|
|
24
|
+
try {
|
|
25
|
+
const requireFromHere = createRequire(
|
|
26
|
+
// `import.meta.url` is the ESM spelling; tsup polyfills it for CJS builds.
|
|
27
|
+
typeof __filename !== "undefined" ? __filename : import.meta.url
|
|
28
|
+
);
|
|
29
|
+
const mod = requireFromHere("eve/context");
|
|
30
|
+
cached = typeof mod.getSession === "function" ? mod.getSession : null;
|
|
31
|
+
} catch {
|
|
32
|
+
cached = null;
|
|
33
|
+
}
|
|
34
|
+
return cached;
|
|
35
|
+
}
|
|
36
|
+
function isEveSessionShape(value) {
|
|
37
|
+
if (typeof value !== "object" || value === null) return false;
|
|
38
|
+
const v = value;
|
|
39
|
+
if (typeof v.sessionId !== "string") return false;
|
|
40
|
+
if (v.turn !== void 0) {
|
|
41
|
+
if (typeof v.turn !== "object" || v.turn === null) return false;
|
|
42
|
+
const t = v.turn;
|
|
43
|
+
if (typeof t.id !== "string" || typeof t.sequence !== "number") return false;
|
|
44
|
+
}
|
|
45
|
+
if (v.parent === void 0) return true;
|
|
46
|
+
return parseEveSessionParent(v.parent) !== void 0;
|
|
47
|
+
}
|
|
48
|
+
function getEveSessionLineage() {
|
|
49
|
+
const getSession = loadGetSession();
|
|
50
|
+
if (!getSession) return void 0;
|
|
51
|
+
let raw;
|
|
52
|
+
try {
|
|
53
|
+
raw = getSession();
|
|
54
|
+
} catch {
|
|
55
|
+
return void 0;
|
|
56
|
+
}
|
|
57
|
+
return isEveSessionShape(raw) ? raw : void 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/internal/eve-turn-registry.ts
|
|
61
|
+
var EVE_TURN_SPAN_NAME = "ai.eve.turn";
|
|
62
|
+
var EVE_CONTINUATION_TOKEN_ATTR = "eve.continuation_token";
|
|
63
|
+
var EVE_SESSION_ID_ATTR = "eve.session.id";
|
|
64
|
+
var EveTurnRegistry = class {
|
|
65
|
+
constructor() {
|
|
66
|
+
/**
|
|
67
|
+
* Per-trace stack of in-flight `ai.eve.turn` entries.
|
|
68
|
+
*
|
|
69
|
+
* The stack lets us handle multiple concurrent turns on the same
|
|
70
|
+
* `traceId` correctly — both the nested case (turn-within-turn,
|
|
71
|
+
* theoretically possible if Eve ever supports tool-calls-as-turns)
|
|
72
|
+
* and the parallel case (two `ai.eve.turn` spans opened on a shared
|
|
73
|
+
* trace via `Promise.all`, which Workshop's in-process executor can
|
|
74
|
+
* do when running sibling sub-agents under one sandbox).
|
|
75
|
+
*
|
|
76
|
+
* Lookup returns the top of the stack, which is the most-recently
|
|
77
|
+
* opened still-active turn. That's the right answer for both cases:
|
|
78
|
+
* - Nested: the inner turn is the descendant of the active span
|
|
79
|
+
* that fires `ai.streamText.onStart`.
|
|
80
|
+
* - Parallel: tie-broken in favour of the most-recently-started
|
|
81
|
+
* entry. Public OTel APIs don't expose which `ai.eve.turn` is
|
|
82
|
+
* the ancestor of the active `step.execute`, so this is the
|
|
83
|
+
* best we can do without walking parent spans.
|
|
84
|
+
*
|
|
85
|
+
* Empty stacks are deleted from the map so the registry stays small.
|
|
86
|
+
*/
|
|
87
|
+
this.stacks = /* @__PURE__ */ new Map();
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Record a turn's attributes. Called from the SpanProcessor's
|
|
91
|
+
* `onStart` callback whenever an `ai.eve.turn` span is created.
|
|
92
|
+
* Push onto the per-trace stack — see {@link stacks}.
|
|
93
|
+
*/
|
|
94
|
+
record(traceId, spanId, entry) {
|
|
95
|
+
if (!traceId || !spanId) return;
|
|
96
|
+
let stack = this.stacks.get(traceId);
|
|
97
|
+
if (!stack) {
|
|
98
|
+
stack = [];
|
|
99
|
+
this.stacks.set(traceId, stack);
|
|
100
|
+
}
|
|
101
|
+
stack.push({ ...entry, spanId });
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Look up the currently-active turn for a trace. Returns `undefined`
|
|
105
|
+
* if no `ai.eve.turn` span has been seen for this trace — for example
|
|
106
|
+
* when running outside Eve, or before the turn span has fired
|
|
107
|
+
* `onStart` (shouldn't happen because Eve opens the turn span before
|
|
108
|
+
* entering its context, but treated defensively).
|
|
109
|
+
*/
|
|
110
|
+
lookup(traceId) {
|
|
111
|
+
if (!traceId) return void 0;
|
|
112
|
+
const stack = this.stacks.get(traceId);
|
|
113
|
+
if (!stack || stack.length === 0) return void 0;
|
|
114
|
+
return stack[stack.length - 1];
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Release the entry for a specific `(traceId, spanId)`. Called from
|
|
118
|
+
* the SpanProcessor's `onEnd` callback. We schedule the removal on a
|
|
119
|
+
* microtask so any in-flight `ai.streamText` `onStart` (which can
|
|
120
|
+
* fire *after* `ai.eve.turn` ends if Eve's tool-loop is
|
|
121
|
+
* mid-finalization) still finds the entry. The grace window is short
|
|
122
|
+
* — we just need to outlive the synchronous AI SDK fan-out, not
|
|
123
|
+
* minutes of idle time.
|
|
124
|
+
*
|
|
125
|
+
* Targets the exact `spanId` rather than popping the top of the
|
|
126
|
+
* stack so that interleaved parallel turns release their own entries
|
|
127
|
+
* regardless of end order.
|
|
128
|
+
*/
|
|
129
|
+
releaseAfterTick(traceId, spanId) {
|
|
130
|
+
if (!traceId || !spanId) return;
|
|
131
|
+
Promise.resolve().then(() => {
|
|
132
|
+
const stack = this.stacks.get(traceId);
|
|
133
|
+
if (!stack) return;
|
|
134
|
+
const idx = stack.findIndex((e) => e.spanId === spanId);
|
|
135
|
+
if (idx !== -1) {
|
|
136
|
+
stack.splice(idx, 1);
|
|
137
|
+
}
|
|
138
|
+
if (stack.length === 0) {
|
|
139
|
+
this.stacks.delete(traceId);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Drop all entries. Called from the SpanProcessor's `shutdown` hook
|
|
145
|
+
* during exporter teardown.
|
|
146
|
+
*/
|
|
147
|
+
clear() {
|
|
148
|
+
this.stacks.clear();
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Total number of in-flight entries across all traces. For tests +
|
|
152
|
+
* diagnostic logging.
|
|
153
|
+
*/
|
|
154
|
+
get size() {
|
|
155
|
+
let total = 0;
|
|
156
|
+
for (const stack of this.stacks.values()) total += stack.length;
|
|
157
|
+
return total;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
var registry = new EveTurnRegistry();
|
|
161
|
+
function createEveTurnSpanProcessor() {
|
|
162
|
+
return {
|
|
163
|
+
onStart(span) {
|
|
164
|
+
if (span.name !== EVE_TURN_SPAN_NAME) return;
|
|
165
|
+
const attrs = span.attributes;
|
|
166
|
+
if (!attrs) return;
|
|
167
|
+
const ctx = span.spanContext();
|
|
168
|
+
const continuationToken = typeof attrs[EVE_CONTINUATION_TOKEN_ATTR] === "string" ? attrs[EVE_CONTINUATION_TOKEN_ATTR] : void 0;
|
|
169
|
+
const sessionId = typeof attrs[EVE_SESSION_ID_ATTR] === "string" ? attrs[EVE_SESSION_ID_ATTR] : void 0;
|
|
170
|
+
registry.record(ctx.traceId, ctx.spanId, {
|
|
171
|
+
continuationToken,
|
|
172
|
+
sessionId
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
onEnd(span) {
|
|
176
|
+
if (span.name !== EVE_TURN_SPAN_NAME) return;
|
|
177
|
+
const ctx = span.spanContext();
|
|
178
|
+
registry.releaseAfterTick(ctx.traceId, ctx.spanId);
|
|
179
|
+
},
|
|
180
|
+
forceFlush() {
|
|
181
|
+
return Promise.resolve();
|
|
182
|
+
},
|
|
183
|
+
shutdown() {
|
|
184
|
+
registry.clear();
|
|
185
|
+
return Promise.resolve();
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function lookupEveTurn(traceId) {
|
|
190
|
+
return registry.lookup(traceId);
|
|
191
|
+
}
|
|
192
|
+
function installEveTurnSpanProcessor(traceApi) {
|
|
193
|
+
if (installState.installed) return true;
|
|
194
|
+
try {
|
|
195
|
+
let provider = traceApi.getTracerProvider();
|
|
196
|
+
const proxyProvider = provider;
|
|
197
|
+
if (typeof proxyProvider.getDelegate === "function") {
|
|
198
|
+
const delegate = proxyProvider.getDelegate();
|
|
199
|
+
if (delegate) provider = delegate;
|
|
200
|
+
}
|
|
201
|
+
const tp = provider;
|
|
202
|
+
if (typeof tp.addSpanProcessor !== "function") {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
tp.addSpanProcessor(createEveTurnSpanProcessor());
|
|
206
|
+
installState.installed = true;
|
|
207
|
+
return true;
|
|
208
|
+
} catch {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
var installState = { installed: false };
|
|
213
|
+
|
|
214
|
+
// ../core/dist/chunk-SK6EJEO7.js
|
|
215
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
216
|
+
var MAX_RETRY_DELAY_MS = 3e4;
|
|
217
|
+
function wait(ms) {
|
|
218
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
219
|
+
}
|
|
220
|
+
function formatEndpoint(endpoint) {
|
|
221
|
+
if (!endpoint) return void 0;
|
|
222
|
+
return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
|
|
223
|
+
}
|
|
224
|
+
function redactUrlForLog(url) {
|
|
225
|
+
try {
|
|
226
|
+
const parsed = new URL(url);
|
|
227
|
+
parsed.username = "";
|
|
228
|
+
parsed.password = "";
|
|
229
|
+
parsed.search = "";
|
|
230
|
+
parsed.hash = "";
|
|
231
|
+
return parsed.toString();
|
|
232
|
+
} catch (e) {
|
|
233
|
+
return "<unparseable-url>";
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function parseRetryAfter(headers) {
|
|
237
|
+
var _a;
|
|
238
|
+
const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
|
|
239
|
+
if (!value) return void 0;
|
|
240
|
+
const asNumber = Number(value);
|
|
241
|
+
if (value.trim() !== "" && !Number.isNaN(asNumber)) return asNumber * 1e3;
|
|
242
|
+
const asDate = new Date(value).getTime();
|
|
243
|
+
if (!Number.isNaN(asDate)) {
|
|
244
|
+
const delta = asDate - Date.now();
|
|
245
|
+
return delta > 0 ? delta : 0;
|
|
246
|
+
}
|
|
247
|
+
return void 0;
|
|
248
|
+
}
|
|
249
|
+
function getRetryDelayMs(attemptNumber, previousError) {
|
|
250
|
+
if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
|
|
251
|
+
const v = previousError.retryAfterMs;
|
|
252
|
+
if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
|
|
253
|
+
}
|
|
254
|
+
if (attemptNumber <= 1) return 0;
|
|
255
|
+
const base = 500;
|
|
256
|
+
const factor = Math.pow(2, attemptNumber - 2);
|
|
257
|
+
return Math.min(base * factor, MAX_RETRY_DELAY_MS);
|
|
258
|
+
}
|
|
259
|
+
async function withRetry(operation, opName, opts) {
|
|
260
|
+
const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
|
|
261
|
+
let lastError = void 0;
|
|
262
|
+
for (let attemptNumber = 1; attemptNumber <= opts.maxAttempts; attemptNumber++) {
|
|
263
|
+
if (attemptNumber > 1) {
|
|
264
|
+
const delay = getRetryDelayMs(attemptNumber, lastError);
|
|
265
|
+
if (opts.debug) {
|
|
266
|
+
console.warn(
|
|
267
|
+
`${prefix} ${opName} retry ${attemptNumber}/${opts.maxAttempts} in ${delay}ms`
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
if (delay > 0) await wait(delay);
|
|
271
|
+
} else if (opts.debug) {
|
|
272
|
+
console.log(`${prefix} ${opName} attempt ${attemptNumber}/${opts.maxAttempts}`);
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
return await operation();
|
|
276
|
+
} catch (err) {
|
|
277
|
+
lastError = err;
|
|
278
|
+
if (opts.debug) {
|
|
279
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
280
|
+
console.warn(
|
|
281
|
+
`${prefix} ${opName} attempt ${attemptNumber} failed: ${msg}${attemptNumber === opts.maxAttempts ? " (no more retries)" : ""}`
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
if (lastError && typeof lastError === "object" && "retryable" in lastError && !lastError.retryable)
|
|
285
|
+
break;
|
|
286
|
+
if (attemptNumber === opts.maxAttempts) break;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
290
|
+
}
|
|
291
|
+
async function postJson(url, body, headers, opts) {
|
|
292
|
+
var _a;
|
|
293
|
+
const opName = `POST ${redactUrlForLog(url)}`;
|
|
294
|
+
const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
|
|
295
|
+
await withRetry(
|
|
296
|
+
async () => {
|
|
297
|
+
const resp = await fetch(url, {
|
|
298
|
+
method: "POST",
|
|
299
|
+
headers: {
|
|
300
|
+
"Content-Type": "application/json",
|
|
301
|
+
...headers
|
|
302
|
+
},
|
|
303
|
+
body: JSON.stringify(body),
|
|
304
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
305
|
+
});
|
|
306
|
+
if (!resp.ok) {
|
|
307
|
+
const text = await resp.text().catch(() => "");
|
|
308
|
+
const err = new Error(
|
|
309
|
+
`HTTP ${resp.status} ${resp.statusText}${text ? `: ${text}` : ""}`
|
|
310
|
+
);
|
|
311
|
+
const retryAfterMs = parseRetryAfter(resp.headers);
|
|
312
|
+
if (typeof retryAfterMs === "number") err.retryAfterMs = retryAfterMs;
|
|
313
|
+
err.retryable = resp.status === 429 || resp.status >= 500;
|
|
314
|
+
throw err;
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
opName,
|
|
318
|
+
opts
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
var SpanStatusCode = {
|
|
322
|
+
UNSET: 0,
|
|
323
|
+
OK: 1,
|
|
324
|
+
ERROR: 2
|
|
325
|
+
};
|
|
326
|
+
function buildExportTraceServiceRequest(spans, serviceName = "raindrop.core", serviceVersion = "0.0.0") {
|
|
327
|
+
return {
|
|
328
|
+
resourceSpans: [
|
|
329
|
+
{
|
|
330
|
+
resource: {
|
|
331
|
+
attributes: [{ key: "service.name", value: { stringValue: serviceName } }]
|
|
332
|
+
},
|
|
333
|
+
scopeSpans: [
|
|
334
|
+
{
|
|
335
|
+
scope: { name: serviceName, version: serviceVersion },
|
|
336
|
+
spans
|
|
337
|
+
}
|
|
338
|
+
]
|
|
339
|
+
}
|
|
340
|
+
]
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
|
|
344
|
+
var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
|
|
345
|
+
var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
|
|
346
|
+
function readEnvVar(name) {
|
|
347
|
+
var _a;
|
|
348
|
+
try {
|
|
349
|
+
const env = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env;
|
|
350
|
+
if (env && typeof env[name] === "string" && env[name].length > 0) {
|
|
351
|
+
return env[name];
|
|
352
|
+
}
|
|
353
|
+
} catch (e) {
|
|
354
|
+
}
|
|
355
|
+
return void 0;
|
|
356
|
+
}
|
|
357
|
+
function readWorkshopEnv() {
|
|
358
|
+
const raw = readEnvVar(WORKSHOP_ENV_VAR);
|
|
359
|
+
if (raw === void 0) return void 0;
|
|
360
|
+
const trimmed = raw.trim();
|
|
361
|
+
if (trimmed.length === 0) return void 0;
|
|
362
|
+
if (/^https?:\/\//i.test(trimmed)) return { url: trimmed };
|
|
363
|
+
if (/^(1|true|yes|on)$/i.test(trimmed)) return "enable";
|
|
364
|
+
if (/^(0|false|no|off)$/i.test(trimmed)) return "disable";
|
|
365
|
+
return void 0;
|
|
366
|
+
}
|
|
367
|
+
function isLocalDevHost(hostname) {
|
|
368
|
+
if (!hostname) return false;
|
|
369
|
+
if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1") {
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
if (hostname.endsWith(".localhost")) return true;
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
function readRuntimeHostname() {
|
|
376
|
+
try {
|
|
377
|
+
const loc = globalThis == null ? void 0 : globalThis.location;
|
|
378
|
+
if (loc && typeof loc.hostname === "string" && loc.hostname.length > 0) {
|
|
379
|
+
return loc.hostname;
|
|
380
|
+
}
|
|
381
|
+
} catch (e) {
|
|
382
|
+
}
|
|
383
|
+
return void 0;
|
|
384
|
+
}
|
|
385
|
+
function shouldAutoEnableLocalWorkshop() {
|
|
386
|
+
if (isLocalDevHost(readRuntimeHostname())) return true;
|
|
387
|
+
if (readEnvVar("NODE_ENV") === "development") return true;
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
function resolveLocalDebuggerBaseUrl(baseUrl) {
|
|
391
|
+
var _a, _b, _c;
|
|
392
|
+
if (baseUrl === null) return null;
|
|
393
|
+
if (typeof baseUrl === "string" && baseUrl.length > 0) {
|
|
394
|
+
return (_a = formatEndpoint(baseUrl)) != null ? _a : null;
|
|
395
|
+
}
|
|
396
|
+
const explicitUrlEnv = readEnvVar(LOCAL_DEBUGGER_ENV_VAR);
|
|
397
|
+
if (explicitUrlEnv) return (_b = formatEndpoint(explicitUrlEnv)) != null ? _b : null;
|
|
398
|
+
const workshopEnv = readWorkshopEnv();
|
|
399
|
+
if (workshopEnv === "disable") return null;
|
|
400
|
+
if (workshopEnv === "enable") return DEFAULT_LOCAL_WORKSHOP_URL;
|
|
401
|
+
if (workshopEnv && "url" in workshopEnv) return (_c = formatEndpoint(workshopEnv.url)) != null ? _c : null;
|
|
402
|
+
if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
function mirrorTraceExportToLocalDebugger(body, options = {}) {
|
|
406
|
+
var _a;
|
|
407
|
+
const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
|
|
408
|
+
if (!baseUrl) return;
|
|
409
|
+
void postJson(`${baseUrl}traces`, body, {}, {
|
|
410
|
+
maxAttempts: 1,
|
|
411
|
+
debug: (_a = options.debug) != null ? _a : false,
|
|
412
|
+
sdkName: options.sdkName
|
|
413
|
+
}).catch(() => {
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
var PROJECT_ID_HEADER = "X-Raindrop-Project-Id";
|
|
417
|
+
var PROJECT_ID_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
418
|
+
function isValidProjectIdSlug(value) {
|
|
419
|
+
return PROJECT_ID_SLUG_PATTERN.test(value);
|
|
420
|
+
}
|
|
421
|
+
function normalizeProjectId(raw, opts) {
|
|
422
|
+
if (typeof raw !== "string") return void 0;
|
|
423
|
+
const trimmed = raw.trim();
|
|
424
|
+
if (!trimmed) return void 0;
|
|
425
|
+
if (!isValidProjectIdSlug(trimmed) && opts.debug) {
|
|
426
|
+
console.warn(
|
|
427
|
+
`${opts.prefix} projectId "${trimmed}" does not match slug ${PROJECT_ID_SLUG_PATTERN.source}; sending anyway \u2014 backend may reject with HTTP 400`
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
return trimmed;
|
|
431
|
+
}
|
|
432
|
+
function projectIdHeaders(projectId) {
|
|
433
|
+
return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
|
|
434
|
+
}
|
|
435
|
+
var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
|
|
436
|
+
"ai.request.providerOptions",
|
|
437
|
+
"ai.response.providerMetadata"
|
|
438
|
+
];
|
|
439
|
+
new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
|
|
440
|
+
globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
|
|
441
|
+
|
|
442
|
+
// src/internal/limits.ts
|
|
443
|
+
var DEFAULT_MAX_TEXT_FIELD_CHARS2 = 1e6;
|
|
444
|
+
var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
|
|
445
|
+
function effectiveAttributeLimit(configured) {
|
|
446
|
+
let limit = typeof configured === "number" && Number.isFinite(configured) && configured > 0 ? Math.floor(configured) : DEFAULT_MAX_TEXT_FIELD_CHARS2;
|
|
447
|
+
try {
|
|
448
|
+
const raw = globalThis?.process?.env?.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
|
|
449
|
+
if (raw) {
|
|
450
|
+
const envLimit = Number.parseInt(raw, 10);
|
|
451
|
+
if (Number.isFinite(envLimit) && envLimit > 0) {
|
|
452
|
+
limit = Math.min(limit, envLimit);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
} catch {
|
|
456
|
+
}
|
|
457
|
+
return limit;
|
|
458
|
+
}
|
|
459
|
+
function truncateText(value, limit) {
|
|
460
|
+
if (value.length <= limit) return value;
|
|
461
|
+
if (limit > TRUNCATION_MARKER2.length) {
|
|
462
|
+
return value.slice(0, limit - TRUNCATION_MARKER2.length) + TRUNCATION_MARKER2;
|
|
463
|
+
}
|
|
464
|
+
return value.slice(0, Math.max(0, limit));
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// src/internal/exporter.ts
|
|
468
|
+
function hexToBase64(hex) {
|
|
469
|
+
if (!hex) return void 0;
|
|
470
|
+
if (hex.length === 0 || /^0+$/.test(hex)) return void 0;
|
|
471
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
472
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
473
|
+
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
|
|
474
|
+
}
|
|
475
|
+
if (typeof Buffer !== "undefined") return Buffer.from(bytes).toString("base64");
|
|
476
|
+
let bin = "";
|
|
477
|
+
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
|
|
478
|
+
return globalThis.btoa(bin);
|
|
479
|
+
}
|
|
480
|
+
function hrTimeToNanoString(hr) {
|
|
481
|
+
const [s, ns] = hr;
|
|
482
|
+
return (BigInt(Math.trunc(s)) * 1000000000n + BigInt(Math.trunc(ns))).toString();
|
|
483
|
+
}
|
|
484
|
+
function attributeToKeyValue(key, value, maxChars) {
|
|
485
|
+
if (value === void 0 || value === null) return void 0;
|
|
486
|
+
if (typeof value === "string") return { key, value: { stringValue: truncateText(value, maxChars) } };
|
|
487
|
+
if (typeof value === "boolean") return { key, value: { boolValue: value } };
|
|
488
|
+
if (typeof value === "number") {
|
|
489
|
+
return Number.isInteger(value) ? { key, value: { intValue: String(value) } } : { key, value: { doubleValue: value } };
|
|
490
|
+
}
|
|
491
|
+
if (Array.isArray(value)) {
|
|
492
|
+
return {
|
|
493
|
+
key,
|
|
494
|
+
value: {
|
|
495
|
+
arrayValue: {
|
|
496
|
+
values: value.map((v) => {
|
|
497
|
+
if (v === null || v === void 0) return void 0;
|
|
498
|
+
if (typeof v === "string") return { stringValue: truncateText(v, maxChars) };
|
|
499
|
+
if (typeof v === "boolean") return { boolValue: v };
|
|
500
|
+
if (typeof v === "number") {
|
|
501
|
+
return Number.isInteger(v) ? { intValue: String(v) } : { doubleValue: v };
|
|
502
|
+
}
|
|
503
|
+
return void 0;
|
|
504
|
+
}).filter((v) => v !== void 0)
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
return void 0;
|
|
510
|
+
}
|
|
511
|
+
function attributesToKeyValues(attrs, maxChars) {
|
|
512
|
+
if (!attrs) return [];
|
|
513
|
+
const out = [];
|
|
514
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
515
|
+
const kv = attributeToKeyValue(k, v, maxChars);
|
|
516
|
+
if (kv) out.push(kv);
|
|
517
|
+
}
|
|
518
|
+
return out;
|
|
519
|
+
}
|
|
520
|
+
function statusFromReadable(span) {
|
|
521
|
+
const code = span.status?.code;
|
|
522
|
+
if (code === void 0) return void 0;
|
|
523
|
+
if (code === 2) {
|
|
524
|
+
return { code: SpanStatusCode.ERROR, message: span.status.message };
|
|
525
|
+
}
|
|
526
|
+
if (code === 1) {
|
|
527
|
+
return { code: SpanStatusCode.OK };
|
|
528
|
+
}
|
|
529
|
+
return { code: SpanStatusCode.UNSET };
|
|
530
|
+
}
|
|
531
|
+
function readableSpanToOtlp(span, maxChars) {
|
|
532
|
+
const ctx = span.spanContext();
|
|
533
|
+
const parentSpanIdHex = span.parentSpanContext?.spanId ?? span.parentSpanId;
|
|
534
|
+
const traceId = hexToBase64(ctx.traceId);
|
|
535
|
+
const spanId = hexToBase64(ctx.spanId);
|
|
536
|
+
if (!traceId || !spanId) {
|
|
537
|
+
throw new Error(
|
|
538
|
+
`span has invalid context: traceId=${ctx.traceId} spanId=${ctx.spanId}`
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
const parentSpanId = hexToBase64(parentSpanIdHex);
|
|
542
|
+
const attrs = attributesToKeyValues(span.attributes, maxChars);
|
|
543
|
+
const otlp = {
|
|
544
|
+
traceId,
|
|
545
|
+
spanId,
|
|
546
|
+
name: span.name,
|
|
547
|
+
startTimeUnixNano: hrTimeToNanoString(span.startTime),
|
|
548
|
+
endTimeUnixNano: hrTimeToNanoString(span.endTime)
|
|
549
|
+
};
|
|
550
|
+
if (parentSpanId) otlp.parentSpanId = parentSpanId;
|
|
551
|
+
if (attrs.length) otlp.attributes = attrs;
|
|
552
|
+
const status = statusFromReadable(span);
|
|
553
|
+
if (status) otlp.status = status;
|
|
554
|
+
return otlp;
|
|
555
|
+
}
|
|
556
|
+
var RaindropEveSpanExporter = class {
|
|
557
|
+
constructor(opts = {}) {
|
|
558
|
+
this.inFlight = /* @__PURE__ */ new Set();
|
|
559
|
+
this.shuttingDown = false;
|
|
560
|
+
this.auxShutdowns = [];
|
|
561
|
+
this.writeKey = opts.writeKey?.trim();
|
|
562
|
+
this.baseUrl = formatEndpoint(opts.endpoint) ?? "https://api.raindrop.ai/v1/";
|
|
563
|
+
this.sdkName = opts.sdkName ?? "eve";
|
|
564
|
+
this.maxTextFieldChars = opts.maxTextFieldChars;
|
|
565
|
+
this.serviceName = opts.serviceName ?? "raindrop.eve";
|
|
566
|
+
this.serviceVersion = opts.serviceVersion ?? "0.0.0";
|
|
567
|
+
this.debug = opts.debug === true;
|
|
568
|
+
this.prefix = `[raindrop-ai/${this.sdkName}]`;
|
|
569
|
+
this.projectId = normalizeProjectId(opts.projectId, {
|
|
570
|
+
debug: this.debug,
|
|
571
|
+
prefix: this.prefix
|
|
572
|
+
});
|
|
573
|
+
const explicit = opts.localWorkshopUrl === false ? null : opts.localWorkshopUrl;
|
|
574
|
+
this.localDebuggerUrl = resolveLocalDebuggerBaseUrl(explicit) ?? void 0;
|
|
575
|
+
if (this.debug) {
|
|
576
|
+
console.log(
|
|
577
|
+
`${this.prefix} Initialized`,
|
|
578
|
+
{
|
|
579
|
+
raindrop: this.writeKey ? this.baseUrl : "disabled (no writeKey)",
|
|
580
|
+
workshop: this.localDebuggerUrl ?? "disabled"
|
|
581
|
+
}
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
/** True when we have at least one destination (Raindrop or Workshop). */
|
|
586
|
+
hasDestinations() {
|
|
587
|
+
return Boolean(this.writeKey || this.localDebuggerUrl);
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Update the OTLP `service.name` attribute. Called by Eve's
|
|
591
|
+
* `setup({ agentName })` so the resource attribute reflects the actual
|
|
592
|
+
* agent name when the caller didn't pin one via `opts.serviceName`.
|
|
593
|
+
*/
|
|
594
|
+
setServiceName(serviceName) {
|
|
595
|
+
if (!serviceName || serviceName === this.serviceName) return;
|
|
596
|
+
this.serviceName = serviceName;
|
|
597
|
+
if (this.debug) {
|
|
598
|
+
console.log(`${this.prefix} serviceName updated \u2192 ${serviceName}`);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
export(spans, resultCallback) {
|
|
602
|
+
if (this.shuttingDown) {
|
|
603
|
+
resultCallback({ code: 1, error: new Error("exporter shut down") });
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
if (spans.length === 0) {
|
|
607
|
+
resultCallback({ code: 0 });
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
const maxChars = effectiveAttributeLimit(this.maxTextFieldChars);
|
|
611
|
+
const otlpSpans = [];
|
|
612
|
+
for (const span of spans) {
|
|
613
|
+
try {
|
|
614
|
+
otlpSpans.push(readableSpanToOtlp(span, maxChars));
|
|
615
|
+
} catch (err) {
|
|
616
|
+
if (this.debug) {
|
|
617
|
+
console.warn(`${this.prefix} failed to convert span`, err);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (otlpSpans.length === 0) {
|
|
622
|
+
resultCallback({ code: 0 });
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
const body = buildExportTraceServiceRequest(otlpSpans, this.serviceName, this.serviceVersion);
|
|
626
|
+
if (this.localDebuggerUrl) {
|
|
627
|
+
mirrorTraceExportToLocalDebugger(body, {
|
|
628
|
+
baseUrl: this.localDebuggerUrl,
|
|
629
|
+
debug: this.debug,
|
|
630
|
+
sdkName: this.sdkName
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
if (!this.writeKey) {
|
|
634
|
+
if (this.debug) {
|
|
635
|
+
console.log(`${this.prefix} exported ${otlpSpans.length} spans (workshop-only)`);
|
|
636
|
+
}
|
|
637
|
+
resultCallback({ code: 0 });
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
const url = `${this.baseUrl}traces`;
|
|
641
|
+
if (this.debug) {
|
|
642
|
+
console.log(`${this.prefix} sending ${otlpSpans.length} spans -> ${url}`);
|
|
643
|
+
}
|
|
644
|
+
const p = postJson(
|
|
645
|
+
url,
|
|
646
|
+
body,
|
|
647
|
+
{
|
|
648
|
+
Authorization: `Bearer ${this.writeKey}`,
|
|
649
|
+
...projectIdHeaders(this.projectId)
|
|
650
|
+
},
|
|
651
|
+
{ maxAttempts: 3, debug: this.debug, sdkName: this.sdkName }
|
|
652
|
+
).then(() => {
|
|
653
|
+
if (this.debug) console.log(`${this.prefix} sent ${otlpSpans.length} spans`);
|
|
654
|
+
resultCallback({ code: 0 });
|
|
655
|
+
}).catch((err) => {
|
|
656
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
657
|
+
console.warn(`${this.prefix} failed to send ${otlpSpans.length} spans: ${msg}`);
|
|
658
|
+
resultCallback({ code: 1, error: err instanceof Error ? err : new Error(msg) });
|
|
659
|
+
}).finally(() => {
|
|
660
|
+
this.inFlight.delete(p);
|
|
661
|
+
});
|
|
662
|
+
this.inFlight.add(p);
|
|
663
|
+
}
|
|
664
|
+
async forceFlush() {
|
|
665
|
+
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
666
|
+
})));
|
|
667
|
+
}
|
|
668
|
+
async shutdown() {
|
|
669
|
+
this.shuttingDown = true;
|
|
670
|
+
await this.forceFlush();
|
|
671
|
+
await Promise.all(
|
|
672
|
+
this.auxShutdowns.map((fn) => fn().catch(() => {
|
|
673
|
+
}))
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Register an extra teardown hook to run during {@link shutdown}. Used to
|
|
678
|
+
* drain the AI SDK trace + event shippers that the `@raindrop-ai/eve`
|
|
679
|
+
* integration builds alongside this exporter — without this, pending
|
|
680
|
+
* `track_partial` events and AI SDK trace spans would be dropped on
|
|
681
|
+
* SIGINT / SIGTERM.
|
|
682
|
+
*/
|
|
683
|
+
attachAuxShutdown(fn) {
|
|
684
|
+
this.auxShutdowns.push(fn);
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
// package.json
|
|
689
|
+
var package_default = {
|
|
690
|
+
name: "@raindrop-ai/eve",
|
|
691
|
+
version: "0.0.10"};
|
|
692
|
+
|
|
693
|
+
// src/internal/version.ts
|
|
694
|
+
var libraryName = package_default.name;
|
|
695
|
+
var libraryVersion = package_default.version;
|
|
696
|
+
|
|
697
|
+
// src/index.ts
|
|
698
|
+
var RAINDROP_EVE_SDK_NAME = libraryName;
|
|
699
|
+
var RAINDROP_EVE_SDK_VERSION = libraryVersion;
|
|
700
|
+
var canonicalTurnIdBySession = /* @__PURE__ */ new Map();
|
|
701
|
+
var MAX_CANONICAL_TURN_SESSIONS = 1024;
|
|
702
|
+
function _resetCanonicalTurnIds() {
|
|
703
|
+
canonicalTurnIdBySession.clear();
|
|
704
|
+
}
|
|
705
|
+
var RAINDROP_EVE_INTEGRATION = /* @__PURE__ */ Symbol.for("raindrop.eve.integration");
|
|
706
|
+
var RAINDROP_EVE_OTEL_REGISTERED = /* @__PURE__ */ Symbol.for("raindrop.eve.otel.registered");
|
|
707
|
+
function wrapWithDebugLogs(integration, prefix) {
|
|
708
|
+
const methodNames = [
|
|
709
|
+
"onStart",
|
|
710
|
+
"onStepStart",
|
|
711
|
+
"onLanguageModelCallStart",
|
|
712
|
+
"onLanguageModelCallEnd",
|
|
713
|
+
"onToolExecutionStart",
|
|
714
|
+
"onToolExecutionEnd",
|
|
715
|
+
"onToolCallStart",
|
|
716
|
+
"onToolCallFinish",
|
|
717
|
+
"onStepFinish",
|
|
718
|
+
"onObjectStepStart",
|
|
719
|
+
"onObjectStepFinish",
|
|
720
|
+
"onEmbedStart",
|
|
721
|
+
"onEmbedEnd",
|
|
722
|
+
"onRerankStart",
|
|
723
|
+
"onRerankEnd",
|
|
724
|
+
// `onFinish` is the v7-canary-and-earlier name; `onEnd` is the
|
|
725
|
+
// post-canary.144 name (see `injectEveCallContext` for the alias).
|
|
726
|
+
// Both are listed so the wrapper logs whichever the underlying
|
|
727
|
+
// integration / dispatcher actually invokes.
|
|
728
|
+
"onFinish",
|
|
729
|
+
"onEnd",
|
|
730
|
+
"onError",
|
|
731
|
+
// `onAbort` (v7 canary post-beta.116) fires when a streamText call is
|
|
732
|
+
// cancelled via its AbortSignal — neither `onEnd` nor `onError` runs then.
|
|
733
|
+
"onAbort"
|
|
734
|
+
];
|
|
735
|
+
const wrapped = integration;
|
|
736
|
+
for (const name of methodNames) {
|
|
737
|
+
const original = wrapped[name];
|
|
738
|
+
if (typeof original !== "function") continue;
|
|
739
|
+
const fn = original;
|
|
740
|
+
wrapped[name] = function(event) {
|
|
741
|
+
try {
|
|
742
|
+
const ev = event;
|
|
743
|
+
const op = ev && (ev.operationId ?? ev["operation_id"]);
|
|
744
|
+
console.log(`${prefix} ai-sdk-tel ${name} op=${String(op ?? "?")}`);
|
|
745
|
+
} catch {
|
|
746
|
+
}
|
|
747
|
+
return fn.call(integration, event);
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
return integration;
|
|
751
|
+
}
|
|
752
|
+
function injectEveCallContext(integration, opts) {
|
|
753
|
+
const target = integration;
|
|
754
|
+
if (typeof target.onFinish === "function" && typeof target.onEnd !== "function") {
|
|
755
|
+
target.onEnd = target.onFinish;
|
|
756
|
+
}
|
|
757
|
+
const original = target.onStart;
|
|
758
|
+
if (typeof original !== "function") return integration;
|
|
759
|
+
const fn = original;
|
|
760
|
+
target.onStart = function(event) {
|
|
761
|
+
try {
|
|
762
|
+
const ev = event;
|
|
763
|
+
if (envDebug()) {
|
|
764
|
+
try {
|
|
765
|
+
const ctxAny = ev.runtimeContext;
|
|
766
|
+
const messages = ev.messages ?? [];
|
|
767
|
+
const firstMsgContent = messages[0]?.content;
|
|
768
|
+
const firstMsgPreview = typeof firstMsgContent === "string" ? firstMsgContent.slice(0, 220) : Array.isArray(firstMsgContent) ? JSON.stringify(firstMsgContent).slice(0, 220) : void 0;
|
|
769
|
+
const activeSpanForDebug = trace.getActiveSpan();
|
|
770
|
+
const traceIdForDebug = activeSpanForDebug?.spanContext?.()?.traceId;
|
|
771
|
+
const turnEntryForDebug = lookupEveTurn(traceIdForDebug);
|
|
772
|
+
const lineageForDebug = getEveSessionLineage();
|
|
773
|
+
console.log(
|
|
774
|
+
`[raindrop-ai/eve][probe] agentName=${opts.agentName} fn=${ev.functionId} traceId=${traceIdForDebug} activeSpan=${activeSpanForDebug?.name} runtimeContextKeys=${ctxAny ? Object.keys(ctxAny).join(",") : "<none>"} turnEntry=${JSON.stringify(turnEntryForDebug)} eveSession=${JSON.stringify(lineageForDebug)} firstMsg=${JSON.stringify(firstMsgPreview)}`
|
|
775
|
+
);
|
|
776
|
+
} catch (e) {
|
|
777
|
+
console.log(`[raindrop-ai/eve][probe] error: ${e.message}`);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
const ctx = ev.runtimeContext;
|
|
781
|
+
const activeSpan = trace.getActiveSpan();
|
|
782
|
+
const spanAttrs = activeSpan?.attributes;
|
|
783
|
+
const traceId = activeSpan?.spanContext?.()?.traceId;
|
|
784
|
+
const turnEntry = lookupEveTurn(traceId);
|
|
785
|
+
const eveSession = getEveSessionLineage();
|
|
786
|
+
const sessionFromContext = eveSession?.sessionId ?? (typeof ctx?.["eve.session.id"] === "string" ? ctx["eve.session.id"] : void 0) ?? turnEntry?.sessionId ?? (typeof spanAttrs?.["eve.session.id"] === "string" ? spanAttrs["eve.session.id"] : void 0);
|
|
787
|
+
const convoFromContext = (typeof ctx?.["eve.continuation_token"] === "string" ? ctx["eve.continuation_token"] : void 0) ?? turnEntry?.continuationToken ?? (typeof spanAttrs?.["eve.continuation_token"] === "string" ? spanAttrs["eve.continuation_token"] : void 0) ?? // eve >= 0.45 stopped forwarding `eve.continuation_token`,
|
|
788
|
+
// the legacy convo key — leaving `raindrop.convoId` null so every
|
|
789
|
+
// multi-turn run un-grouped on the Conversations view. The session id is
|
|
790
|
+
// stable across a run's turns and distinct per run, so falling back to
|
|
791
|
+
// it restores turn-to-conversation grouping. An author-supplied
|
|
792
|
+
// `raindrop.convoId` (e.g. a durable Slack thread id mapped in the step
|
|
793
|
+
// hook) still wins: it is already on `meta` before this is applied.
|
|
794
|
+
sessionFromContext;
|
|
795
|
+
const ctxTurnId = typeof ctx?.["eve.turn.id"] === "string" ? ctx["eve.turn.id"] : void 0;
|
|
796
|
+
if (sessionFromContext && ctxTurnId) {
|
|
797
|
+
if (canonicalTurnIdBySession.size >= MAX_CANONICAL_TURN_SESSIONS && !canonicalTurnIdBySession.has(sessionFromContext)) {
|
|
798
|
+
const oldest = canonicalTurnIdBySession.keys().next().value;
|
|
799
|
+
if (oldest !== void 0) canonicalTurnIdBySession.delete(oldest);
|
|
800
|
+
}
|
|
801
|
+
canonicalTurnIdBySession.set(sessionFromContext, ctxTurnId);
|
|
802
|
+
}
|
|
803
|
+
const turnIdFromContext = ctxTurnId ?? (sessionFromContext ? canonicalTurnIdBySession.get(sessionFromContext) : void 0) ?? (typeof eveSession?.turn?.id === "string" ? eveSession.turn.id : void 0) ?? (typeof spanAttrs?.["eve.turn.id"] === "string" ? spanAttrs["eve.turn.id"] : void 0);
|
|
804
|
+
const meta = ev.metadata ?? {};
|
|
805
|
+
let userProps;
|
|
806
|
+
for (const k of Object.keys(meta)) {
|
|
807
|
+
if (k === "raindrop.properties") continue;
|
|
808
|
+
if (k.startsWith("raindrop.") || k.startsWith("eve.")) continue;
|
|
809
|
+
(userProps ?? (userProps = {}))[k] = meta[k];
|
|
810
|
+
}
|
|
811
|
+
if (ctx) {
|
|
812
|
+
for (const [k, v] of Object.entries(ctx)) {
|
|
813
|
+
if (typeof v !== "string") continue;
|
|
814
|
+
if (k.startsWith("eve.")) continue;
|
|
815
|
+
if (k.startsWith("raindrop.")) {
|
|
816
|
+
if (meta[k] === void 0) meta[k] = v;
|
|
817
|
+
} else if (!userProps || userProps[k] === void 0) {
|
|
818
|
+
(userProps ?? (userProps = {}))[k] = v;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
if (opts.authoredMetadata) {
|
|
823
|
+
for (const [k, v] of Object.entries(opts.authoredMetadata)) {
|
|
824
|
+
if (k.startsWith("raindrop.") || k.startsWith("eve.")) {
|
|
825
|
+
if (meta[k] === void 0) meta[k] = v;
|
|
826
|
+
} else if (!userProps || userProps[k] === void 0) {
|
|
827
|
+
(userProps ?? (userProps = {}))[k] = v;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
if (userProps) {
|
|
832
|
+
const existing = meta["raindrop.properties"];
|
|
833
|
+
const existingProps = existing && typeof existing === "object" ? existing : void 0;
|
|
834
|
+
meta["raindrop.properties"] = { ...userProps, ...existingProps };
|
|
835
|
+
}
|
|
836
|
+
const parentToolContext = getCurrentParentToolContext();
|
|
837
|
+
const crossSandboxParent = parentToolContext === void 0 ? eveSession?.parent : void 0;
|
|
838
|
+
const runtimeParentSessionId = parentToolContext === void 0 && crossSandboxParent === void 0 && typeof meta["raindrop.parent.sessionId"] === "string" ? meta["raindrop.parent.sessionId"] : void 0;
|
|
839
|
+
const crossSandboxAgentName = typeof ev.functionId === "string" && ev.functionId ? ev.functionId : opts.agentName;
|
|
840
|
+
const detected = parentToolContext?.toolName ?? (crossSandboxParent !== void 0 || runtimeParentSessionId !== void 0 ? crossSandboxAgentName : void 0);
|
|
841
|
+
const activeTraceId = parentToolContext ? void 0 : trace.getActiveSpan()?.spanContext()?.traceId;
|
|
842
|
+
const isSubAgent = parentToolContext !== void 0 || crossSandboxParent !== void 0 || runtimeParentSessionId !== void 0;
|
|
843
|
+
const rootTurnEventId = !isSubAgent && sessionFromContext && turnIdFromContext ? eveTurnEventId(sessionFromContext, turnIdFromContext) : void 0;
|
|
844
|
+
const eventId = rootTurnEventId ?? activeTraceId;
|
|
845
|
+
if (sessionFromContext && meta["raindrop.userId"] === void 0) {
|
|
846
|
+
meta["raindrop.userId"] = sessionFromContext;
|
|
847
|
+
}
|
|
848
|
+
if (meta["raindrop.convoId"] === void 0) {
|
|
849
|
+
const parentConvo = isSubAgent && typeof meta["raindrop.parent.rootSessionId"] === "string" ? meta["raindrop.parent.rootSessionId"] : isSubAgent && typeof meta["raindrop.parent.sessionId"] === "string" ? meta["raindrop.parent.sessionId"] : void 0;
|
|
850
|
+
const convo = parentConvo ?? convoFromContext;
|
|
851
|
+
if (convo) meta["raindrop.convoId"] = convo;
|
|
852
|
+
}
|
|
853
|
+
if (eventId && meta["raindrop.eventId"] === void 0) {
|
|
854
|
+
meta["raindrop.eventId"] = eventId;
|
|
855
|
+
}
|
|
856
|
+
if (detected && meta["eve.subagent.name"] === void 0) {
|
|
857
|
+
meta["eve.subagent.name"] = detected;
|
|
858
|
+
}
|
|
859
|
+
if (meta["eve.agent.role"] === void 0) {
|
|
860
|
+
meta["eve.agent.role"] = detected ? "subagent" : "root";
|
|
861
|
+
}
|
|
862
|
+
if (parentToolContext) {
|
|
863
|
+
if (meta["raindrop.parent.callId"] === void 0) {
|
|
864
|
+
meta["raindrop.parent.callId"] = parentToolContext.parentCallId;
|
|
865
|
+
}
|
|
866
|
+
if (meta["raindrop.parent.toolCallId"] === void 0) {
|
|
867
|
+
meta["raindrop.parent.toolCallId"] = parentToolContext.toolCallId;
|
|
868
|
+
}
|
|
869
|
+
if (meta["raindrop.parent.toolName"] === void 0) {
|
|
870
|
+
meta["raindrop.parent.toolName"] = parentToolContext.toolName;
|
|
871
|
+
}
|
|
872
|
+
} else if (crossSandboxParent) {
|
|
873
|
+
for (const [key, value] of Object.entries(parentLineageMetadata(crossSandboxParent))) {
|
|
874
|
+
if (meta[key] === void 0) meta[key] = value;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
if (Object.keys(meta).length > 0) ev.metadata = meta;
|
|
878
|
+
} catch {
|
|
879
|
+
}
|
|
880
|
+
return fn.call(integration, event);
|
|
881
|
+
};
|
|
882
|
+
return integration;
|
|
883
|
+
}
|
|
884
|
+
function registerAISdkTelemetry(integration) {
|
|
885
|
+
const g = globalThis;
|
|
886
|
+
if (!Array.isArray(g.AI_SDK_TELEMETRY_INTEGRATIONS)) {
|
|
887
|
+
g.AI_SDK_TELEMETRY_INTEGRATIONS = [];
|
|
888
|
+
}
|
|
889
|
+
const tagged = integration;
|
|
890
|
+
tagged[RAINDROP_EVE_INTEGRATION] = true;
|
|
891
|
+
g.AI_SDK_TELEMETRY_INTEGRATIONS = g.AI_SDK_TELEMETRY_INTEGRATIONS.filter(
|
|
892
|
+
(e) => !e[RAINDROP_EVE_INTEGRATION]
|
|
893
|
+
);
|
|
894
|
+
g.AI_SDK_TELEMETRY_INTEGRATIONS.push(tagged);
|
|
895
|
+
}
|
|
896
|
+
function envWriteKey() {
|
|
897
|
+
if (typeof process === "undefined") return void 0;
|
|
898
|
+
const v = process.env?.RAINDROP_WRITE_KEY;
|
|
899
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
900
|
+
}
|
|
901
|
+
function envProjectId() {
|
|
902
|
+
if (typeof process === "undefined") return void 0;
|
|
903
|
+
const v = process.env?.RAINDROP_PROJECT_ID;
|
|
904
|
+
return v && v.length > 0 ? v : void 0;
|
|
905
|
+
}
|
|
906
|
+
function envEndpoint() {
|
|
907
|
+
if (typeof process === "undefined") return void 0;
|
|
908
|
+
const v = process.env?.RAINDROP_ENDPOINT;
|
|
909
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
910
|
+
}
|
|
911
|
+
function envDebug() {
|
|
912
|
+
if (typeof process === "undefined") return false;
|
|
913
|
+
const v = process.env?.RAINDROP_AI_DEBUG;
|
|
914
|
+
return v === "1" || v === "true";
|
|
915
|
+
}
|
|
916
|
+
function isEveMetadataConfig(m) {
|
|
917
|
+
return typeof m === "object" && m !== null && typeof m["step.started"] === "function";
|
|
918
|
+
}
|
|
919
|
+
function eveTurnEventId(sessionId, turnId) {
|
|
920
|
+
return `eve:${sessionId}:${turnId}`;
|
|
921
|
+
}
|
|
922
|
+
function parentLineageMetadata(parent) {
|
|
923
|
+
const out = {
|
|
924
|
+
"raindrop.parent.sessionId": parent.sessionId,
|
|
925
|
+
"raindrop.parent.eventId": eveTurnEventId(parent.sessionId, parent.turn.id),
|
|
926
|
+
"raindrop.parent.turnId": parent.turn.id,
|
|
927
|
+
"raindrop.parent.turnSequence": String(parent.turn.sequence)
|
|
928
|
+
};
|
|
929
|
+
if (parent.rootSessionId !== void 0) {
|
|
930
|
+
out["raindrop.parent.rootSessionId"] = parent.rootSessionId;
|
|
931
|
+
}
|
|
932
|
+
return out;
|
|
933
|
+
}
|
|
934
|
+
function resolveCrossSandboxParentMetadata(input) {
|
|
935
|
+
const parent = parseEveSessionParent(input.session.parent);
|
|
936
|
+
if (parent === void 0) return {};
|
|
937
|
+
return parentLineageMetadata(parent);
|
|
938
|
+
}
|
|
939
|
+
var RAINDROP_PARENT_PREFIX = "raindrop.parent.";
|
|
940
|
+
function stripReservedParentMetadata(meta) {
|
|
941
|
+
if (meta == null) return {};
|
|
942
|
+
const out = {};
|
|
943
|
+
for (const [key, value] of Object.entries(meta)) {
|
|
944
|
+
if (!key.startsWith(RAINDROP_PARENT_PREFIX)) out[key] = value;
|
|
945
|
+
}
|
|
946
|
+
return out;
|
|
947
|
+
}
|
|
948
|
+
function resolveStaticMetadata(opts) {
|
|
949
|
+
const legacyFlat = isEveMetadataConfig(opts.metadata) ? void 0 : opts.metadata;
|
|
950
|
+
if (opts.staticMetadata === void 0 && legacyFlat === void 0) return void 0;
|
|
951
|
+
return stripReservedParentMetadata({ ...legacyFlat, ...opts.staticMetadata });
|
|
952
|
+
}
|
|
953
|
+
function buildEveStepStartedResolver(opts, staticMetadata) {
|
|
954
|
+
const metadataConfig = isEveMetadataConfig(opts.metadata) ? opts.metadata : void 0;
|
|
955
|
+
const eventsConfig = opts.events;
|
|
956
|
+
const eventsStepStarted = eventsConfig?.["step.started"];
|
|
957
|
+
const metadataStepStarted = metadataConfig?.["step.started"];
|
|
958
|
+
return (input) => {
|
|
959
|
+
const userMetadata = eventsStepStarted ? (
|
|
960
|
+
// Invoke with the authored config as the receiver so callbacks defined
|
|
961
|
+
// as object methods (`{ "step.started"() { return this... } }`) keep
|
|
962
|
+
// their `this` binding. The 0.53 hook returns `{ runtimeContext }`.
|
|
963
|
+
eventsStepStarted.call(eventsConfig, input)?.runtimeContext ?? {}
|
|
964
|
+
) : metadataStepStarted ? metadataStepStarted.call(metadataConfig, input) : {};
|
|
965
|
+
return {
|
|
966
|
+
...staticMetadata,
|
|
967
|
+
...stripReservedParentMetadata(userMetadata),
|
|
968
|
+
...resolveCrossSandboxParentMetadata(input)
|
|
969
|
+
};
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
function buildEveMetadataConfig(opts, staticMetadata) {
|
|
973
|
+
const resolve = buildEveStepStartedResolver(opts, staticMetadata);
|
|
974
|
+
return { "step.started": (input) => resolve(input) };
|
|
975
|
+
}
|
|
976
|
+
function buildEveEventsConfig(opts, staticMetadata) {
|
|
977
|
+
const resolve = buildEveStepStartedResolver(opts, staticMetadata);
|
|
978
|
+
return { "step.started": (input) => ({ runtimeContext: resolve(input) }) };
|
|
979
|
+
}
|
|
980
|
+
function defineRaindropInstrumentation(opts = {}) {
|
|
981
|
+
const debug = opts.debug === true || envDebug();
|
|
982
|
+
const writeKey = opts.writeKey ?? envWriteKey();
|
|
983
|
+
const endpoint = opts.endpoint ?? envEndpoint();
|
|
984
|
+
const projectId = opts.projectId ?? envProjectId();
|
|
985
|
+
const exporter = new RaindropEveSpanExporter({
|
|
986
|
+
writeKey,
|
|
987
|
+
endpoint,
|
|
988
|
+
projectId,
|
|
989
|
+
localWorkshopUrl: opts.localWorkshopUrl,
|
|
990
|
+
serviceName: opts.serviceName,
|
|
991
|
+
serviceVersion: opts.serviceVersion ?? libraryVersion,
|
|
992
|
+
sdkName: "eve",
|
|
993
|
+
debug,
|
|
994
|
+
maxTextFieldChars: opts.maxTextFieldChars
|
|
995
|
+
});
|
|
996
|
+
const localWorkshopUrl = opts.localWorkshopUrl === false ? false : opts.localWorkshopUrl;
|
|
997
|
+
const raindrop = createRaindropAISDK({
|
|
998
|
+
writeKey,
|
|
999
|
+
endpoint,
|
|
1000
|
+
projectId,
|
|
1001
|
+
localWorkshopUrl,
|
|
1002
|
+
events: { debug },
|
|
1003
|
+
traces: { debug }
|
|
1004
|
+
});
|
|
1005
|
+
exporter.attachAuxShutdown(() => raindrop.shutdown());
|
|
1006
|
+
const prefix = "[raindrop-ai/eve]";
|
|
1007
|
+
const staticMetadata = resolveStaticMetadata(opts);
|
|
1008
|
+
const eveMetadata = buildEveMetadataConfig(opts, staticMetadata);
|
|
1009
|
+
const eveEvents = buildEveEventsConfig(opts, staticMetadata);
|
|
1010
|
+
return {
|
|
1011
|
+
functionId: opts.functionId,
|
|
1012
|
+
metadata: eveMetadata,
|
|
1013
|
+
events: eveEvents,
|
|
1014
|
+
recordInputs: opts.recordInputs,
|
|
1015
|
+
recordOutputs: opts.recordOutputs,
|
|
1016
|
+
exporter,
|
|
1017
|
+
raindrop,
|
|
1018
|
+
setup({ agentName }) {
|
|
1019
|
+
if (!exporter.hasDestinations()) {
|
|
1020
|
+
console.warn(
|
|
1021
|
+
`${prefix} no destinations configured \u2014 set RAINDROP_WRITE_KEY for hosted Raindrop, or run \`raindrop workshop\` for local mirroring.`
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
const serviceName = opts.serviceName ?? agentName;
|
|
1025
|
+
exporter.setServiceName(serviceName);
|
|
1026
|
+
if (opts.registerOTel) {
|
|
1027
|
+
const g = globalThis;
|
|
1028
|
+
const already = g[RAINDROP_EVE_OTEL_REGISTERED];
|
|
1029
|
+
if (already === void 0) {
|
|
1030
|
+
opts.registerOTel({
|
|
1031
|
+
serviceName,
|
|
1032
|
+
traceExporter: exporter
|
|
1033
|
+
});
|
|
1034
|
+
g[RAINDROP_EVE_OTEL_REGISTERED] = { exporter };
|
|
1035
|
+
if (debug) {
|
|
1036
|
+
console.log(`${prefix} registered OTel for serviceName=${serviceName}`);
|
|
1037
|
+
}
|
|
1038
|
+
} else if (debug) {
|
|
1039
|
+
console.log(
|
|
1040
|
+
`${prefix} skipping duplicate registerOTel \u2014 already registered (hot reload). Restart the process to re-register.`
|
|
1041
|
+
);
|
|
1042
|
+
}
|
|
1043
|
+
} else {
|
|
1044
|
+
console.warn(
|
|
1045
|
+
`${prefix} no \`registerOTel\` passed \u2014 Eve's own OTel spans (workflow/step/fetch) will not be exported. Pass \`registerOTel\` from \`@vercel/otel\`.`
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
const installed = installEveTurnSpanProcessor(trace);
|
|
1049
|
+
if (!installed && debug) {
|
|
1050
|
+
console.log(
|
|
1051
|
+
`${prefix} could not install ai.eve.turn SpanProcessor \u2014 cross-sandbox sub-agent detection will fall back to active-span attributes only. (Global tracer provider missing addSpanProcessor \u2014 register \`registerOTel\` from \`@vercel/otel\` before \`defineRaindropInstrumentation\`'s \`setup()\` runs.)`
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
const integration = raindrop.createTelemetryIntegration({
|
|
1055
|
+
userId: opts.userId ?? agentName,
|
|
1056
|
+
eventName: opts.eventName
|
|
1057
|
+
});
|
|
1058
|
+
injectEveCallContext(integration, {
|
|
1059
|
+
agentName,
|
|
1060
|
+
rootFunctionId: opts.functionId ?? agentName,
|
|
1061
|
+
authoredMetadata: staticMetadata
|
|
1062
|
+
});
|
|
1063
|
+
const wrapped = debug ? wrapWithDebugLogs(integration, prefix) : integration;
|
|
1064
|
+
registerAISdkTelemetry(wrapped);
|
|
1065
|
+
if (debug) {
|
|
1066
|
+
console.log(`${prefix} registered AI SDK telemetry integration (events + traces).`);
|
|
1067
|
+
const g = globalThis;
|
|
1068
|
+
console.log(
|
|
1069
|
+
`${prefix} globalThis.AI_SDK_TELEMETRY_INTEGRATIONS length=${g.AI_SDK_TELEMETRY_INTEGRATIONS?.length ?? 0}`
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
var index_default = defineRaindropInstrumentation;
|
|
1076
|
+
|
|
1077
|
+
export { RAINDROP_EVE_SDK_NAME, RAINDROP_EVE_SDK_VERSION, RaindropEveSpanExporter, _resetCanonicalTurnIds, index_default as default, defineRaindropInstrumentation };
|
|
1078
|
+
//# sourceMappingURL=index.mjs.map
|
|
1079
|
+
//# sourceMappingURL=index.mjs.map
|