raindrop-ai 0.7.0 → 0.7.1

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.
@@ -101,1082 +101,1120 @@ function evalScopeAttributes() {
101
101
  return { [EVAL_CORRELATION_ID_ATTRIBUTE]: scope.correlationId };
102
102
  }
103
103
 
104
- // ../core/dist/chunk-T53LQAH7.js
105
- function normalizeFeatureFlags(input) {
106
- if (Array.isArray(input)) {
107
- return Object.fromEntries(input.map((name) => [name, "true"]));
104
+ // ../core/dist/chunk-WJOR6M7H.js
105
+ var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
106
+ function state() {
107
+ const holder = globalThis;
108
+ let current = holder[STATE_KEY];
109
+ if (!current) {
110
+ let storage = null;
111
+ try {
112
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
113
+ if (Ctor) storage = new Ctor();
114
+ } catch (e) {
115
+ storage = null;
116
+ }
117
+ current = {
118
+ storage,
119
+ syncDestination: void 0,
120
+ spanDestinations: /* @__PURE__ */ new WeakMap()
121
+ };
122
+ holder[STATE_KEY] = current;
108
123
  }
109
- return Object.fromEntries(
110
- Object.entries(input).map(([k, v]) => [k, String(v)])
111
- );
124
+ return current;
112
125
  }
113
- function getCrypto() {
114
- const c = globalThis.crypto;
115
- return c;
126
+ function currentReplayTraceDestination() {
127
+ var _a, _b;
128
+ const routing = state();
129
+ return (_b = (_a = routing.storage) == null ? void 0 : _a.getStore()) != null ? _b : routing.syncDestination;
116
130
  }
117
- function randomBytes(length) {
118
- const cryptoObj = getCrypto();
119
- const out = new Uint8Array(length);
120
- if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
121
- cryptoObj.getRandomValues(out);
122
- return out;
123
- }
124
- for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
125
- return out;
131
+ function rememberReplayTraceDestination(span) {
132
+ const destination = currentReplayTraceDestination();
133
+ if (destination) state().spanDestinations.set(span, destination);
126
134
  }
127
- function randomUUID() {
128
- const cryptoObj = getCrypto();
129
- if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
130
- return cryptoObj.randomUUID();
131
- }
132
- const b = randomBytes(16);
133
- b[6] = b[6] & 15 | 64;
134
- b[8] = b[8] & 63 | 128;
135
- const hex = [...b].map((x) => x.toString(16).padStart(2, "0")).join("");
136
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
135
+ function copyReplayTraceDestination(source, target) {
136
+ const destination = state().spanDestinations.get(source);
137
+ if (destination) state().spanDestinations.set(target, destination);
137
138
  }
138
- var HANDOFF_ATTRIBUTE_SUFFIXES = {
139
- mode: "raindrop.handoff.mode",
140
- childEventId: "raindrop.handoff.childEventId",
141
- parentEventId: "raindrop.handoff.parentEventId",
142
- parentSpanId: "raindrop.handoff.parentSpanId",
143
- name: "raindrop.handoff.name",
144
- terminal: "raindrop.handoff.terminal",
145
- agentRole: "raindrop.agent.role"
146
- };
147
- var AI_SDK_METADATA_PREFIX = "ai.telemetry.metadata.";
148
- var DETACHED_MODE = "detached";
149
- var SUBAGENT_AGENT_ROLE = "subagent";
150
- var HANDOFF_TERMINAL_CANCELLED = "cancelled";
151
- var TOOL_EVENTS_SUFFIX = "raindrop.toolEvents";
152
- var TOOL_EVENTS_ALLOW = "allow";
153
- var OPERATION_ID_ATTRIBUTE = "ai.operationId";
154
- var SUBAGENT_ABORT_OPERATION_ID = "ai.subagent.failed";
155
- var SUBAGENT_ABORT_SPAN_NAME = SUBAGENT_ABORT_OPERATION_ID;
156
- var DEFAULT_DISPATCH_SPAN_NAME = "launch_subagent";
157
- var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
158
- function defined(entries) {
159
- const out = {};
160
- for (const [key, value] of Object.entries(entries)) {
161
- if (typeof value === "string" && value.trim().length > 0) out[key] = value;
139
+ function replayTraceDestinationFor(value) {
140
+ if (typeof value !== "object" && typeof value !== "function" || value === null) {
141
+ return void 0;
162
142
  }
163
- return out;
164
- }
165
- function detachedDispatchMetadata(dispatch) {
166
- return defined({
167
- [HANDOFF_ATTRIBUTE_SUFFIXES.mode]: DETACHED_MODE,
168
- [HANDOFF_ATTRIBUTE_SUFFIXES.childEventId]: dispatch.childEventId,
169
- [HANDOFF_ATTRIBUTE_SUFFIXES.name]: dispatch.name
170
- });
143
+ return state().spanDestinations.get(value);
171
144
  }
172
- function detachedChildMetadata(link) {
173
- return defined({
174
- [HANDOFF_ATTRIBUTE_SUFFIXES.parentEventId]: link.parentEventId,
175
- [HANDOFF_ATTRIBUTE_SUFFIXES.parentSpanId]: link.parentSpanId,
176
- [HANDOFF_ATTRIBUTE_SUFFIXES.name]: link.name,
177
- [HANDOFF_ATTRIBUTE_SUFFIXES.agentRole]: SUBAGENT_AGENT_ROLE
178
- });
145
+ function recordReplayExportedSpan(destination, spanId, traceId) {
146
+ var _a, _b;
147
+ if (!destination || !spanId || !traceId) return;
148
+ (_a = destination.exportedSpanIds) == null ? void 0 : _a.add(spanId);
149
+ (_b = destination.exportedTraceIds) == null ? void 0 : _b.add(traceId);
179
150
  }
180
- function cancelledTerminalMetadata() {
181
- return { [HANDOFF_ATTRIBUTE_SUFFIXES.terminal]: HANDOFF_TERMINAL_CANCELLED };
151
+ function replayTraceUrl(baseUrl) {
152
+ return `${baseUrl.replace(/\/+$/, "")}/v1/traces`;
182
153
  }
183
- function cancelledTerminalAttributes() {
184
- const bare = cancelledTerminalMetadata();
185
- return { ...bare, ...withMetadataPrefix(bare) };
154
+ function getAsyncLocalStorageCtor() {
155
+ return globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
186
156
  }
187
- function toolEventsAllowMetadata() {
188
- return { [TOOL_EVENTS_SUFFIX]: TOOL_EVENTS_ALLOW };
157
+ function getWeakRefCtor() {
158
+ const ctor = globalThis.WeakRef;
159
+ return typeof ctor === "function" ? ctor : void 0;
189
160
  }
190
- function withMetadataPrefix(metadata) {
191
- const out = {};
192
- for (const [key, value] of Object.entries(metadata)) {
193
- out[key.startsWith(AI_SDK_METADATA_PREFIX) ? key : `${AI_SDK_METADATA_PREFIX}${key}`] = value;
161
+ var MAX_BINDING_STACK = 128;
162
+ var EMPTY_STACK = [];
163
+ var ContextStackStorage = class {
164
+ /** Emitted once when this runtime cannot support an ambient push. */
165
+ constructor(noAmbientBindWarning) {
166
+ this.noAmbientBindWarning = noAmbientBindWarning;
167
+ this._als = null;
168
+ this._enterWithUsable = false;
169
+ this._syncStack = EMPTY_STACK;
170
+ this._adoptionChecked = false;
171
+ this._warnedNoAmbientBind = false;
194
172
  }
195
- return out;
196
- }
197
- var BAGGAGE_KEYS = {
198
- eventId: "raindrop-event-id",
199
- childEventId: "raindrop-child-event-id",
200
- name: "raindrop-handoff-name",
201
- convoId: "raindrop-convo-id",
202
- userId: "raindrop-user-id",
203
- // The dispatch span and its trace, carried as fields as well as in
204
- // `traceparent`. Not redundant: OTel's HTTP instrumentations rewrite
205
- // `traceparent` from whatever span is current as the request goes out, so by
206
- // the time the child reads it the ids name the HTTP client span and its trace
207
- // instead of the dispatch. The child would then point its reverse reference at
208
- // a span the launcher's UI does not know as a dispatch. A field states which
209
- // span it means rather than meaning "whoever wrote this header last".
210
- dispatchSpanId: "raindrop-dispatch-span-id",
211
- traceId: "raindrop-trace-id"
212
- };
213
- var TRACEPARENT_HEADER = "traceparent";
214
- var BAGGAGE_HEADER = "baggage";
215
- var W3C_TRACE_ID = /^[0-9a-f]{32}$/;
216
- var W3C_SPAN_ID = /^[0-9a-f]{16}$/;
217
- function isW3CTraceId(value) {
218
- return value !== void 0 && W3C_TRACE_ID.test(value);
219
- }
220
- function isW3CSpanId(value) {
221
- return value !== void 0 && W3C_SPAN_ID.test(value);
222
- }
223
- var warnedNonConformantIds = false;
224
- function warnNonConformantIdsOnce(traceId, spanId) {
225
- if (warnedNonConformantIds) return;
226
- warnedNonConformantIds = true;
227
- console.warn(
228
- `[raindrop] hand-off carrier ids are not W3C-shaped (traceId="${traceId}", spanId="${spanId}"). The child still reports as its own event, but its reverse reference names a span id the backend does not store, so the link back to the launcher will not resolve. A LangSmith carrier reads this way \u2014 its run ids are UUIDs; propagate Raindrop's own carrier (toHeaders) to link.`
229
- );
230
- }
231
- var HANDOFF_HEADER = "x-raindrop-handoff";
232
- var LANGSMITH_TRACE_HEADER = "langsmith-trace";
233
- var LANGSMITH_METADATA_BAGGAGE_KEY = "langsmith-metadata";
234
- function encodeBaggage(entries) {
235
- return Object.entries(entries).filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join(",");
236
- }
237
- function decodeBaggage(header) {
238
- if (!header) return {};
239
- const parsed = {};
240
- for (const part of header.split(",")) {
241
- const [rawKey, ...rest] = part.split("=");
242
- if (!rawKey || rest.length === 0) continue;
243
- const key = rawKey.trim();
244
- let value;
173
+ maybeAdoptAsyncLocalStorage() {
174
+ if (this._adoptionChecked) return;
175
+ this._adoptionChecked = true;
176
+ const Ctor = getAsyncLocalStorageCtor();
177
+ if (!Ctor) return;
178
+ const als = new Ctor();
245
179
  try {
246
- value = decodeURIComponent(rest.join("=").trim());
180
+ als.run(EMPTY_STACK, () => {
181
+ });
247
182
  } catch (e) {
248
- value = rest.join("=").trim();
183
+ return;
184
+ }
185
+ this._als = als;
186
+ if (typeof als.enterWith === "function") {
187
+ try {
188
+ als.enterWith(EMPTY_STACK);
189
+ this._enterWithUsable = true;
190
+ } catch (e) {
191
+ this._enterWithUsable = false;
192
+ }
249
193
  }
250
- if (key in parsed && parsed[key] !== value) throw new ConflictingCarrierError(key);
251
- parsed[key] = value;
252
194
  }
253
- return parsed;
254
- }
255
- function carrierBaggage(carrier) {
256
- return {
257
- [BAGGAGE_KEYS.eventId]: carrier.eventId,
258
- [BAGGAGE_KEYS.childEventId]: carrier.childEventId,
259
- [BAGGAGE_KEYS.name]: carrier.name,
260
- [BAGGAGE_KEYS.convoId]: carrier.convoId,
261
- [BAGGAGE_KEYS.userId]: carrier.userId,
262
- [BAGGAGE_KEYS.dispatchSpanId]: carrier.spanId,
263
- [BAGGAGE_KEYS.traceId]: carrier.traceId
264
- };
265
- }
266
- function toHeaders(carrier) {
267
- const fields = encodeBaggage(carrierBaggage(carrier));
268
- const headers = {
269
- [BAGGAGE_HEADER]: fields,
270
- [HANDOFF_HEADER]: fields
271
- };
272
- if (isW3CTraceId(carrier.traceId) && isW3CSpanId(carrier.spanId)) {
273
- headers[TRACEPARENT_HEADER] = `00-${carrier.traceId}-${carrier.spanId}-01`;
195
+ getStack() {
196
+ var _a;
197
+ this.maybeAdoptAsyncLocalStorage();
198
+ if (this._als) return (_a = this._als.getStore()) != null ? _a : this._syncStack;
199
+ return this._syncStack;
274
200
  }
275
- return headers;
276
- }
277
- function langsmithStamp() {
278
- return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "000000");
279
- }
280
- function toLangSmithHeaders(carrier) {
281
- const stamp = langsmithStamp();
282
- const dottedOrder = `${stamp}Z${carrier.traceId}.${stamp}Z${carrier.spanId}`;
283
- return {
284
- [LANGSMITH_TRACE_HEADER]: dottedOrder,
285
- [BAGGAGE_HEADER]: encodeBaggage({
286
- [LANGSMITH_METADATA_BAGGAGE_KEY]: JSON.stringify(carrierBaggage(carrier))
287
- }),
288
- // Carried here too: a LangSmith-shaped carrier is rewritten in transit
289
- // exactly like the native one, and an injected `traceparent` is preferred
290
- // over the dotted order on read.
291
- [HANDOFF_HEADER]: encodeBaggage(carrierBaggage(carrier))
292
- };
293
- }
294
- function isHeaderLookup(headers) {
295
- return "get" in headers && typeof headers.get === "function";
296
- }
297
- var ConflictingCarrierError = class extends Error {
298
- };
299
- function readHeader(headers, name) {
300
- var _a, _b, _c;
301
- if (isHeaderLookup(headers)) return (_a = headers.get(name)) != null ? _a : void 0;
302
- const value = (_c = headers[name]) != null ? _c : (_b = Object.entries(headers).find(([key]) => key.toLowerCase() === name)) == null ? void 0 : _b[1];
303
- if (!Array.isArray(value)) return value;
304
- const distinct = new Set(value);
305
- if (distinct.size > 1) throw new ConflictingCarrierError(name);
306
- return value[0];
307
- }
308
- function readTraceHeader(headers, name) {
309
- const value = readHeader(headers, name);
310
- if (!(value == null ? void 0 : value.includes(","))) return value;
311
- const copies = value.split(",").map((copy) => copy.trim());
312
- if (new Set(copies).size > 1) throw new ConflictingCarrierError(name);
313
- return copies[0];
314
- }
315
- function fromHeaders(headers) {
316
- var _a, _b;
317
- let baggage;
318
- let handoff;
319
- let traceparent;
320
- let langsmithTrace;
321
- try {
322
- baggage = decodeBaggage(readHeader(headers, BAGGAGE_HEADER));
323
- handoff = decodeBaggage(readHeader(headers, HANDOFF_HEADER));
324
- traceparent = readTraceHeader(headers, TRACEPARENT_HEADER);
325
- langsmithTrace = readTraceHeader(headers, LANGSMITH_TRACE_HEADER);
326
- } catch (error) {
327
- if (error instanceof ConflictingCarrierError) return null;
328
- throw error;
201
+ /**
202
+ * Persist an ambient stack mutation. Returns false when an ALS is adopted but
203
+ * `enterWith` is unusable (workerd): callers must NOT fall back to the
204
+ * process-shared sync stack, since concurrent isolates would cross-stamp.
205
+ */
206
+ setAmbientStack(stack) {
207
+ if (this._als) {
208
+ if (!this._enterWithUsable) return false;
209
+ this._als.enterWith(stack);
210
+ return true;
211
+ }
212
+ this._syncStack = stack;
213
+ return true;
329
214
  }
330
- const langsmithMetadata = baggage[LANGSMITH_METADATA_BAGGAGE_KEY];
331
- const nested = langsmithMetadata ? safeParseStringRecord(langsmithMetadata) : {};
332
- for (const [key, value] of Object.entries(nested)) {
333
- if (key in baggage && baggage[key] !== value) return null;
215
+ push(binding) {
216
+ this.maybeAdoptAsyncLocalStorage();
217
+ if (this._als && !this._enterWithUsable) {
218
+ this.warnNoAmbientBindOnce();
219
+ return;
220
+ }
221
+ let stack = this.getStack();
222
+ if (stack.length >= MAX_BINDING_STACK) {
223
+ stack = stack.filter((b) => b.isLive());
224
+ if (stack.length >= MAX_BINDING_STACK) {
225
+ stack = stack.slice(stack.length - (MAX_BINDING_STACK - 1));
226
+ }
227
+ }
228
+ this.setAmbientStack([...stack, binding]);
334
229
  }
335
- const fields = {
336
- ...baggage,
337
- ...nested,
338
- // Last, so it wins: the standard headers may have been rewritten in transit
339
- // by a propagator, this one is only ever written by us.
340
- ...handoff
341
- };
342
- let traceId;
343
- let spanId;
344
- if (traceparent) {
345
- const [, parsedTraceId, parsedSpanId] = traceparent.split("-");
346
- if (isW3CTraceId(parsedTraceId) && isW3CSpanId(parsedSpanId)) {
347
- traceId = parsedTraceId;
348
- spanId = parsedSpanId;
230
+ remove(binding) {
231
+ binding.markDead();
232
+ const stack = this.getStack();
233
+ const pruned = stack.filter((b) => b !== binding && b.isLive());
234
+ if (pruned.length !== stack.length) {
235
+ this.setAmbientStack(pruned);
349
236
  }
350
237
  }
351
- if (!traceId && !spanId && langsmithTrace) {
352
- const segments = langsmithTrace.split(".");
353
- traceId = (_a = segments[0]) == null ? void 0 : _a.split("Z").pop();
354
- spanId = (_b = segments[segments.length - 1]) == null ? void 0 : _b.split("Z").pop();
238
+ runWith(binding, callback) {
239
+ this.maybeAdoptAsyncLocalStorage();
240
+ if (this._als) {
241
+ return this._als.run([...this.getStack(), binding], callback);
242
+ }
243
+ const previous = this._syncStack;
244
+ this._syncStack = [...previous, binding];
245
+ try {
246
+ return callback();
247
+ } finally {
248
+ this._syncStack = previous;
249
+ }
355
250
  }
356
- traceId = fields[BAGGAGE_KEYS.traceId] || traceId;
357
- spanId = fields[BAGGAGE_KEYS.dispatchSpanId] || spanId;
358
- const eventId = fields[BAGGAGE_KEYS.eventId];
359
- if (!traceId || !spanId || !eventId) return null;
360
- if (!isW3CTraceId(traceId) || !isW3CSpanId(spanId)) {
361
- warnNonConformantIdsOnce(traceId, spanId);
251
+ current() {
252
+ const stack = this.getStack();
253
+ if (stack.length === 0) return void 0;
254
+ const top = stack[stack.length - 1];
255
+ if (top.isLive()) return top;
256
+ const pruned = stack.filter((b) => b.isLive());
257
+ this.setAmbientStack(pruned);
258
+ return pruned.length > 0 ? pruned[pruned.length - 1] : void 0;
362
259
  }
363
- const carrier = { traceId, spanId, eventId };
364
- const childEventId = fields[BAGGAGE_KEYS.childEventId];
365
- if (childEventId) carrier.childEventId = childEventId;
366
- const name = fields[BAGGAGE_KEYS.name];
367
- if (name) carrier.name = name;
368
- const convoId = fields[BAGGAGE_KEYS.convoId];
369
- if (convoId) carrier.convoId = convoId;
370
- const userId = fields[BAGGAGE_KEYS.userId];
371
- if (userId) carrier.userId = userId;
372
- return carrier;
373
- }
374
- var UNRESOLVED_CARRIER_WARNING = `[raindrop] resume: headers were supplied but no hand-off carrier was found on them (searched ${HANDOFF_HEADER}, ${TRACEPARENT_HEADER}, ${BAGGAGE_HEADER}). This run reports as an UNLINKED event, and the launcher that dispatched it will stay on "queued" forever because nothing ever references its child. Passing headers means a parent handed off, so no carrier on them is almost certainly a defect: a gateway or proxy stripping ${HANDOFF_HEADER}, a middleware overwriting ${BAGGAGE_HEADER}, a request forwarded without its headers, or a hand-built carrier whose field names do not match. Send every header the launcher's dispatch returned. Logged once per process; pass no headers for a directly-invoked sub-agent to silence it.`;
375
- var UNRESOLVED_CARRIER_WARNED_KEY = /* @__PURE__ */ Symbol.for("raindrop.handoff.warnedUnresolvedCarrier");
376
- function warnedHolder() {
377
- return globalThis;
260
+ warnNoAmbientBindOnce() {
261
+ if (this._warnedNoAmbientBind) return;
262
+ this._warnedNoAmbientBind = true;
263
+ console.warn(this.noAmbientBindWarning);
264
+ }
265
+ };
266
+ var NO_AMBIENT_ROUTING_BIND_WARNING = "[raindrop] This runtime's AsyncLocalStorage does not implement enterWith (e.g. Cloudflare Workers), so begin()/finish() cannot scope auto-instrumented spans to a project without cross-contaminating concurrent requests. Manual events and explicit span routing are unaffected; use asCurrent()/withSpan (run-based) to route auto-instrumented spans on this runtime.";
267
+ var NO_AMBIENT_ATTRIBUTE_BIND_WARNING = "[raindrop] This runtime's AsyncLocalStorage does not implement enterWith (e.g. Cloudflare Workers), so contextual span attributes (the detached sub-agent hand-off reference) cannot be bound ambiently without cross-contaminating concurrent requests. The child's own event still reports; only the per-span reverse reference is skipped.";
268
+ var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
269
+ function routingStorage() {
270
+ const holder = globalThis;
271
+ let storage = holder[ROUTING_STORAGE_KEY];
272
+ if (!storage) {
273
+ storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
274
+ holder[ROUTING_STORAGE_KEY] = storage;
275
+ }
276
+ return storage;
378
277
  }
379
- function warnUnresolvedCarrier(headers, carrier) {
380
- if (carrier) return;
381
- if (!headers) return;
382
- const holder = warnedHolder();
383
- if (holder[UNRESOLVED_CARRIER_WARNED_KEY]) return;
384
- holder[UNRESOLVED_CARRIER_WARNED_KEY] = true;
385
- console.warn(UNRESOLVED_CARRIER_WARNING);
278
+ function makeBoundContext(binding, owner) {
279
+ const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
280
+ const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
281
+ let dead = false;
282
+ return {
283
+ projectId: binding.projectId,
284
+ authHint: binding.authHint,
285
+ ...binding.appGit ? { appGit: binding.appGit } : {},
286
+ ...binding.appGitSource ? { appGitSource: binding.appGitSource } : {},
287
+ isLive() {
288
+ if (dead) return false;
289
+ return ref === void 0 || ref.deref() !== void 0;
290
+ },
291
+ markDead() {
292
+ dead = true;
293
+ }
294
+ };
386
295
  }
387
- function safeParseStringRecord(raw) {
296
+ function bindRoutingContext(binding, owner) {
297
+ const bound = makeBoundContext(binding, owner);
388
298
  try {
389
- const parsed = JSON.parse(raw);
390
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
391
- const result = {};
392
- for (const [key, value] of Object.entries(parsed)) {
393
- if (typeof value === "string") result[key] = value;
394
- }
395
- return result;
299
+ routingStorage().push(bound);
396
300
  } catch (e) {
397
- return {};
398
301
  }
302
+ return bound;
399
303
  }
400
- function runWithTracingSuppressed(fn) {
401
- const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
402
- if (typeof hook !== "function") return fn();
403
- let started = false;
304
+ function unbindRoutingContext(bound) {
305
+ if (!bound || !isBoundContext(bound)) return;
404
306
  try {
405
- return hook(() => {
406
- started = true;
407
- return fn();
408
- });
409
- } catch (err) {
410
- if (started) throw err;
411
- return fn();
307
+ routingStorage().remove(bound);
308
+ } catch (e) {
412
309
  }
413
310
  }
414
- var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
415
- var MAX_RETRY_DELAY_MS = 3e4;
416
- function wait(ms) {
417
- return new Promise((resolve) => setTimeout(resolve, ms));
311
+ function withRoutingContext(binding, callback) {
312
+ const bound = makeBoundContext(binding);
313
+ return routingStorage().runWith(bound, callback);
418
314
  }
419
- function formatEndpoint(endpoint) {
420
- if (!endpoint) return void 0;
421
- return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
315
+ function currentRoutingBinding() {
316
+ const bound = routingStorage().current();
317
+ if (!bound) return void 0;
318
+ return {
319
+ projectId: bound.projectId,
320
+ authHint: bound.authHint,
321
+ ...bound.appGit ? { appGit: bound.appGit } : {},
322
+ ...bound.appGitSource ? { appGitSource: bound.appGitSource } : {}
323
+ };
422
324
  }
423
- function redactUrlForLog(url) {
424
- try {
425
- const parsed = new URL(url);
426
- parsed.username = "";
427
- parsed.password = "";
428
- parsed.search = "";
429
- parsed.hash = "";
430
- return parsed.toString();
431
- } catch (e) {
432
- return "<unparseable-url>";
433
- }
325
+ function isBoundContext(value) {
326
+ return typeof value.isLive === "function";
434
327
  }
435
- var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
436
- var rateLimitedLogLast = /* @__PURE__ */ new Map();
437
- function rateLimitedLog(key, log) {
438
- const now = Date.now();
439
- const last = rateLimitedLogLast.get(key);
440
- if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
441
- return false;
328
+ var ATTRIBUTE_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.spanAttributeStorage");
329
+ function attributeStorage() {
330
+ const holder = globalThis;
331
+ let storage = holder[ATTRIBUTE_STORAGE_KEY];
332
+ if (!storage) {
333
+ storage = new ContextStackStorage(NO_AMBIENT_ATTRIBUTE_BIND_WARNING);
334
+ holder[ATTRIBUTE_STORAGE_KEY] = storage;
442
335
  }
443
- rateLimitedLogLast.set(key, now);
444
- log();
445
- return true;
446
- }
447
- async function raceWithTimeout(promise, timeoutMs) {
448
- let timer;
449
- const settledInTime = await Promise.race([
450
- promise.then(
451
- () => true,
452
- () => true
453
- ),
454
- new Promise((resolve) => {
455
- var _a;
456
- timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
457
- (_a = timer.unref) == null ? void 0 : _a.call(timer);
458
- })
459
- ]);
460
- if (timer) clearTimeout(timer);
461
- return settledInTime;
336
+ return storage;
462
337
  }
463
- function parseRetryAfter(headers) {
464
- var _a;
465
- const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
466
- if (!value) return void 0;
467
- const asNumber = Number(value);
468
- if (value.trim() !== "" && !Number.isNaN(asNumber)) return asNumber * 1e3;
469
- const asDate = new Date(value).getTime();
470
- if (!Number.isNaN(asDate)) {
471
- const delta = asDate - Date.now();
472
- return delta > 0 ? delta : 0;
338
+ function makeAttributeFrame(attributes, owner) {
339
+ const snapshot = { ...attributes };
340
+ const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
341
+ const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
342
+ const owned = /* @__PURE__ */ new WeakSet();
343
+ let dead = false;
344
+ let sawError = false;
345
+ return {
346
+ attributes: snapshot,
347
+ sawErrorSpan: () => sawError,
348
+ ownsSpan: (span) => owned.has(span),
349
+ claimSpan(span) {
350
+ owned.add(span);
351
+ },
352
+ markErrorSpanSeen() {
353
+ sawError = true;
354
+ },
355
+ // A frame whose owner has been collected is dead even though nothing
356
+ // unbound it: a run that never settles must not keep handing its reverse
357
+ // reference to whatever runs next on a reused context.
358
+ isLive() {
359
+ if (dead) return false;
360
+ return ref === void 0 || ref.deref() !== void 0;
361
+ },
362
+ markDead() {
363
+ dead = true;
364
+ }
365
+ };
366
+ }
367
+ function bindSpanAttributes(attributes, owner) {
368
+ if (Object.keys(attributes).length === 0) return void 0;
369
+ const frame = makeAttributeFrame(attributes, owner);
370
+ try {
371
+ attributeStorage().push(frame);
372
+ } catch (e) {
373
+ return void 0;
473
374
  }
474
- return void 0;
375
+ return frame;
475
376
  }
476
- function getRetryDelayMs(attemptNumber, previousError) {
477
- if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
478
- const v = previousError.retryAfterMs;
479
- if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
377
+ function unbindSpanAttributes(frame) {
378
+ if (!frame || !isAttributeFrame(frame)) return;
379
+ try {
380
+ attributeStorage().remove(frame);
381
+ } catch (e) {
480
382
  }
481
- if (attemptNumber <= 1) return 0;
482
- const base = 500;
483
- const factor = Math.pow(2, attemptNumber - 2);
484
- return Math.min(base * factor, MAX_RETRY_DELAY_MS);
485
383
  }
486
- async function withRetry(operation, opName, opts) {
487
- const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
488
- let lastError = void 0;
489
- for (let attemptNumber = 1; attemptNumber <= opts.maxAttempts; attemptNumber++) {
490
- if (attemptNumber > 1) {
491
- const delay = getRetryDelayMs(attemptNumber, lastError);
492
- if (opts.debug) {
493
- console.warn(
494
- `${prefix} ${opName} retry ${attemptNumber}/${opts.maxAttempts} in ${delay}ms`
495
- );
496
- }
497
- if (delay > 0) await wait(delay);
498
- } else if (opts.debug) {
499
- console.log(`${prefix} ${opName} attempt ${attemptNumber}/${opts.maxAttempts}`);
500
- }
501
- try {
502
- return await operation();
503
- } catch (err) {
504
- lastError = err;
505
- if (opts.debug) {
506
- const msg = err instanceof Error ? err.message : String(err);
507
- console.warn(
508
- `${prefix} ${opName} attempt ${attemptNumber} failed: ${msg}${attemptNumber === opts.maxAttempts ? " (no more retries)" : ""}`
509
- );
384
+ function applySpanAttributeFrames(span, setAttribute) {
385
+ try {
386
+ for (const frame of attributeStorage().getStack()) {
387
+ if (!frame.isLive()) continue;
388
+ frame.claimSpan(span);
389
+ for (const [key, value] of Object.entries(frame.attributes)) {
390
+ setAttribute(key, value);
510
391
  }
511
- if (lastError && typeof lastError === "object" && "retryable" in lastError && !lastError.retryable)
512
- break;
513
- if (attemptNumber === opts.maxAttempts) break;
514
392
  }
393
+ } catch (e) {
515
394
  }
516
- throw lastError instanceof Error ? lastError : new Error(String(lastError));
517
- }
518
- async function postJson(url, body, headers, opts) {
519
- var _a;
520
- const opName = `POST ${redactUrlForLog(url)}`;
521
- const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
522
- await withRetry(
523
- async () => {
524
- const resp = await runWithTracingSuppressed(
525
- () => fetch(url, {
526
- method: "POST",
527
- redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
528
- headers: {
529
- "Content-Type": "application/json",
530
- ...headers
531
- },
532
- body: JSON.stringify(body),
533
- signal: AbortSignal.timeout(timeoutMs)
534
- })
535
- );
536
- if (!resp.ok) {
537
- const text = await resp.text().catch(() => "");
538
- const err = new Error(
539
- `HTTP ${resp.status} ${resp.statusText}${text ? `: ${text}` : ""}`
540
- );
541
- const retryAfterMs = parseRetryAfter(resp.headers);
542
- if (typeof retryAfterMs === "number") err.retryAfterMs = retryAfterMs;
543
- err.retryable = resp.status === 429 || resp.status >= 500;
544
- throw err;
545
- }
546
- },
547
- opName,
548
- opts
549
- );
550
395
  }
551
- var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
552
- var TRUNCATION_MARKER = "...[truncated by raindrop]";
553
- var BOUNDED_CLONE_MAX_DEPTH = 12;
554
- var BOUNDED_CLONE_SLACK = 256;
555
- var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
556
- function truncateToLimit(text, limit) {
557
- if (limit > TRUNCATION_MARKER.length) {
558
- return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
396
+ function claimSpanForFrames(span) {
397
+ try {
398
+ for (const frame of attributeStorage().getStack()) {
399
+ if (frame.isLive()) frame.claimSpan(span);
400
+ }
401
+ } catch (e) {
559
402
  }
560
- return text.slice(0, Math.max(0, limit));
561
403
  }
562
- function capText(value, limit) {
563
- if (typeof value !== "string") return value;
564
- const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
565
- if (value.length <= max) return value;
566
- return truncateToLimit(value, max);
567
- }
568
- function boundedClone(obj, charBudget) {
569
- let budget = charBudget;
570
- const seen = /* @__PURE__ */ new WeakSet();
571
- const walk = (node, depth) => {
572
- if (budget <= 0) return TRUNCATION_MARKER;
573
- if (typeof node === "string") {
574
- if (node.length > budget) {
575
- const taken = node.slice(0, Math.max(0, budget)) + TRUNCATION_MARKER;
576
- budget = 0;
577
- return taken;
578
- }
579
- budget -= Math.max(node.length, 1);
580
- return node;
581
- }
582
- if (node === null || typeof node === "number" || typeof node === "boolean") {
583
- budget -= 8;
584
- return node;
585
- }
586
- if (typeof node !== "object") {
587
- budget -= 8;
588
- return node;
589
- }
590
- if (seen.has(node)) return "[CIRCULAR]";
591
- if (depth >= BOUNDED_CLONE_MAX_DEPTH) {
592
- budget -= 16;
593
- return `<max depth>`;
594
- }
595
- seen.add(node);
596
- if (Array.isArray(node)) {
597
- const out2 = [];
598
- for (const v of node) {
599
- if (budget <= 0) {
600
- out2.push(TRUNCATION_MARKER);
601
- break;
602
- }
603
- out2.push(walk(v, depth + 1));
604
- }
605
- return out2;
606
- }
607
- if (typeof node.toJSON === "function") {
608
- budget -= 16;
609
- return node;
404
+ function currentSpanAttributes() {
405
+ const merged = {};
406
+ try {
407
+ for (const frame of attributeStorage().getStack()) {
408
+ if (!frame.isLive()) continue;
409
+ Object.assign(merged, frame.attributes);
610
410
  }
611
- const out = {};
612
- for (const k in node) {
613
- if (!Object.prototype.hasOwnProperty.call(node, k)) continue;
614
- if (budget <= 0) {
615
- out["..."] = TRUNCATION_MARKER;
616
- break;
617
- }
618
- const key = walk(k, depth + 1);
619
- out[key] = walk(node[k], depth + 1);
411
+ } catch (e) {
412
+ }
413
+ return merged;
414
+ }
415
+ function markErrorSpanSeen(span) {
416
+ try {
417
+ for (const frame of attributeStorage().getStack()) {
418
+ if (frame.ownsSpan(span)) frame.markErrorSpanSeen();
620
419
  }
621
- return out;
622
- };
623
- return walk(obj, 0);
420
+ } catch (e) {
421
+ }
624
422
  }
625
- function stringifyBounded(value, limit) {
626
- const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
627
- if (typeof value === "string") {
628
- return capText(value, max);
423
+ function isAttributeFrame(value) {
424
+ return typeof value.isLive === "function";
425
+ }
426
+ var APP_COMMIT_SHA = "raindrop.app.commit_sha";
427
+ var APP_COMMIT_DIRTY = "raindrop.app.commit_dirty";
428
+ var APP_BRANCH = "raindrop.app.branch";
429
+ var APP_GIT_KEYS = [APP_COMMIT_SHA, APP_COMMIT_DIRTY, APP_BRANCH];
430
+ var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
431
+ function withAppGitProperties(properties, snapshot) {
432
+ try {
433
+ const merged = { ...properties };
434
+ for (const key of APP_GIT_KEYS)
435
+ if (!own(merged, key) && own(snapshot, key)) merged[key] = snapshot[key];
436
+ return merged;
437
+ } catch (e) {
438
+ return properties != null ? properties : {};
629
439
  }
630
- const pruned = boundedClone(value, max + TRUNCATION_MARKER.length + BOUNDED_CLONE_SLACK);
631
- let json;
440
+ }
441
+ function withAppGitAttributes(attributes, snapshot) {
632
442
  try {
633
- json = JSON.stringify(pruned);
443
+ const present = new Set(attributes.flatMap((attr) => attr ? [attr.key] : []));
444
+ const added = [];
445
+ for (const key of APP_GIT_KEYS) {
446
+ if (present.has(key) || present.has(`ai.telemetry.metadata.${key}`) || present.has(`traceloop.association.properties.${key}`))
447
+ continue;
448
+ const value = snapshot[key];
449
+ if (typeof value === "string") added.push({ key, value: { stringValue: value } });
450
+ else if (typeof value === "boolean") added.push({ key, value: { boolValue: value } });
451
+ }
452
+ return added.length ? [...attributes, ...added] : attributes;
634
453
  } catch (e) {
635
- json = void 0;
454
+ return attributes;
636
455
  }
637
- if (json === void 0) {
638
- return capText(String(value), max);
456
+ }
457
+ function normalizeFeatureFlags(input) {
458
+ if (Array.isArray(input)) {
459
+ return Object.fromEntries(input.map((name) => [name, "true"]));
639
460
  }
640
- return json.length <= max ? json : truncateToLimit(json, max);
461
+ return Object.fromEntries(
462
+ Object.entries(input).map(([k, v]) => [k, String(v)])
463
+ );
641
464
  }
642
- function attrString(key, value) {
643
- if (value === void 0) return void 0;
644
- return { key, value: { stringValue: value } };
465
+ function getCrypto() {
466
+ const c = globalThis.crypto;
467
+ return c;
645
468
  }
646
- function attrInt(key, value) {
647
- if (value === void 0) return void 0;
648
- if (!Number.isFinite(value)) return void 0;
649
- return { key, value: { intValue: String(Math.trunc(value)) } };
650
- }
651
- function attrDouble(key, value) {
652
- if (value === void 0) return void 0;
653
- if (!Number.isFinite(value)) return void 0;
654
- return { key, value: { doubleValue: value } };
469
+ function randomBytes(length) {
470
+ const cryptoObj = getCrypto();
471
+ const out = new Uint8Array(length);
472
+ if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
473
+ cryptoObj.getRandomValues(out);
474
+ return out;
475
+ }
476
+ for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
477
+ return out;
655
478
  }
656
- var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
657
- function state() {
658
- const holder = globalThis;
659
- let current = holder[STATE_KEY];
660
- if (!current) {
661
- let storage = null;
662
- try {
663
- const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
664
- if (Ctor) storage = new Ctor();
665
- } catch (e) {
666
- storage = null;
667
- }
668
- current = {
669
- storage,
670
- syncDestination: void 0,
671
- spanDestinations: /* @__PURE__ */ new WeakMap()
672
- };
673
- holder[STATE_KEY] = current;
479
+ function randomUUID() {
480
+ const cryptoObj = getCrypto();
481
+ if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
482
+ return cryptoObj.randomUUID();
674
483
  }
675
- return current;
484
+ const b = randomBytes(16);
485
+ b[6] = b[6] & 15 | 64;
486
+ b[8] = b[8] & 63 | 128;
487
+ const hex = [...b].map((x) => x.toString(16).padStart(2, "0")).join("");
488
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
676
489
  }
677
- function currentReplayTraceDestination() {
678
- var _a, _b;
679
- const routing = state();
680
- return (_b = (_a = routing.storage) == null ? void 0 : _a.getStore()) != null ? _b : routing.syncDestination;
490
+ var HANDOFF_ATTRIBUTE_SUFFIXES = {
491
+ mode: "raindrop.handoff.mode",
492
+ childEventId: "raindrop.handoff.childEventId",
493
+ parentEventId: "raindrop.handoff.parentEventId",
494
+ parentSpanId: "raindrop.handoff.parentSpanId",
495
+ name: "raindrop.handoff.name",
496
+ terminal: "raindrop.handoff.terminal",
497
+ agentRole: "raindrop.agent.role"
498
+ };
499
+ var AI_SDK_METADATA_PREFIX = "ai.telemetry.metadata.";
500
+ var DETACHED_MODE = "detached";
501
+ var SUBAGENT_AGENT_ROLE = "subagent";
502
+ var HANDOFF_TERMINAL_CANCELLED = "cancelled";
503
+ var TOOL_EVENTS_SUFFIX = "raindrop.toolEvents";
504
+ var TOOL_EVENTS_ALLOW = "allow";
505
+ var OPERATION_ID_ATTRIBUTE = "ai.operationId";
506
+ var SUBAGENT_ABORT_OPERATION_ID = "ai.subagent.failed";
507
+ var SUBAGENT_ABORT_SPAN_NAME = SUBAGENT_ABORT_OPERATION_ID;
508
+ var DEFAULT_DISPATCH_SPAN_NAME = "launch_subagent";
509
+ var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
510
+ function defined(entries) {
511
+ const out = {};
512
+ for (const [key, value] of Object.entries(entries)) {
513
+ if (typeof value === "string" && value.trim().length > 0) out[key] = value;
514
+ }
515
+ return out;
681
516
  }
682
- function rememberReplayTraceDestination(span) {
683
- const destination = currentReplayTraceDestination();
684
- if (destination) state().spanDestinations.set(span, destination);
517
+ function detachedDispatchMetadata(dispatch) {
518
+ return defined({
519
+ [HANDOFF_ATTRIBUTE_SUFFIXES.mode]: DETACHED_MODE,
520
+ [HANDOFF_ATTRIBUTE_SUFFIXES.childEventId]: dispatch.childEventId,
521
+ [HANDOFF_ATTRIBUTE_SUFFIXES.name]: dispatch.name
522
+ });
685
523
  }
686
- function copyReplayTraceDestination(source, target) {
687
- const destination = state().spanDestinations.get(source);
688
- if (destination) state().spanDestinations.set(target, destination);
524
+ function detachedChildMetadata(link) {
525
+ return defined({
526
+ [HANDOFF_ATTRIBUTE_SUFFIXES.parentEventId]: link.parentEventId,
527
+ [HANDOFF_ATTRIBUTE_SUFFIXES.parentSpanId]: link.parentSpanId,
528
+ [HANDOFF_ATTRIBUTE_SUFFIXES.name]: link.name,
529
+ [HANDOFF_ATTRIBUTE_SUFFIXES.agentRole]: SUBAGENT_AGENT_ROLE
530
+ });
689
531
  }
690
- function replayTraceDestinationFor(value) {
691
- if (typeof value !== "object" && typeof value !== "function" || value === null) {
692
- return void 0;
693
- }
694
- return state().spanDestinations.get(value);
532
+ function cancelledTerminalMetadata() {
533
+ return { [HANDOFF_ATTRIBUTE_SUFFIXES.terminal]: HANDOFF_TERMINAL_CANCELLED };
695
534
  }
696
- function recordReplayExportedSpan(destination, spanId, traceId) {
697
- var _a, _b;
698
- if (!destination || !spanId || !traceId) return;
699
- (_a = destination.exportedSpanIds) == null ? void 0 : _a.add(spanId);
700
- (_b = destination.exportedTraceIds) == null ? void 0 : _b.add(traceId);
535
+ function cancelledTerminalAttributes() {
536
+ const bare = cancelledTerminalMetadata();
537
+ return { ...bare, ...withMetadataPrefix(bare) };
701
538
  }
702
- function replayTraceUrl(baseUrl) {
703
- return `${baseUrl.replace(/\/+$/, "")}/v1/traces`;
539
+ function toolEventsAllowMetadata() {
540
+ return { [TOOL_EVENTS_SUFFIX]: TOOL_EVENTS_ALLOW };
704
541
  }
705
- var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
706
- var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
707
- var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
708
- function readEnvVar(name) {
709
- var _a;
710
- try {
711
- const env = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env;
712
- if (env && typeof env[name] === "string" && env[name].length > 0) {
713
- return env[name];
714
- }
715
- } catch (e) {
542
+ function withMetadataPrefix(metadata) {
543
+ const out = {};
544
+ for (const [key, value] of Object.entries(metadata)) {
545
+ out[key.startsWith(AI_SDK_METADATA_PREFIX) ? key : `${AI_SDK_METADATA_PREFIX}${key}`] = value;
716
546
  }
717
- return void 0;
547
+ return out;
718
548
  }
719
- function readWorkshopEnv() {
720
- const raw = readEnvVar(WORKSHOP_ENV_VAR);
721
- if (raw === void 0) return void 0;
722
- const trimmed = raw.trim();
723
- if (trimmed.length === 0) return void 0;
724
- if (/^https?:\/\//i.test(trimmed)) return { url: trimmed };
725
- if (/^(1|true|yes|on)$/i.test(trimmed)) return "enable";
726
- if (/^(0|false|no|off)$/i.test(trimmed)) return "disable";
727
- return void 0;
549
+ var BAGGAGE_KEYS = {
550
+ eventId: "raindrop-event-id",
551
+ childEventId: "raindrop-child-event-id",
552
+ name: "raindrop-handoff-name",
553
+ convoId: "raindrop-convo-id",
554
+ userId: "raindrop-user-id",
555
+ // The dispatch span and its trace, carried as fields as well as in
556
+ // `traceparent`. Not redundant: OTel's HTTP instrumentations rewrite
557
+ // `traceparent` from whatever span is current as the request goes out, so by
558
+ // the time the child reads it the ids name the HTTP client span and its trace
559
+ // instead of the dispatch. The child would then point its reverse reference at
560
+ // a span the launcher's UI does not know as a dispatch. A field states which
561
+ // span it means rather than meaning "whoever wrote this header last".
562
+ dispatchSpanId: "raindrop-dispatch-span-id",
563
+ traceId: "raindrop-trace-id"
564
+ };
565
+ var TRACEPARENT_HEADER = "traceparent";
566
+ var BAGGAGE_HEADER = "baggage";
567
+ var W3C_TRACE_ID = /^[0-9a-f]{32}$/;
568
+ var W3C_SPAN_ID = /^[0-9a-f]{16}$/;
569
+ function isW3CTraceId(value) {
570
+ return value !== void 0 && W3C_TRACE_ID.test(value);
728
571
  }
729
- function isLocalDevHost(hostname) {
730
- if (!hostname) return false;
731
- if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1") {
732
- return true;
733
- }
734
- if (hostname.endsWith(".localhost")) return true;
735
- return false;
572
+ function isW3CSpanId(value) {
573
+ return value !== void 0 && W3C_SPAN_ID.test(value);
736
574
  }
737
- function readRuntimeHostname() {
738
- try {
739
- const loc = globalThis == null ? void 0 : globalThis.location;
740
- if (loc && typeof loc.hostname === "string" && loc.hostname.length > 0) {
741
- return loc.hostname;
575
+ var warnedNonConformantIds = false;
576
+ function warnNonConformantIdsOnce(traceId, spanId) {
577
+ if (warnedNonConformantIds) return;
578
+ warnedNonConformantIds = true;
579
+ console.warn(
580
+ `[raindrop] hand-off carrier ids are not W3C-shaped (traceId="${traceId}", spanId="${spanId}"). The child still reports as its own event, but its reverse reference names a span id the backend does not store, so the link back to the launcher will not resolve. A LangSmith carrier reads this way \u2014 its run ids are UUIDs; propagate Raindrop's own carrier (toHeaders) to link.`
581
+ );
582
+ }
583
+ var HANDOFF_HEADER = "x-raindrop-handoff";
584
+ var LANGSMITH_TRACE_HEADER = "langsmith-trace";
585
+ var LANGSMITH_METADATA_BAGGAGE_KEY = "langsmith-metadata";
586
+ function encodeBaggage(entries) {
587
+ return Object.entries(entries).filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join(",");
588
+ }
589
+ function decodeBaggage(header) {
590
+ if (!header) return {};
591
+ const parsed = {};
592
+ for (const part of header.split(",")) {
593
+ const [rawKey, ...rest] = part.split("=");
594
+ if (!rawKey || rest.length === 0) continue;
595
+ const key = rawKey.trim();
596
+ let value;
597
+ try {
598
+ value = decodeURIComponent(rest.join("=").trim());
599
+ } catch (e) {
600
+ value = rest.join("=").trim();
742
601
  }
743
- } catch (e) {
602
+ if (key in parsed && parsed[key] !== value) throw new ConflictingCarrierError(key);
603
+ parsed[key] = value;
744
604
  }
745
- return void 0;
605
+ return parsed;
746
606
  }
747
- function shouldAutoEnableLocalWorkshop() {
748
- if (isLocalDevHost(readRuntimeHostname())) return true;
749
- if (readEnvVar("NODE_ENV") === "development") return true;
750
- return false;
607
+ function carrierBaggage(carrier) {
608
+ return {
609
+ [BAGGAGE_KEYS.eventId]: carrier.eventId,
610
+ [BAGGAGE_KEYS.childEventId]: carrier.childEventId,
611
+ [BAGGAGE_KEYS.name]: carrier.name,
612
+ [BAGGAGE_KEYS.convoId]: carrier.convoId,
613
+ [BAGGAGE_KEYS.userId]: carrier.userId,
614
+ [BAGGAGE_KEYS.dispatchSpanId]: carrier.spanId,
615
+ [BAGGAGE_KEYS.traceId]: carrier.traceId
616
+ };
751
617
  }
752
- function resolveLocalDebuggerBaseUrl(baseUrl) {
753
- var _a, _b, _c;
754
- if (baseUrl === null) return null;
755
- if (typeof baseUrl === "string" && baseUrl.length > 0) {
756
- return (_a = formatEndpoint(baseUrl)) != null ? _a : null;
618
+ function toHeaders(carrier) {
619
+ const fields = encodeBaggage(carrierBaggage(carrier));
620
+ const headers = {
621
+ [BAGGAGE_HEADER]: fields,
622
+ [HANDOFF_HEADER]: fields
623
+ };
624
+ if (isW3CTraceId(carrier.traceId) && isW3CSpanId(carrier.spanId)) {
625
+ headers[TRACEPARENT_HEADER] = `00-${carrier.traceId}-${carrier.spanId}-01`;
757
626
  }
758
- const explicitUrlEnv = readEnvVar(LOCAL_DEBUGGER_ENV_VAR);
759
- if (explicitUrlEnv) return (_b = formatEndpoint(explicitUrlEnv)) != null ? _b : null;
760
- const workshopEnv = readWorkshopEnv();
761
- if (workshopEnv === "disable") return null;
762
- if (workshopEnv === "enable") return DEFAULT_LOCAL_WORKSHOP_URL;
763
- if (workshopEnv && "url" in workshopEnv) return (_c = formatEndpoint(workshopEnv.url)) != null ? _c : null;
764
- if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
765
- return null;
766
- }
767
- function localDebuggerEnabled(baseUrl) {
768
- return resolveLocalDebuggerBaseUrl(baseUrl) !== null;
627
+ return headers;
769
628
  }
770
- var LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS = 2e3;
771
- function mirrorTraceExportToLocalDebugger(body, options = {}) {
772
- var _a;
773
- const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
774
- if (!baseUrl) return Promise.resolve();
775
- return postJson(`${baseUrl}traces`, body, {}, {
776
- maxAttempts: 1,
777
- debug: (_a = options.debug) != null ? _a : false,
778
- sdkName: options.sdkName,
779
- timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
780
- }).catch(() => {
781
- });
629
+ function langsmithStamp() {
630
+ return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "000000");
782
631
  }
783
- function sendLocalDebuggerLiveEvent(event, options = {}) {
632
+ function toLangSmithHeaders(carrier) {
633
+ const stamp = langsmithStamp();
634
+ const dottedOrder = `${stamp}Z${carrier.traceId}.${stamp}Z${carrier.spanId}`;
635
+ return {
636
+ [LANGSMITH_TRACE_HEADER]: dottedOrder,
637
+ [BAGGAGE_HEADER]: encodeBaggage({
638
+ [LANGSMITH_METADATA_BAGGAGE_KEY]: JSON.stringify(carrierBaggage(carrier))
639
+ }),
640
+ // Carried here too: a LangSmith-shaped carrier is rewritten in transit
641
+ // exactly like the native one, and an injected `traceparent` is preferred
642
+ // over the dotted order on read.
643
+ [HANDOFF_HEADER]: encodeBaggage(carrierBaggage(carrier))
644
+ };
645
+ }
646
+ function isHeaderLookup(headers) {
647
+ return "get" in headers && typeof headers.get === "function";
648
+ }
649
+ var ConflictingCarrierError = class extends Error {
650
+ };
651
+ function readHeader(headers, name) {
652
+ var _a, _b, _c;
653
+ if (isHeaderLookup(headers)) return (_a = headers.get(name)) != null ? _a : void 0;
654
+ const value = (_c = headers[name]) != null ? _c : (_b = Object.entries(headers).find(([key]) => key.toLowerCase() === name)) == null ? void 0 : _b[1];
655
+ if (!Array.isArray(value)) return value;
656
+ const distinct = new Set(value);
657
+ if (distinct.size > 1) throw new ConflictingCarrierError(name);
658
+ return value[0];
659
+ }
660
+ function readTraceHeader(headers, name) {
661
+ const value = readHeader(headers, name);
662
+ if (!(value == null ? void 0 : value.includes(","))) return value;
663
+ const copies = value.split(",").map((copy) => copy.trim());
664
+ if (new Set(copies).size > 1) throw new ConflictingCarrierError(name);
665
+ return copies[0];
666
+ }
667
+ function fromHeaders(headers) {
784
668
  var _a, _b;
785
- const destination = currentReplayTraceDestination();
786
- if (destination && destination.kind !== "workshop") return;
787
- const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
788
- if (!baseUrl) return;
789
- void postJson(
790
- `${baseUrl}live`,
791
- {
792
- ...event,
793
- type: event.type,
794
- timestamp: (_a = event.timestamp) != null ? _a : Date.now()
795
- },
796
- {},
797
- {
798
- maxAttempts: 1,
799
- debug: (_b = options.debug) != null ? _b : false,
800
- sdkName: options.sdkName
669
+ let baggage;
670
+ let handoff;
671
+ let traceparent;
672
+ let langsmithTrace;
673
+ try {
674
+ baggage = decodeBaggage(readHeader(headers, BAGGAGE_HEADER));
675
+ handoff = decodeBaggage(readHeader(headers, HANDOFF_HEADER));
676
+ traceparent = readTraceHeader(headers, TRACEPARENT_HEADER);
677
+ langsmithTrace = readTraceHeader(headers, LANGSMITH_TRACE_HEADER);
678
+ } catch (error) {
679
+ if (error instanceof ConflictingCarrierError) return null;
680
+ throw error;
681
+ }
682
+ const langsmithMetadata = baggage[LANGSMITH_METADATA_BAGGAGE_KEY];
683
+ const nested = langsmithMetadata ? safeParseStringRecord(langsmithMetadata) : {};
684
+ for (const [key, value] of Object.entries(nested)) {
685
+ if (key in baggage && baggage[key] !== value) return null;
686
+ }
687
+ const fields = {
688
+ ...baggage,
689
+ ...nested,
690
+ // Last, so it wins: the standard headers may have been rewritten in transit
691
+ // by a propagator, this one is only ever written by us.
692
+ ...handoff
693
+ };
694
+ let traceId;
695
+ let spanId;
696
+ if (traceparent) {
697
+ const [, parsedTraceId, parsedSpanId] = traceparent.split("-");
698
+ if (isW3CTraceId(parsedTraceId) && isW3CSpanId(parsedSpanId)) {
699
+ traceId = parsedTraceId;
700
+ spanId = parsedSpanId;
801
701
  }
802
- ).catch(() => {
803
- });
702
+ }
703
+ if (!traceId && !spanId && langsmithTrace) {
704
+ const segments = langsmithTrace.split(".");
705
+ traceId = (_a = segments[0]) == null ? void 0 : _a.split("Z").pop();
706
+ spanId = (_b = segments[segments.length - 1]) == null ? void 0 : _b.split("Z").pop();
707
+ }
708
+ traceId = fields[BAGGAGE_KEYS.traceId] || traceId;
709
+ spanId = fields[BAGGAGE_KEYS.dispatchSpanId] || spanId;
710
+ const eventId = fields[BAGGAGE_KEYS.eventId];
711
+ if (!traceId || !spanId || !eventId) return null;
712
+ if (!isW3CTraceId(traceId) || !isW3CSpanId(spanId)) {
713
+ warnNonConformantIdsOnce(traceId, spanId);
714
+ }
715
+ const carrier = { traceId, spanId, eventId };
716
+ const childEventId = fields[BAGGAGE_KEYS.childEventId];
717
+ if (childEventId) carrier.childEventId = childEventId;
718
+ const name = fields[BAGGAGE_KEYS.name];
719
+ if (name) carrier.name = name;
720
+ const convoId = fields[BAGGAGE_KEYS.convoId];
721
+ if (convoId) carrier.convoId = convoId;
722
+ const userId = fields[BAGGAGE_KEYS.userId];
723
+ if (userId) carrier.userId = userId;
724
+ return carrier;
804
725
  }
805
- var PROJECT_ID_HEADER = "X-Raindrop-Project-Id";
806
- function projectIdHeaders(projectId) {
807
- return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
726
+ var UNRESOLVED_CARRIER_WARNING = `[raindrop] resume: headers were supplied but no hand-off carrier was found on them (searched ${HANDOFF_HEADER}, ${TRACEPARENT_HEADER}, ${BAGGAGE_HEADER}). This run reports as an UNLINKED event, and the launcher that dispatched it will stay on "queued" forever because nothing ever references its child. Passing headers means a parent handed off, so no carrier on them is almost certainly a defect: a gateway or proxy stripping ${HANDOFF_HEADER}, a middleware overwriting ${BAGGAGE_HEADER}, a request forwarded without its headers, or a hand-built carrier whose field names do not match. Send every header the launcher's dispatch returned. Logged once per process; pass no headers for a directly-invoked sub-agent to silence it.`;
727
+ var UNRESOLVED_CARRIER_WARNED_KEY = /* @__PURE__ */ Symbol.for("raindrop.handoff.warnedUnresolvedCarrier");
728
+ function warnedHolder() {
729
+ return globalThis;
808
730
  }
809
- var SHUTDOWN_DEADLINE_MS = 1e4;
810
- var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
811
- var MODEL_USAGE_ATTRIBUTES = {
812
- usageSource: "raindrop.usage.source",
813
- providerName: "gen_ai.provider.name",
814
- requestModel: "gen_ai.request.model",
815
- responseModel: "gen_ai.response.model",
816
- inputTokens: "gen_ai.usage.input_tokens",
817
- outputTokens: "gen_ai.usage.output_tokens",
818
- reasoningTokens: "gen_ai.usage.reasoning_tokens",
819
- cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
820
- cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
821
- providerReportedCost: "gen_ai.usage.provider_reported_cost",
822
- nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
823
- nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
824
- totalOutputTokens: "raindrop.usage.output_tokens.total",
825
- reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
826
- cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
827
- cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
828
- };
829
- var MODEL_PROVIDER_NAMES = [
830
- ["google.vertex.anthropic", "google-vertex"],
831
- ["google.vertex_ai", "google-vertex"],
832
- ["google.vertex", "google-vertex"],
833
- ["gcp.vertex_ai", "google-vertex"],
834
- ["gcp.gemini", "google"],
835
- ["google_vertexai", "google-vertex"],
836
- ["vertex.anthropic", "google-vertex"],
837
- ["amazon-bedrock", "amazon-bedrock"],
838
- ["aws.bedrock", "amazon-bedrock"],
839
- ["bedrock", "amazon-bedrock"],
840
- ["anthropic", "anthropic"],
841
- ["openai", "openai"],
842
- ["google", "google"],
843
- ["az.ai.openai", "azure"],
844
- ["azure", "azure"],
845
- ["openrouter", "openrouter"]
846
- ];
847
- function canonicalModelProvider(provider) {
731
+ function warnUnresolvedCarrier(headers, carrier) {
732
+ if (carrier) return;
733
+ if (!headers) return;
734
+ const holder = warnedHolder();
735
+ if (holder[UNRESOLVED_CARRIER_WARNED_KEY]) return;
736
+ holder[UNRESOLVED_CARRIER_WARNED_KEY] = true;
737
+ console.warn(UNRESOLVED_CARRIER_WARNING);
738
+ }
739
+ function safeParseStringRecord(raw) {
740
+ try {
741
+ const parsed = JSON.parse(raw);
742
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
743
+ const result = {};
744
+ for (const [key, value] of Object.entries(parsed)) {
745
+ if (typeof value === "string") result[key] = value;
746
+ }
747
+ return result;
748
+ } catch (e) {
749
+ return {};
750
+ }
751
+ }
752
+ function runWithTracingSuppressed(fn) {
753
+ const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
754
+ if (typeof hook !== "function") return fn();
755
+ let started = false;
756
+ try {
757
+ return hook(() => {
758
+ started = true;
759
+ return fn();
760
+ });
761
+ } catch (err) {
762
+ if (started) throw err;
763
+ return fn();
764
+ }
765
+ }
766
+ var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
767
+ var MAX_RETRY_DELAY_MS = 3e4;
768
+ function wait(ms) {
769
+ return new Promise((resolve) => setTimeout(resolve, ms));
770
+ }
771
+ function formatEndpoint(endpoint) {
772
+ if (!endpoint) return void 0;
773
+ return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
774
+ }
775
+ function redactUrlForLog(url) {
776
+ try {
777
+ const parsed = new URL(url);
778
+ parsed.username = "";
779
+ parsed.password = "";
780
+ parsed.search = "";
781
+ parsed.hash = "";
782
+ return parsed.toString();
783
+ } catch (e) {
784
+ return "<unparseable-url>";
785
+ }
786
+ }
787
+ var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
788
+ var rateLimitedLogLast = /* @__PURE__ */ new Map();
789
+ function rateLimitedLog(key, log) {
790
+ const now = Date.now();
791
+ const last = rateLimitedLogLast.get(key);
792
+ if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
793
+ return false;
794
+ }
795
+ rateLimitedLogLast.set(key, now);
796
+ log();
797
+ return true;
798
+ }
799
+ async function raceWithTimeout(promise, timeoutMs) {
800
+ let timer;
801
+ const settledInTime = await Promise.race([
802
+ promise.then(
803
+ () => true,
804
+ () => true
805
+ ),
806
+ new Promise((resolve) => {
807
+ var _a;
808
+ timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
809
+ (_a = timer.unref) == null ? void 0 : _a.call(timer);
810
+ })
811
+ ]);
812
+ if (timer) clearTimeout(timer);
813
+ return settledInTime;
814
+ }
815
+ function parseRetryAfter(headers) {
848
816
  var _a;
849
- const normalizedProvider = provider.trim().toLowerCase();
850
- if (normalizedProvider === "gateway") return "vercel";
851
- const match = MODEL_PROVIDER_NAMES.find(
852
- ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
853
- );
854
- return (_a = match == null ? void 0 : match[1]) != null ? _a : normalizedProvider;
817
+ const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
818
+ if (!value) return void 0;
819
+ const asNumber = Number(value);
820
+ if (value.trim() !== "" && !Number.isNaN(asNumber)) return asNumber * 1e3;
821
+ const asDate = new Date(value).getTime();
822
+ if (!Number.isNaN(asDate)) {
823
+ const delta = asDate - Date.now();
824
+ return delta > 0 ? delta : 0;
825
+ }
826
+ return void 0;
855
827
  }
856
- function tokenCount(value) {
857
- return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
828
+ function getRetryDelayMs(attemptNumber, previousError) {
829
+ if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
830
+ const v = previousError.retryAfterMs;
831
+ if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
832
+ }
833
+ if (attemptNumber <= 1) return 0;
834
+ const base = 500;
835
+ const factor = Math.pow(2, attemptNumber - 2);
836
+ return Math.min(base * factor, MAX_RETRY_DELAY_MS);
858
837
  }
859
- function buildRawModelUsageAttributes(usage) {
860
- const providerReportedCost = typeof usage.providerReportedCost === "number" && Number.isFinite(usage.providerReportedCost) && usage.providerReportedCost >= 0 ? usage.providerReportedCost : void 0;
861
- return [
862
- attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, tokenCount(usage.inputTokens)),
863
- attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, tokenCount(usage.outputTokens)),
864
- attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, tokenCount(usage.reasoningTokens)),
865
- attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, tokenCount(usage.cacheReadInputTokens)),
866
- attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, tokenCount(usage.cacheWriteInputTokens)),
867
- attrDouble(MODEL_USAGE_ATTRIBUTES.providerReportedCost, providerReportedCost)
868
- ].filter((attribute) => attribute !== void 0);
838
+ async function withRetry(operation, opName, opts) {
839
+ const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
840
+ let lastError = void 0;
841
+ for (let attemptNumber = 1; attemptNumber <= opts.maxAttempts; attemptNumber++) {
842
+ if (attemptNumber > 1) {
843
+ const delay = getRetryDelayMs(attemptNumber, lastError);
844
+ if (opts.debug) {
845
+ console.warn(
846
+ `${prefix} ${opName} retry ${attemptNumber}/${opts.maxAttempts} in ${delay}ms`
847
+ );
848
+ }
849
+ if (delay > 0) await wait(delay);
850
+ } else if (opts.debug) {
851
+ console.log(`${prefix} ${opName} attempt ${attemptNumber}/${opts.maxAttempts}`);
852
+ }
853
+ try {
854
+ return await operation();
855
+ } catch (err) {
856
+ lastError = err;
857
+ if (opts.debug) {
858
+ const msg = err instanceof Error ? err.message : String(err);
859
+ console.warn(
860
+ `${prefix} ${opName} attempt ${attemptNumber} failed: ${msg}${attemptNumber === opts.maxAttempts ? " (no more retries)" : ""}`
861
+ );
862
+ }
863
+ if (lastError && typeof lastError === "object" && "retryable" in lastError && !lastError.retryable)
864
+ break;
865
+ if (attemptNumber === opts.maxAttempts) break;
866
+ }
867
+ }
868
+ throw lastError instanceof Error ? lastError : new Error(String(lastError));
869
869
  }
870
- var STRING_ALIASES = {
871
- [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
872
- };
873
- var NUMBER_ALIASES = {
874
- [MODEL_USAGE_ATTRIBUTES.inputTokens]: [
875
- "gen_ai.usage.prompt_tokens",
876
- "ai.usage.prompt_tokens",
877
- "ai.usage.promptTokens",
878
- "ai.usage.input_tokens",
879
- "ai.usage.inputTokens"
880
- ],
881
- [MODEL_USAGE_ATTRIBUTES.outputTokens]: [
882
- "gen_ai.usage.completion_tokens",
883
- "ai.usage.completion_tokens",
884
- "ai.usage.completionTokens",
885
- "ai.usage.output_tokens",
886
- "ai.usage.outputTokens"
887
- ],
888
- [MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
889
- "gen_ai.usage.reasoning.output_tokens",
890
- "ai.usage.reasoningTokens",
891
- "ai.usage.thoughts_tokens"
892
- ],
893
- [MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
894
- "gen_ai.usage.cache_read_tokens",
895
- "gen_ai.usage.cache_read.input_tokens",
896
- "ai.usage.cachedInputTokens",
897
- "ai.usage.cached_tokens",
898
- "ai.usage.cache_read_tokens",
899
- "ai.usage.cache_read_input_tokens",
900
- "ai.usage.cacheReadInputTokens"
901
- ],
902
- [MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
903
- "gen_ai.usage.cache_creation_input_tokens",
904
- "gen_ai.usage.cache_creation.input_tokens",
905
- "ai.usage.cacheWriteInputTokens",
906
- "ai.usage.cache_creation_input_tokens",
907
- "ai.usage.cacheCreationInputTokens"
908
- ]
909
- };
910
- var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
911
- "ai.request.providerOptions",
912
- "ai.response.providerMetadata"
913
- ];
914
- var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
915
- function getAsyncLocalStorageCtor() {
916
- return globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
870
+ async function postJson(url, body, headers, opts) {
871
+ var _a;
872
+ const opName = `POST ${redactUrlForLog(url)}`;
873
+ const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
874
+ await withRetry(
875
+ async () => {
876
+ const resp = await runWithTracingSuppressed(
877
+ () => fetch(url, {
878
+ method: "POST",
879
+ redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
880
+ headers: {
881
+ "Content-Type": "application/json",
882
+ ...headers
883
+ },
884
+ body: JSON.stringify(body),
885
+ signal: AbortSignal.timeout(timeoutMs)
886
+ })
887
+ );
888
+ if (!resp.ok) {
889
+ const text = await resp.text().catch(() => "");
890
+ const err = new Error(
891
+ `HTTP ${resp.status} ${resp.statusText}${text ? `: ${text}` : ""}`
892
+ );
893
+ const retryAfterMs = parseRetryAfter(resp.headers);
894
+ if (typeof retryAfterMs === "number") err.retryAfterMs = retryAfterMs;
895
+ err.retryable = resp.status === 429 || resp.status >= 500;
896
+ throw err;
897
+ }
898
+ },
899
+ opName,
900
+ opts
901
+ );
902
+ }
903
+ var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
904
+ var TRUNCATION_MARKER = "...[truncated by raindrop]";
905
+ var BOUNDED_CLONE_MAX_DEPTH = 12;
906
+ var BOUNDED_CLONE_SLACK = 256;
907
+ var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
908
+ function truncateToLimit(text, limit) {
909
+ if (limit > TRUNCATION_MARKER.length) {
910
+ return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
911
+ }
912
+ return text.slice(0, Math.max(0, limit));
917
913
  }
918
- function getWeakRefCtor() {
919
- const ctor = globalThis.WeakRef;
920
- return typeof ctor === "function" ? ctor : void 0;
914
+ function capText(value, limit) {
915
+ if (typeof value !== "string") return value;
916
+ const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
917
+ if (value.length <= max) return value;
918
+ return truncateToLimit(value, max);
921
919
  }
922
- var MAX_BINDING_STACK = 128;
923
- var EMPTY_STACK = [];
924
- var ContextStackStorage = class {
925
- /** Emitted once when this runtime cannot support an ambient push. */
926
- constructor(noAmbientBindWarning) {
927
- this.noAmbientBindWarning = noAmbientBindWarning;
928
- this._als = null;
929
- this._enterWithUsable = false;
930
- this._syncStack = EMPTY_STACK;
931
- this._adoptionChecked = false;
932
- this._warnedNoAmbientBind = false;
933
- }
934
- maybeAdoptAsyncLocalStorage() {
935
- if (this._adoptionChecked) return;
936
- this._adoptionChecked = true;
937
- const Ctor = getAsyncLocalStorageCtor();
938
- if (!Ctor) return;
939
- const als = new Ctor();
940
- try {
941
- als.run(EMPTY_STACK, () => {
942
- });
943
- } catch (e) {
944
- return;
945
- }
946
- this._als = als;
947
- if (typeof als.enterWith === "function") {
948
- try {
949
- als.enterWith(EMPTY_STACK);
950
- this._enterWithUsable = true;
951
- } catch (e) {
952
- this._enterWithUsable = false;
920
+ function boundedClone(obj, charBudget) {
921
+ let budget = charBudget;
922
+ const seen = /* @__PURE__ */ new WeakSet();
923
+ const walk = (node, depth) => {
924
+ if (budget <= 0) return TRUNCATION_MARKER;
925
+ if (typeof node === "string") {
926
+ if (node.length > budget) {
927
+ const taken = node.slice(0, Math.max(0, budget)) + TRUNCATION_MARKER;
928
+ budget = 0;
929
+ return taken;
953
930
  }
931
+ budget -= Math.max(node.length, 1);
932
+ return node;
954
933
  }
955
- }
956
- getStack() {
957
- var _a;
958
- this.maybeAdoptAsyncLocalStorage();
959
- if (this._als) return (_a = this._als.getStore()) != null ? _a : this._syncStack;
960
- return this._syncStack;
961
- }
962
- /**
963
- * Persist an ambient stack mutation. Returns false when an ALS is adopted but
964
- * `enterWith` is unusable (workerd): callers must NOT fall back to the
965
- * process-shared sync stack, since concurrent isolates would cross-stamp.
966
- */
967
- setAmbientStack(stack) {
968
- if (this._als) {
969
- if (!this._enterWithUsable) return false;
970
- this._als.enterWith(stack);
971
- return true;
972
- }
973
- this._syncStack = stack;
974
- return true;
975
- }
976
- push(binding) {
977
- this.maybeAdoptAsyncLocalStorage();
978
- if (this._als && !this._enterWithUsable) {
979
- this.warnNoAmbientBindOnce();
980
- return;
934
+ if (node === null || typeof node === "number" || typeof node === "boolean") {
935
+ budget -= 8;
936
+ return node;
981
937
  }
982
- let stack = this.getStack();
983
- if (stack.length >= MAX_BINDING_STACK) {
984
- stack = stack.filter((b) => b.isLive());
985
- if (stack.length >= MAX_BINDING_STACK) {
986
- stack = stack.slice(stack.length - (MAX_BINDING_STACK - 1));
987
- }
938
+ if (typeof node !== "object") {
939
+ budget -= 8;
940
+ return node;
988
941
  }
989
- this.setAmbientStack([...stack, binding]);
990
- }
991
- remove(binding) {
992
- binding.markDead();
993
- const stack = this.getStack();
994
- const pruned = stack.filter((b) => b !== binding && b.isLive());
995
- if (pruned.length !== stack.length) {
996
- this.setAmbientStack(pruned);
942
+ if (seen.has(node)) return "[CIRCULAR]";
943
+ if (depth >= BOUNDED_CLONE_MAX_DEPTH) {
944
+ budget -= 16;
945
+ return `<max depth>`;
997
946
  }
998
- }
999
- runWith(binding, callback) {
1000
- this.maybeAdoptAsyncLocalStorage();
1001
- if (this._als) {
1002
- return this._als.run([...this.getStack(), binding], callback);
947
+ seen.add(node);
948
+ if (Array.isArray(node)) {
949
+ const out2 = [];
950
+ for (const v of node) {
951
+ if (budget <= 0) {
952
+ out2.push(TRUNCATION_MARKER);
953
+ break;
954
+ }
955
+ out2.push(walk(v, depth + 1));
956
+ }
957
+ return out2;
1003
958
  }
1004
- const previous = this._syncStack;
1005
- this._syncStack = [...previous, binding];
1006
- try {
1007
- return callback();
1008
- } finally {
1009
- this._syncStack = previous;
959
+ if (typeof node.toJSON === "function") {
960
+ budget -= 16;
961
+ return node;
1010
962
  }
1011
- }
1012
- current() {
1013
- const stack = this.getStack();
1014
- if (stack.length === 0) return void 0;
1015
- const top = stack[stack.length - 1];
1016
- if (top.isLive()) return top;
1017
- const pruned = stack.filter((b) => b.isLive());
1018
- this.setAmbientStack(pruned);
1019
- return pruned.length > 0 ? pruned[pruned.length - 1] : void 0;
1020
- }
1021
- warnNoAmbientBindOnce() {
1022
- if (this._warnedNoAmbientBind) return;
1023
- this._warnedNoAmbientBind = true;
1024
- console.warn(this.noAmbientBindWarning);
1025
- }
1026
- };
1027
- var NO_AMBIENT_ROUTING_BIND_WARNING = "[raindrop] This runtime's AsyncLocalStorage does not implement enterWith (e.g. Cloudflare Workers), so begin()/finish() cannot scope auto-instrumented spans to a project without cross-contaminating concurrent requests. Manual events and explicit span routing are unaffected; use asCurrent()/withSpan (run-based) to route auto-instrumented spans on this runtime.";
1028
- var NO_AMBIENT_ATTRIBUTE_BIND_WARNING = "[raindrop] This runtime's AsyncLocalStorage does not implement enterWith (e.g. Cloudflare Workers), so contextual span attributes (the detached sub-agent hand-off reference) cannot be bound ambiently without cross-contaminating concurrent requests. The child's own event still reports; only the per-span reverse reference is skipped.";
1029
- var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
1030
- function routingStorage() {
1031
- const holder = globalThis;
1032
- let storage = holder[ROUTING_STORAGE_KEY];
1033
- if (!storage) {
1034
- storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
1035
- holder[ROUTING_STORAGE_KEY] = storage;
1036
- }
1037
- return storage;
1038
- }
1039
- function makeBoundContext(binding, owner) {
1040
- const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
1041
- const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
1042
- let dead = false;
1043
- return {
1044
- projectId: binding.projectId,
1045
- authHint: binding.authHint,
1046
- isLive() {
1047
- if (dead) return false;
1048
- return ref === void 0 || ref.deref() !== void 0;
1049
- },
1050
- markDead() {
1051
- dead = true;
963
+ const out = {};
964
+ for (const k in node) {
965
+ if (!Object.prototype.hasOwnProperty.call(node, k)) continue;
966
+ if (budget <= 0) {
967
+ out["..."] = TRUNCATION_MARKER;
968
+ break;
969
+ }
970
+ const key = walk(k, depth + 1);
971
+ out[key] = walk(node[k], depth + 1);
1052
972
  }
973
+ return out;
1053
974
  };
975
+ return walk(obj, 0);
1054
976
  }
1055
- function bindRoutingContext(binding, owner) {
1056
- const bound = makeBoundContext(binding, owner);
1057
- try {
1058
- routingStorage().push(bound);
1059
- } catch (e) {
977
+ function stringifyBounded(value, limit) {
978
+ const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
979
+ if (typeof value === "string") {
980
+ return capText(value, max);
1060
981
  }
1061
- return bound;
1062
- }
1063
- function unbindRoutingContext(bound) {
1064
- if (!bound || !isBoundContext(bound)) return;
982
+ const pruned = boundedClone(value, max + TRUNCATION_MARKER.length + BOUNDED_CLONE_SLACK);
983
+ let json;
1065
984
  try {
1066
- routingStorage().remove(bound);
985
+ json = JSON.stringify(pruned);
1067
986
  } catch (e) {
987
+ json = void 0;
1068
988
  }
989
+ if (json === void 0) {
990
+ return capText(String(value), max);
991
+ }
992
+ return json.length <= max ? json : truncateToLimit(json, max);
1069
993
  }
1070
- function withRoutingContext(binding, callback) {
1071
- const bound = makeBoundContext(binding);
1072
- return routingStorage().runWith(bound, callback);
1073
- }
1074
- function currentRoutingBinding() {
1075
- const bound = routingStorage().current();
1076
- if (!bound) return void 0;
1077
- return { projectId: bound.projectId, authHint: bound.authHint };
1078
- }
1079
- function isBoundContext(value) {
1080
- return typeof value.isLive === "function";
994
+ function attrString(key, value) {
995
+ if (value === void 0) return void 0;
996
+ return { key, value: { stringValue: value } };
1081
997
  }
1082
- var ATTRIBUTE_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.spanAttributeStorage");
1083
- function attributeStorage() {
1084
- const holder = globalThis;
1085
- let storage = holder[ATTRIBUTE_STORAGE_KEY];
1086
- if (!storage) {
1087
- storage = new ContextStackStorage(NO_AMBIENT_ATTRIBUTE_BIND_WARNING);
1088
- holder[ATTRIBUTE_STORAGE_KEY] = storage;
1089
- }
1090
- return storage;
998
+ function attrInt(key, value) {
999
+ if (value === void 0) return void 0;
1000
+ if (!Number.isFinite(value)) return void 0;
1001
+ return { key, value: { intValue: String(Math.trunc(value)) } };
1091
1002
  }
1092
- function makeAttributeFrame(attributes, owner) {
1093
- const snapshot = { ...attributes };
1094
- const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
1095
- const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
1096
- const owned = /* @__PURE__ */ new WeakSet();
1097
- let dead = false;
1098
- let sawError = false;
1099
- return {
1100
- attributes: snapshot,
1101
- sawErrorSpan: () => sawError,
1102
- ownsSpan: (span) => owned.has(span),
1103
- claimSpan(span) {
1104
- owned.add(span);
1105
- },
1106
- markErrorSpanSeen() {
1107
- sawError = true;
1108
- },
1109
- // A frame whose owner has been collected is dead even though nothing
1110
- // unbound it: a run that never settles must not keep handing its reverse
1111
- // reference to whatever runs next on a reused context.
1112
- isLive() {
1113
- if (dead) return false;
1114
- return ref === void 0 || ref.deref() !== void 0;
1115
- },
1116
- markDead() {
1117
- dead = true;
1118
- }
1119
- };
1003
+ function attrDouble(key, value) {
1004
+ if (value === void 0) return void 0;
1005
+ if (!Number.isFinite(value)) return void 0;
1006
+ return { key, value: { doubleValue: value } };
1120
1007
  }
1121
- function bindSpanAttributes(attributes, owner) {
1122
- if (Object.keys(attributes).length === 0) return void 0;
1123
- const frame = makeAttributeFrame(attributes, owner);
1008
+ var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
1009
+ var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
1010
+ var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
1011
+ function readEnvVar(name) {
1012
+ var _a;
1124
1013
  try {
1125
- attributeStorage().push(frame);
1014
+ const env = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env;
1015
+ if (env && typeof env[name] === "string" && env[name].length > 0) {
1016
+ return env[name];
1017
+ }
1126
1018
  } catch (e) {
1127
- return void 0;
1128
1019
  }
1129
- return frame;
1020
+ return void 0;
1130
1021
  }
1131
- function unbindSpanAttributes(frame) {
1132
- if (!frame || !isAttributeFrame(frame)) return;
1133
- try {
1134
- attributeStorage().remove(frame);
1135
- } catch (e) {
1136
- }
1022
+ function readWorkshopEnv() {
1023
+ const raw = readEnvVar(WORKSHOP_ENV_VAR);
1024
+ if (raw === void 0) return void 0;
1025
+ const trimmed = raw.trim();
1026
+ if (trimmed.length === 0) return void 0;
1027
+ if (/^https?:\/\//i.test(trimmed)) return { url: trimmed };
1028
+ if (/^(1|true|yes|on)$/i.test(trimmed)) return "enable";
1029
+ if (/^(0|false|no|off)$/i.test(trimmed)) return "disable";
1030
+ return void 0;
1137
1031
  }
1138
- function applySpanAttributeFrames(span, setAttribute) {
1139
- try {
1140
- for (const frame of attributeStorage().getStack()) {
1141
- if (!frame.isLive()) continue;
1142
- frame.claimSpan(span);
1143
- for (const [key, value] of Object.entries(frame.attributes)) {
1144
- setAttribute(key, value);
1145
- }
1146
- }
1147
- } catch (e) {
1032
+ function isLocalDevHost(hostname) {
1033
+ if (!hostname) return false;
1034
+ if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1") {
1035
+ return true;
1148
1036
  }
1037
+ if (hostname.endsWith(".localhost")) return true;
1038
+ return false;
1149
1039
  }
1150
- function claimSpanForFrames(span) {
1040
+ function readRuntimeHostname() {
1151
1041
  try {
1152
- for (const frame of attributeStorage().getStack()) {
1153
- if (frame.isLive()) frame.claimSpan(span);
1042
+ const loc = globalThis == null ? void 0 : globalThis.location;
1043
+ if (loc && typeof loc.hostname === "string" && loc.hostname.length > 0) {
1044
+ return loc.hostname;
1154
1045
  }
1155
1046
  } catch (e) {
1156
1047
  }
1048
+ return void 0;
1157
1049
  }
1158
- function currentSpanAttributes() {
1159
- const merged = {};
1160
- try {
1161
- for (const frame of attributeStorage().getStack()) {
1162
- if (!frame.isLive()) continue;
1163
- Object.assign(merged, frame.attributes);
1164
- }
1165
- } catch (e) {
1050
+ function shouldAutoEnableLocalWorkshop() {
1051
+ if (isLocalDevHost(readRuntimeHostname())) return true;
1052
+ if (readEnvVar("NODE_ENV") === "development") return true;
1053
+ return false;
1054
+ }
1055
+ function resolveLocalDebuggerBaseUrl(baseUrl) {
1056
+ var _a, _b, _c;
1057
+ if (baseUrl === null) return null;
1058
+ if (typeof baseUrl === "string" && baseUrl.length > 0) {
1059
+ return (_a = formatEndpoint(baseUrl)) != null ? _a : null;
1166
1060
  }
1167
- return merged;
1061
+ const explicitUrlEnv = readEnvVar(LOCAL_DEBUGGER_ENV_VAR);
1062
+ if (explicitUrlEnv) return (_b = formatEndpoint(explicitUrlEnv)) != null ? _b : null;
1063
+ const workshopEnv = readWorkshopEnv();
1064
+ if (workshopEnv === "disable") return null;
1065
+ if (workshopEnv === "enable") return DEFAULT_LOCAL_WORKSHOP_URL;
1066
+ if (workshopEnv && "url" in workshopEnv) return (_c = formatEndpoint(workshopEnv.url)) != null ? _c : null;
1067
+ if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
1068
+ return null;
1168
1069
  }
1169
- function markErrorSpanSeen(span) {
1170
- try {
1171
- for (const frame of attributeStorage().getStack()) {
1172
- if (frame.ownsSpan(span)) frame.markErrorSpanSeen();
1070
+ function localDebuggerEnabled(baseUrl) {
1071
+ return resolveLocalDebuggerBaseUrl(baseUrl) !== null;
1072
+ }
1073
+ var LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS = 2e3;
1074
+ function mirrorTraceExportToLocalDebugger(body, options = {}) {
1075
+ var _a;
1076
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
1077
+ if (!baseUrl) return Promise.resolve();
1078
+ return postJson(`${baseUrl}traces`, body, {}, {
1079
+ maxAttempts: 1,
1080
+ debug: (_a = options.debug) != null ? _a : false,
1081
+ sdkName: options.sdkName,
1082
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
1083
+ }).catch(() => {
1084
+ });
1085
+ }
1086
+ function sendLocalDebuggerLiveEvent(event, options = {}) {
1087
+ var _a, _b;
1088
+ const destination = currentReplayTraceDestination();
1089
+ if (destination && destination.kind !== "workshop") return;
1090
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
1091
+ if (!baseUrl) return;
1092
+ void postJson(
1093
+ `${baseUrl}live`,
1094
+ {
1095
+ ...event,
1096
+ type: event.type,
1097
+ timestamp: (_a = event.timestamp) != null ? _a : Date.now()
1098
+ },
1099
+ {},
1100
+ {
1101
+ maxAttempts: 1,
1102
+ debug: (_b = options.debug) != null ? _b : false,
1103
+ sdkName: options.sdkName
1173
1104
  }
1174
- } catch (e) {
1175
- }
1105
+ ).catch(() => {
1106
+ });
1176
1107
  }
1177
- function isAttributeFrame(value) {
1178
- return typeof value.isLive === "function";
1108
+ var PROJECT_ID_HEADER = "X-Raindrop-Project-Id";
1109
+ function projectIdHeaders(projectId) {
1110
+ return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
1111
+ }
1112
+ var SHUTDOWN_DEADLINE_MS = 1e4;
1113
+ var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
1114
+ var MODEL_USAGE_ATTRIBUTES = {
1115
+ usageSource: "raindrop.usage.source",
1116
+ providerName: "gen_ai.provider.name",
1117
+ requestModel: "gen_ai.request.model",
1118
+ responseModel: "gen_ai.response.model",
1119
+ inputTokens: "gen_ai.usage.input_tokens",
1120
+ outputTokens: "gen_ai.usage.output_tokens",
1121
+ reasoningTokens: "gen_ai.usage.reasoning_tokens",
1122
+ cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
1123
+ cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
1124
+ providerReportedCost: "gen_ai.usage.provider_reported_cost",
1125
+ nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
1126
+ nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
1127
+ totalOutputTokens: "raindrop.usage.output_tokens.total",
1128
+ reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
1129
+ cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
1130
+ cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
1131
+ };
1132
+ var MODEL_PROVIDER_NAMES = [
1133
+ ["google.vertex.anthropic", "google-vertex"],
1134
+ ["google.vertex_ai", "google-vertex"],
1135
+ ["google.vertex", "google-vertex"],
1136
+ ["gcp.vertex_ai", "google-vertex"],
1137
+ ["gcp.gemini", "google"],
1138
+ ["google_vertexai", "google-vertex"],
1139
+ ["vertex.anthropic", "google-vertex"],
1140
+ ["amazon-bedrock", "amazon-bedrock"],
1141
+ ["aws.bedrock", "amazon-bedrock"],
1142
+ ["bedrock", "amazon-bedrock"],
1143
+ ["anthropic", "anthropic"],
1144
+ ["openai", "openai"],
1145
+ ["google", "google"],
1146
+ ["az.ai.openai", "azure"],
1147
+ ["azure", "azure"],
1148
+ ["openrouter", "openrouter"]
1149
+ ];
1150
+ function canonicalModelProvider(provider) {
1151
+ var _a;
1152
+ const normalizedProvider = provider.trim().toLowerCase();
1153
+ if (normalizedProvider === "gateway") return "vercel";
1154
+ const match = MODEL_PROVIDER_NAMES.find(
1155
+ ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
1156
+ );
1157
+ return (_a = match == null ? void 0 : match[1]) != null ? _a : normalizedProvider;
1158
+ }
1159
+ function tokenCount(value) {
1160
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
1161
+ }
1162
+ function buildRawModelUsageAttributes(usage) {
1163
+ const providerReportedCost = typeof usage.providerReportedCost === "number" && Number.isFinite(usage.providerReportedCost) && usage.providerReportedCost >= 0 ? usage.providerReportedCost : void 0;
1164
+ return [
1165
+ attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, tokenCount(usage.inputTokens)),
1166
+ attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, tokenCount(usage.outputTokens)),
1167
+ attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, tokenCount(usage.reasoningTokens)),
1168
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, tokenCount(usage.cacheReadInputTokens)),
1169
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, tokenCount(usage.cacheWriteInputTokens)),
1170
+ attrDouble(MODEL_USAGE_ATTRIBUTES.providerReportedCost, providerReportedCost)
1171
+ ].filter((attribute) => attribute !== void 0);
1179
1172
  }
1173
+ var STRING_ALIASES = {
1174
+ [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
1175
+ };
1176
+ var NUMBER_ALIASES = {
1177
+ [MODEL_USAGE_ATTRIBUTES.inputTokens]: [
1178
+ "gen_ai.usage.prompt_tokens",
1179
+ "ai.usage.prompt_tokens",
1180
+ "ai.usage.promptTokens",
1181
+ "ai.usage.input_tokens",
1182
+ "ai.usage.inputTokens"
1183
+ ],
1184
+ [MODEL_USAGE_ATTRIBUTES.outputTokens]: [
1185
+ "gen_ai.usage.completion_tokens",
1186
+ "ai.usage.completion_tokens",
1187
+ "ai.usage.completionTokens",
1188
+ "ai.usage.output_tokens",
1189
+ "ai.usage.outputTokens"
1190
+ ],
1191
+ [MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
1192
+ "gen_ai.usage.reasoning.output_tokens",
1193
+ "ai.usage.reasoningTokens",
1194
+ "ai.usage.thoughts_tokens"
1195
+ ],
1196
+ [MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
1197
+ "gen_ai.usage.cache_read_tokens",
1198
+ "gen_ai.usage.cache_read.input_tokens",
1199
+ "ai.usage.cachedInputTokens",
1200
+ "ai.usage.cached_tokens",
1201
+ "ai.usage.cache_read_tokens",
1202
+ "ai.usage.cache_read_input_tokens",
1203
+ "ai.usage.cacheReadInputTokens"
1204
+ ],
1205
+ [MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
1206
+ "gen_ai.usage.cache_creation_input_tokens",
1207
+ "gen_ai.usage.cache_creation.input_tokens",
1208
+ "ai.usage.cacheWriteInputTokens",
1209
+ "ai.usage.cache_creation_input_tokens",
1210
+ "ai.usage.cacheCreationInputTokens"
1211
+ ]
1212
+ };
1213
+ var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
1214
+ "ai.request.providerOptions",
1215
+ "ai.response.providerMetadata"
1216
+ ];
1217
+ var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
1180
1218
 
1181
1219
  // ../core/dist/index.node.js
1182
1220
  var import_async_hooks = require("async_hooks");
@@ -1384,7 +1422,7 @@ function base64ToHex2(base64) {
1384
1422
  // package.json
1385
1423
  var package_default = {
1386
1424
  name: "raindrop-ai",
1387
- version: "0.7.0",
1425
+ version: "0.7.1",
1388
1426
  main: "dist/index.js",
1389
1427
  module: "dist/index.mjs",
1390
1428
  types: "dist/index.d.ts",
@@ -1800,11 +1838,38 @@ function stampContextAttributes(span) {
1800
1838
  }
1801
1839
  }
1802
1840
  var RaindropContextSpanProcessor = class {
1841
+ constructor(appGitSnapshot) {
1842
+ this.appGitSnapshot = appGitSnapshot;
1843
+ this.appGitAdded = /* @__PURE__ */ new WeakMap();
1844
+ }
1803
1845
  onStart(span) {
1846
+ var _a, _b, _c, _d;
1804
1847
  try {
1805
1848
  stampContextAttributes(span);
1806
1849
  rememberReplayTraceDestination(span);
1807
1850
  const bound = currentRoutingBinding();
1851
+ const keys = ["raindrop.app.commit_sha", "raindrop.app.commit_dirty", "raindrop.app.branch"];
1852
+ const attrs = (_a = span.attributes) != null ? _a : {};
1853
+ const properties = {};
1854
+ for (const key of keys) {
1855
+ const existing = [key, `ai.telemetry.metadata.${key}`, `traceloop.association.properties.${key}`].find((name) => Object.prototype.hasOwnProperty.call(attrs, name));
1856
+ if (existing) properties[key] = attrs[existing];
1857
+ }
1858
+ const source = { ...(_d = (_c = (_b = this.appGitSnapshot) == null ? void 0 : _b.call(this, properties)) != null ? _c : bound == null ? void 0 : bound.appGit) != null ? _d : {} };
1859
+ if (Object.prototype.hasOwnProperty.call(properties, "raindrop.app.commit_sha") && properties["raindrop.app.commit_sha"] !== source["raindrop.app.commit_sha"]) {
1860
+ delete source["raindrop.app.commit_dirty"];
1861
+ delete source["raindrop.app.branch"];
1862
+ }
1863
+ const added = /* @__PURE__ */ new Map();
1864
+ for (const key of keys) {
1865
+ if ([key, `ai.telemetry.metadata.${key}`, `traceloop.association.properties.${key}`].some((name) => Object.prototype.hasOwnProperty.call(attrs, name))) continue;
1866
+ const value = source == null ? void 0 : source[key];
1867
+ if (typeof value === "string" || typeof value === "boolean") {
1868
+ span.setAttribute(key, value);
1869
+ added.set(key, value);
1870
+ }
1871
+ }
1872
+ this.appGitAdded.set(span, added);
1808
1873
  if (!bound) return;
1809
1874
  stampSpan(span, bound.projectId, bound.authHint);
1810
1875
  } catch (e) {
@@ -1813,6 +1878,16 @@ var RaindropContextSpanProcessor = class {
1813
1878
  onEnd(span) {
1814
1879
  var _a;
1815
1880
  try {
1881
+ if (span == null ? void 0 : span.attributes) {
1882
+ const attrs = span.attributes;
1883
+ const added = this.appGitAdded.get(span);
1884
+ const shaKey = "raindrop.app.commit_sha";
1885
+ const hasAlias = (key) => [`ai.telemetry.metadata.${key}`, `traceloop.association.properties.${key}`].some((name) => Object.prototype.hasOwnProperty.call(attrs, name));
1886
+ const shaChanged = (added == null ? void 0 : added.has(shaKey)) && (attrs[shaKey] !== added.get(shaKey) || hasAlias(shaKey));
1887
+ for (const [key, value] of added != null ? added : []) {
1888
+ if (attrs[key] === value && (hasAlias(key) || shaChanged && key !== shaKey)) delete attrs[key];
1889
+ }
1890
+ }
1816
1891
  if (((_a = span == null ? void 0 : span.status) == null ? void 0 : _a.code) !== SPAN_STATUS_ERROR) return;
1817
1892
  markErrorSpanSeen(span);
1818
1893
  } catch (e) {
@@ -1938,6 +2013,7 @@ var DirectTraceShipper = class {
1938
2013
  */
1939
2014
  this.hasShutdown = false;
1940
2015
  var _a, _b, _c, _d, _e, _f;
2016
+ this.appGitSnapshot = opts.appGitSnapshot;
1941
2017
  this.writeKey = opts.writeKey;
1942
2018
  this.baseUrl = (_a = formatEndpoint2(opts.endpoint)) != null ? _a : "https://api.raindrop.ai/v1/";
1943
2019
  this.enabled = (_b = opts.enabled) != null ? _b : true;
@@ -1952,6 +2028,14 @@ var DirectTraceShipper = class {
1952
2028
  this.localDebuggerUrl = resolved;
1953
2029
  }
1954
2030
  }
2031
+ applicationGitSnapshot(properties) {
2032
+ var _a, _b, _c, _d;
2033
+ try {
2034
+ return (_d = (_c = (_a = this.appGitSnapshot) == null ? void 0 : _a.call(this, properties)) != null ? _c : (_b = currentRoutingBinding()) == null ? void 0 : _b.appGit) != null ? _d : {};
2035
+ } catch (e) {
2036
+ return {};
2037
+ }
2038
+ }
1955
2039
  /**
1956
2040
  * Build and enqueue an OTLP span for a trackTool call.
1957
2041
  *
@@ -2026,7 +2110,10 @@ var DirectTraceShipper = class {
2026
2110
  name: params.name,
2027
2111
  startTimeUnixNano,
2028
2112
  endTimeUnixNano,
2029
- attributes,
2113
+ attributes: withAppGitAttributes(
2114
+ attributes,
2115
+ params.appGitSnapshot !== void 0 ? params.appGitSnapshot : this.applicationGitSnapshot(params.properties)
2116
+ ),
2030
2117
  status
2031
2118
  });
2032
2119
  const replayDestination = currentReplayTraceDestination();
@@ -3647,7 +3734,7 @@ var LiveInteraction = class {
3647
3734
  span.end();
3648
3735
  }
3649
3736
  async finish(resultEvent) {
3650
- var _a, _b, _c, _d, _e, _f, _g;
3737
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
3651
3738
  unbindRoutingContext(this.boundContext);
3652
3739
  this.boundContext = void 0;
3653
3740
  unbindSpanAttributes(this.toolEventsFrame);
@@ -3657,10 +3744,19 @@ var LiveInteraction = class {
3657
3744
  }
3658
3745
  const eventId = resultEvent.eventId || this.context.eventId;
3659
3746
  if (!this.usageEmitted) {
3660
- (_c = this.analytics) == null ? void 0 : _c._recordModelUsage(
3747
+ const appGitSnapshot = (_b = (_a = this.analytics) == null ? void 0 : _a.getAppGitSnapshot(resultEvent.properties, eventId)) != null ? _b : {};
3748
+ const usageProperties = {};
3749
+ for (const key of [APP_COMMIT_SHA, APP_COMMIT_DIRTY, APP_BRANCH]) {
3750
+ if (resultEvent.properties && Object.prototype.hasOwnProperty.call(resultEvent.properties, key)) {
3751
+ usageProperties[key] = resultEvent.properties[key];
3752
+ }
3753
+ }
3754
+ (_e = this.analytics) == null ? void 0 : _e._recordModelUsage(
3661
3755
  eventId,
3662
- (_a = resultEvent.model) != null ? _a : this.context.model,
3663
- (_b = resultEvent.usage) != null ? _b : this.context.usage
3756
+ (_c = resultEvent.model) != null ? _c : this.context.model,
3757
+ (_d = resultEvent.usage) != null ? _d : this.context.usage,
3758
+ withAppGitProperties(usageProperties, appGitSnapshot),
3759
+ appGitSnapshot
3664
3760
  );
3665
3761
  this.usageEmitted = true;
3666
3762
  }
@@ -3671,20 +3767,20 @@ var LiveInteraction = class {
3671
3767
  } catch (e) {
3672
3768
  }
3673
3769
  })();
3674
- if ((_e = (_d = this.analytics) == null ? void 0 : _d._shouldBlockFinishFlush()) != null ? _e : true) {
3770
+ if ((_g = (_f = this.analytics) == null ? void 0 : _f._shouldBlockFinishFlush()) != null ? _g : true) {
3675
3771
  await spanFlush;
3676
3772
  } else {
3677
3773
  void spanFlush.catch(() => {
3678
3774
  });
3679
3775
  }
3680
3776
  try {
3681
- await ((_f = this.analytics) == null ? void 0 : _f._drainInFlightPartials(eventId));
3777
+ await ((_h = this.analytics) == null ? void 0 : _h._drainInFlightPartials(eventId));
3682
3778
  } catch (e) {
3683
3779
  }
3684
3780
  if (this.traceId) {
3685
3781
  this.setProperty("trace_id", this.traceId);
3686
3782
  }
3687
- await ((_g = this.analytics) == null ? void 0 : _g._trackAiPartial({
3783
+ await ((_i = this.analytics) == null ? void 0 : _i._trackAiPartial({
3688
3784
  ...resultEvent,
3689
3785
  eventId,
3690
3786
  isPending: false
@@ -4491,6 +4587,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
4491
4587
  }
4492
4588
  }
4493
4589
  const directShipper = bypassOtelForTools ? new DirectTraceShipper({
4590
+ appGitSnapshot: (properties) => analytics.getAppGitSnapshot(properties),
4494
4591
  writeKey,
4495
4592
  endpoint: directEndpoint != null ? directEndpoint : apiUrl,
4496
4593
  debug: isDebug,
@@ -4550,7 +4647,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
4550
4647
  tracingEnabled: tracingEnabled !== false,
4551
4648
  disableBatch: disableBatching,
4552
4649
  processor: new CompositeSpanProcessor(
4553
- [new RaindropContextSpanProcessor(), localDebuggerProcessor],
4650
+ [new RaindropContextSpanProcessor((properties) => analytics.getAppGitSnapshot(properties)), localDebuggerProcessor],
4554
4651
  []
4555
4652
  ),
4556
4653
  instrumentations: createConfiguredInstrumentations(otherOptions)
@@ -4574,7 +4671,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
4574
4671
  tracingEnabled: tracingEnabled !== false,
4575
4672
  disableBatch: disableBatching,
4576
4673
  processor: new CompositeSpanProcessor(
4577
- [new RaindropContextSpanProcessor(), replayOnlyProcessor],
4674
+ [new RaindropContextSpanProcessor((properties) => analytics.getAppGitSnapshot(properties)), replayOnlyProcessor],
4578
4675
  []
4579
4676
  ),
4580
4677
  instrumentations: createConfiguredInstrumentations(otherOptions)
@@ -4615,7 +4712,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
4615
4712
  );
4616
4713
  }
4617
4714
  if (guardedExporter) {
4618
- const contextProcessor = new RaindropContextSpanProcessor();
4715
+ const contextProcessor = new RaindropContextSpanProcessor((properties) => analytics.getAppGitSnapshot(properties));
4619
4716
  const callerProcessor = otherOptions.processor;
4620
4717
  const processor = new CompositeSpanProcessor(
4621
4718
  callerProcessor ? [contextProcessor, callerProcessor] : [contextProcessor],
@@ -4678,9 +4775,9 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
4678
4775
  traceId,
4679
4776
  analytics,
4680
4777
  directShipper,
4681
- { projectId, authHint }
4778
+ { projectId, authHint, appGit: analytics.getAppGitSnapshot(traceContext.properties, traceContext.eventId) }
4682
4779
  );
4683
- const bound = bindRoutingContext({ projectId, authHint }, interaction);
4780
+ const bound = bindRoutingContext({ projectId, authHint, appGit: analytics.getAppGitSnapshot(traceContext.properties, traceContext.eventId) }, interaction);
4684
4781
  interaction.setBoundContext(bound);
4685
4782
  try {
4686
4783
  analytics._trackAiPartial({
@@ -4757,7 +4854,8 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
4757
4854
  directShipper.sendSpan({
4758
4855
  name: "ai.model.usage",
4759
4856
  spanKind: "llm",
4760
- properties: { event_id: params.eventId },
4857
+ properties: { ...params.appGitProperties, event_id: params.eventId },
4858
+ appGitSnapshot: params.appGitSnapshot,
4761
4859
  startTimeMs: timeMs,
4762
4860
  endTimeMs: timeMs,
4763
4861
  durationMs: 0,
@@ -4765,9 +4863,23 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
4765
4863
  });
4766
4864
  return;
4767
4865
  }
4768
- const span = import_api6.trace.getTracer("traceloop.tracer").startSpan("ai.model.usage");
4769
- span.setAttribute("traceloop.span.kind", "llm");
4770
- span.setAttribute("traceloop.association.properties.event_id", params.eventId);
4866
+ const initialAttributes = {
4867
+ "traceloop.span.kind": "llm"
4868
+ };
4869
+ for (const [key, value] of Object.entries({
4870
+ ...params.appGitProperties,
4871
+ event_id: params.eventId
4872
+ })) {
4873
+ if (value !== void 0) {
4874
+ initialAttributes[`traceloop.association.properties.${key}`] = stringifyBounded(value);
4875
+ }
4876
+ }
4877
+ for (const [key, value] of Object.entries(params.appGitSnapshot)) {
4878
+ if (typeof value === "string" || typeof value === "boolean") {
4879
+ initialAttributes[key] = value;
4880
+ }
4881
+ }
4882
+ const span = import_api6.trace.getTracer("traceloop.tracer").startSpan("ai.model.usage", { attributes: initialAttributes });
4771
4883
  stampSpan(span, projectId, authHint);
4772
4884
  for (const attribute of attributes) {
4773
4885
  const value = attribute.value;
@@ -4888,7 +5000,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
4888
5000
  allowedInstrumentationLibraries: processorOptions == null ? void 0 : processorOptions.allowedInstrumentationLibraries,
4889
5001
  localDebuggerUrlOpt: localDebuggerUrl
4890
5002
  });
4891
- const contextProcessor = new RaindropContextSpanProcessor();
5003
+ const contextProcessor = new RaindropContextSpanProcessor((properties) => analytics.getAppGitSnapshot(properties));
4892
5004
  return withV2SpanCompat(
4893
5005
  new CompositeSpanProcessor(
4894
5006
  [contextProcessor, productionProcessor],