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.
package/dist/evals/cli.js CHANGED
@@ -5676,11 +5676,11 @@ var require_AsyncCompositeRedactor = __commonJS({
5676
5676
  "use strict";
5677
5677
  var __awaiter = exports2 && exports2.__awaiter || function(thisArg, _arguments, P, generator) {
5678
5678
  function adopt(value) {
5679
- return value instanceof P ? value : new P(function(resolve2) {
5680
- resolve2(value);
5679
+ return value instanceof P ? value : new P(function(resolve3) {
5680
+ resolve3(value);
5681
5681
  });
5682
5682
  }
5683
- return new (P || (P = Promise))(function(resolve2, reject) {
5683
+ return new (P || (P = Promise))(function(resolve3, reject) {
5684
5684
  function fulfilled(value) {
5685
5685
  try {
5686
5686
  step(generator.next(value));
@@ -5696,7 +5696,7 @@ var require_AsyncCompositeRedactor = __commonJS({
5696
5696
  }
5697
5697
  }
5698
5698
  function step(result) {
5699
- result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
5699
+ result.done ? resolve3(result.value) : adopt(result.value).then(fulfilled, rejected);
5700
5700
  }
5701
5701
  step((generator = generator.apply(thisArg, _arguments || [])).next());
5702
5702
  });
@@ -6940,7 +6940,7 @@ async function drivePromise(runtime, context9, promise, deadline) {
6940
6940
  const state2 = context9.getPromiseState(promise);
6941
6941
  if (state2.type === "fulfilled") return { value: state2.value };
6942
6942
  if (state2.type === "rejected") return { error: state2.error };
6943
- await new Promise((resolve2) => setTimeout(resolve2, 0));
6943
+ await new Promise((resolve3) => setTimeout(resolve3, 0));
6944
6944
  if (Date.now() >= deadline) throw new Error("interrupted");
6945
6945
  }
6946
6946
  }
@@ -7264,1135 +7264,1509 @@ function evalScopeAttributes() {
7264
7264
  return { [EVAL_CORRELATION_ID_ATTRIBUTE]: scope.correlationId };
7265
7265
  }
7266
7266
 
7267
- // ../core/dist/chunk-T53LQAH7.js
7268
- function normalizeFeatureFlags(input) {
7269
- if (Array.isArray(input)) {
7270
- return Object.fromEntries(input.map((name) => [name, "true"]));
7271
- }
7272
- return Object.fromEntries(
7273
- Object.entries(input).map(([k, v]) => [k, String(v)])
7274
- );
7275
- }
7276
- function getCrypto() {
7277
- const c = globalThis.crypto;
7278
- return c;
7279
- }
7280
- function randomBytes(length) {
7281
- const cryptoObj = getCrypto();
7282
- const out = new Uint8Array(length);
7283
- if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
7284
- cryptoObj.getRandomValues(out);
7285
- return out;
7286
- }
7287
- for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
7288
- return out;
7289
- }
7290
- function randomUUID() {
7291
- const cryptoObj = getCrypto();
7292
- if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
7293
- return cryptoObj.randomUUID();
7267
+ // ../core/dist/chunk-WJOR6M7H.js
7268
+ var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
7269
+ function state() {
7270
+ const holder = globalThis;
7271
+ let current = holder[STATE_KEY];
7272
+ if (!current) {
7273
+ let storage = null;
7274
+ try {
7275
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
7276
+ if (Ctor) storage = new Ctor();
7277
+ } catch (e) {
7278
+ storage = null;
7279
+ }
7280
+ current = {
7281
+ storage,
7282
+ syncDestination: void 0,
7283
+ spanDestinations: /* @__PURE__ */ new WeakMap()
7284
+ };
7285
+ holder[STATE_KEY] = current;
7294
7286
  }
7295
- const b = randomBytes(16);
7296
- b[6] = b[6] & 15 | 64;
7297
- b[8] = b[8] & 63 | 128;
7298
- const hex = [...b].map((x) => x.toString(16).padStart(2, "0")).join("");
7299
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
7287
+ return current;
7300
7288
  }
7301
- var HANDOFF_ATTRIBUTE_SUFFIXES = {
7302
- mode: "raindrop.handoff.mode",
7303
- childEventId: "raindrop.handoff.childEventId",
7304
- parentEventId: "raindrop.handoff.parentEventId",
7305
- parentSpanId: "raindrop.handoff.parentSpanId",
7306
- name: "raindrop.handoff.name",
7307
- terminal: "raindrop.handoff.terminal",
7308
- agentRole: "raindrop.agent.role"
7309
- };
7310
- var AI_SDK_METADATA_PREFIX = "ai.telemetry.metadata.";
7311
- var DETACHED_MODE = "detached";
7312
- var SUBAGENT_AGENT_ROLE = "subagent";
7313
- var HANDOFF_TERMINAL_CANCELLED = "cancelled";
7314
- var TOOL_EVENTS_SUFFIX = "raindrop.toolEvents";
7315
- var TOOL_EVENTS_ALLOW = "allow";
7316
- var OPERATION_ID_ATTRIBUTE = "ai.operationId";
7317
- var SUBAGENT_ABORT_OPERATION_ID = "ai.subagent.failed";
7318
- var SUBAGENT_ABORT_SPAN_NAME = SUBAGENT_ABORT_OPERATION_ID;
7319
- var DEFAULT_DISPATCH_SPAN_NAME = "launch_subagent";
7320
- var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
7321
- function defined(entries) {
7322
- const out = {};
7323
- for (const [key, value] of Object.entries(entries)) {
7324
- if (typeof value === "string" && value.trim().length > 0) out[key] = value;
7289
+ function withReplayTraceDestination(destination, callback) {
7290
+ const routing = state();
7291
+ if (routing.storage) return routing.storage.run(destination, callback);
7292
+ const previous = routing.syncDestination;
7293
+ routing.syncDestination = destination;
7294
+ try {
7295
+ return callback();
7296
+ } finally {
7297
+ routing.syncDestination = previous;
7325
7298
  }
7326
- return out;
7327
- }
7328
- function detachedDispatchMetadata(dispatch) {
7329
- return defined({
7330
- [HANDOFF_ATTRIBUTE_SUFFIXES.mode]: DETACHED_MODE,
7331
- [HANDOFF_ATTRIBUTE_SUFFIXES.childEventId]: dispatch.childEventId,
7332
- [HANDOFF_ATTRIBUTE_SUFFIXES.name]: dispatch.name
7333
- });
7334
- }
7335
- function detachedChildMetadata(link) {
7336
- return defined({
7337
- [HANDOFF_ATTRIBUTE_SUFFIXES.parentEventId]: link.parentEventId,
7338
- [HANDOFF_ATTRIBUTE_SUFFIXES.parentSpanId]: link.parentSpanId,
7339
- [HANDOFF_ATTRIBUTE_SUFFIXES.name]: link.name,
7340
- [HANDOFF_ATTRIBUTE_SUFFIXES.agentRole]: SUBAGENT_AGENT_ROLE
7341
- });
7342
7299
  }
7343
- function cancelledTerminalMetadata() {
7344
- return { [HANDOFF_ATTRIBUTE_SUFFIXES.terminal]: HANDOFF_TERMINAL_CANCELLED };
7300
+ function currentReplayTraceDestination() {
7301
+ var _a, _b;
7302
+ const routing = state();
7303
+ return (_b = (_a = routing.storage) == null ? void 0 : _a.getStore()) != null ? _b : routing.syncDestination;
7345
7304
  }
7346
- function cancelledTerminalAttributes() {
7347
- const bare = cancelledTerminalMetadata();
7348
- return { ...bare, ...withMetadataPrefix(bare) };
7305
+ function rememberReplayTraceDestination(span) {
7306
+ const destination = currentReplayTraceDestination();
7307
+ if (destination) state().spanDestinations.set(span, destination);
7349
7308
  }
7350
- function toolEventsAllowMetadata() {
7351
- return { [TOOL_EVENTS_SUFFIX]: TOOL_EVENTS_ALLOW };
7309
+ function copyReplayTraceDestination(source, target) {
7310
+ const destination = state().spanDestinations.get(source);
7311
+ if (destination) state().spanDestinations.set(target, destination);
7352
7312
  }
7353
- function withMetadataPrefix(metadata) {
7354
- const out = {};
7355
- for (const [key, value] of Object.entries(metadata)) {
7356
- out[key.startsWith(AI_SDK_METADATA_PREFIX) ? key : `${AI_SDK_METADATA_PREFIX}${key}`] = value;
7313
+ function replayTraceDestinationFor(value) {
7314
+ if (typeof value !== "object" && typeof value !== "function" || value === null) {
7315
+ return void 0;
7357
7316
  }
7358
- return out;
7317
+ return state().spanDestinations.get(value);
7359
7318
  }
7360
- var BAGGAGE_KEYS = {
7361
- eventId: "raindrop-event-id",
7362
- childEventId: "raindrop-child-event-id",
7363
- name: "raindrop-handoff-name",
7364
- convoId: "raindrop-convo-id",
7365
- userId: "raindrop-user-id",
7366
- // The dispatch span and its trace, carried as fields as well as in
7367
- // `traceparent`. Not redundant: OTel's HTTP instrumentations rewrite
7368
- // `traceparent` from whatever span is current as the request goes out, so by
7369
- // the time the child reads it the ids name the HTTP client span and its trace
7370
- // instead of the dispatch. The child would then point its reverse reference at
7371
- // a span the launcher's UI does not know as a dispatch. A field states which
7372
- // span it means rather than meaning "whoever wrote this header last".
7373
- dispatchSpanId: "raindrop-dispatch-span-id",
7374
- traceId: "raindrop-trace-id"
7375
- };
7376
- var TRACEPARENT_HEADER = "traceparent";
7377
- var BAGGAGE_HEADER = "baggage";
7378
- var W3C_TRACE_ID = /^[0-9a-f]{32}$/;
7379
- var W3C_SPAN_ID = /^[0-9a-f]{16}$/;
7380
- function isW3CTraceId(value) {
7381
- return value !== void 0 && W3C_TRACE_ID.test(value);
7319
+ function recordReplayExportedSpan(destination, spanId, traceId) {
7320
+ var _a, _b;
7321
+ if (!destination || !spanId || !traceId) return;
7322
+ (_a = destination.exportedSpanIds) == null ? void 0 : _a.add(spanId);
7323
+ (_b = destination.exportedTraceIds) == null ? void 0 : _b.add(traceId);
7382
7324
  }
7383
- function isW3CSpanId(value) {
7384
- return value !== void 0 && W3C_SPAN_ID.test(value);
7325
+ function replayTraceUrl(baseUrl) {
7326
+ return `${baseUrl.replace(/\/+$/, "")}/v1/traces`;
7385
7327
  }
7386
- var warnedNonConformantIds = false;
7387
- function warnNonConformantIdsOnce(traceId, spanId) {
7388
- if (warnedNonConformantIds) return;
7389
- warnedNonConformantIds = true;
7390
- console.warn(
7391
- `[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.`
7392
- );
7328
+ function getAsyncLocalStorageCtor() {
7329
+ return globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
7393
7330
  }
7394
- var HANDOFF_HEADER = "x-raindrop-handoff";
7395
- var LANGSMITH_TRACE_HEADER = "langsmith-trace";
7396
- var LANGSMITH_METADATA_BAGGAGE_KEY = "langsmith-metadata";
7397
- function encodeBaggage(entries) {
7398
- return Object.entries(entries).filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join(",");
7331
+ function getWeakRefCtor() {
7332
+ const ctor = globalThis.WeakRef;
7333
+ return typeof ctor === "function" ? ctor : void 0;
7399
7334
  }
7400
- function decodeBaggage(header) {
7401
- if (!header) return {};
7402
- const parsed = {};
7403
- for (const part of header.split(",")) {
7404
- const [rawKey, ...rest] = part.split("=");
7405
- if (!rawKey || rest.length === 0) continue;
7406
- const key = rawKey.trim();
7407
- let value;
7335
+ var MAX_BINDING_STACK = 128;
7336
+ var EMPTY_STACK = [];
7337
+ var ContextStackStorage = class {
7338
+ /** Emitted once when this runtime cannot support an ambient push. */
7339
+ constructor(noAmbientBindWarning) {
7340
+ this.noAmbientBindWarning = noAmbientBindWarning;
7341
+ this._als = null;
7342
+ this._enterWithUsable = false;
7343
+ this._syncStack = EMPTY_STACK;
7344
+ this._adoptionChecked = false;
7345
+ this._warnedNoAmbientBind = false;
7346
+ }
7347
+ maybeAdoptAsyncLocalStorage() {
7348
+ if (this._adoptionChecked) return;
7349
+ this._adoptionChecked = true;
7350
+ const Ctor = getAsyncLocalStorageCtor();
7351
+ if (!Ctor) return;
7352
+ const als = new Ctor();
7408
7353
  try {
7409
- value = decodeURIComponent(rest.join("=").trim());
7354
+ als.run(EMPTY_STACK, () => {
7355
+ });
7410
7356
  } catch (e) {
7411
- value = rest.join("=").trim();
7357
+ return;
7358
+ }
7359
+ this._als = als;
7360
+ if (typeof als.enterWith === "function") {
7361
+ try {
7362
+ als.enterWith(EMPTY_STACK);
7363
+ this._enterWithUsable = true;
7364
+ } catch (e) {
7365
+ this._enterWithUsable = false;
7366
+ }
7412
7367
  }
7413
- if (key in parsed && parsed[key] !== value) throw new ConflictingCarrierError(key);
7414
- parsed[key] = value;
7415
7368
  }
7416
- return parsed;
7417
- }
7418
- function carrierBaggage(carrier) {
7419
- return {
7420
- [BAGGAGE_KEYS.eventId]: carrier.eventId,
7421
- [BAGGAGE_KEYS.childEventId]: carrier.childEventId,
7422
- [BAGGAGE_KEYS.name]: carrier.name,
7423
- [BAGGAGE_KEYS.convoId]: carrier.convoId,
7424
- [BAGGAGE_KEYS.userId]: carrier.userId,
7425
- [BAGGAGE_KEYS.dispatchSpanId]: carrier.spanId,
7426
- [BAGGAGE_KEYS.traceId]: carrier.traceId
7427
- };
7428
- }
7429
- function toHeaders(carrier) {
7430
- const fields = encodeBaggage(carrierBaggage(carrier));
7431
- const headers = {
7432
- [BAGGAGE_HEADER]: fields,
7433
- [HANDOFF_HEADER]: fields
7434
- };
7435
- if (isW3CTraceId(carrier.traceId) && isW3CSpanId(carrier.spanId)) {
7436
- headers[TRACEPARENT_HEADER] = `00-${carrier.traceId}-${carrier.spanId}-01`;
7369
+ getStack() {
7370
+ var _a;
7371
+ this.maybeAdoptAsyncLocalStorage();
7372
+ if (this._als) return (_a = this._als.getStore()) != null ? _a : this._syncStack;
7373
+ return this._syncStack;
7437
7374
  }
7438
- return headers;
7439
- }
7440
- function langsmithStamp() {
7441
- return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "000000");
7442
- }
7443
- function toLangSmithHeaders(carrier) {
7444
- const stamp = langsmithStamp();
7445
- const dottedOrder = `${stamp}Z${carrier.traceId}.${stamp}Z${carrier.spanId}`;
7446
- return {
7447
- [LANGSMITH_TRACE_HEADER]: dottedOrder,
7448
- [BAGGAGE_HEADER]: encodeBaggage({
7449
- [LANGSMITH_METADATA_BAGGAGE_KEY]: JSON.stringify(carrierBaggage(carrier))
7450
- }),
7451
- // Carried here too: a LangSmith-shaped carrier is rewritten in transit
7452
- // exactly like the native one, and an injected `traceparent` is preferred
7453
- // over the dotted order on read.
7454
- [HANDOFF_HEADER]: encodeBaggage(carrierBaggage(carrier))
7455
- };
7456
- }
7457
- function isHeaderLookup(headers) {
7458
- return "get" in headers && typeof headers.get === "function";
7459
- }
7460
- var ConflictingCarrierError = class extends Error {
7461
- };
7462
- function readHeader(headers, name) {
7463
- var _a, _b, _c;
7464
- if (isHeaderLookup(headers)) return (_a = headers.get(name)) != null ? _a : void 0;
7465
- const value = (_c = headers[name]) != null ? _c : (_b = Object.entries(headers).find(([key]) => key.toLowerCase() === name)) == null ? void 0 : _b[1];
7466
- if (!Array.isArray(value)) return value;
7467
- const distinct = new Set(value);
7468
- if (distinct.size > 1) throw new ConflictingCarrierError(name);
7469
- return value[0];
7470
- }
7471
- function readTraceHeader(headers, name) {
7472
- const value = readHeader(headers, name);
7473
- if (!(value == null ? void 0 : value.includes(","))) return value;
7474
- const copies = value.split(",").map((copy) => copy.trim());
7475
- if (new Set(copies).size > 1) throw new ConflictingCarrierError(name);
7476
- return copies[0];
7477
- }
7478
- function fromHeaders(headers) {
7479
- var _a, _b;
7480
- let baggage;
7481
- let handoff;
7482
- let traceparent;
7483
- let langsmithTrace;
7484
- try {
7485
- baggage = decodeBaggage(readHeader(headers, BAGGAGE_HEADER));
7486
- handoff = decodeBaggage(readHeader(headers, HANDOFF_HEADER));
7487
- traceparent = readTraceHeader(headers, TRACEPARENT_HEADER);
7488
- langsmithTrace = readTraceHeader(headers, LANGSMITH_TRACE_HEADER);
7489
- } catch (error) {
7490
- if (error instanceof ConflictingCarrierError) return null;
7491
- throw error;
7375
+ /**
7376
+ * Persist an ambient stack mutation. Returns false when an ALS is adopted but
7377
+ * `enterWith` is unusable (workerd): callers must NOT fall back to the
7378
+ * process-shared sync stack, since concurrent isolates would cross-stamp.
7379
+ */
7380
+ setAmbientStack(stack) {
7381
+ if (this._als) {
7382
+ if (!this._enterWithUsable) return false;
7383
+ this._als.enterWith(stack);
7384
+ return true;
7385
+ }
7386
+ this._syncStack = stack;
7387
+ return true;
7492
7388
  }
7493
- const langsmithMetadata = baggage[LANGSMITH_METADATA_BAGGAGE_KEY];
7494
- const nested = langsmithMetadata ? safeParseStringRecord(langsmithMetadata) : {};
7495
- for (const [key, value] of Object.entries(nested)) {
7496
- if (key in baggage && baggage[key] !== value) return null;
7389
+ push(binding) {
7390
+ this.maybeAdoptAsyncLocalStorage();
7391
+ if (this._als && !this._enterWithUsable) {
7392
+ this.warnNoAmbientBindOnce();
7393
+ return;
7394
+ }
7395
+ let stack = this.getStack();
7396
+ if (stack.length >= MAX_BINDING_STACK) {
7397
+ stack = stack.filter((b) => b.isLive());
7398
+ if (stack.length >= MAX_BINDING_STACK) {
7399
+ stack = stack.slice(stack.length - (MAX_BINDING_STACK - 1));
7400
+ }
7401
+ }
7402
+ this.setAmbientStack([...stack, binding]);
7497
7403
  }
7498
- const fields = {
7499
- ...baggage,
7500
- ...nested,
7501
- // Last, so it wins: the standard headers may have been rewritten in transit
7502
- // by a propagator, this one is only ever written by us.
7503
- ...handoff
7504
- };
7505
- let traceId;
7506
- let spanId;
7507
- if (traceparent) {
7508
- const [, parsedTraceId, parsedSpanId] = traceparent.split("-");
7509
- if (isW3CTraceId(parsedTraceId) && isW3CSpanId(parsedSpanId)) {
7510
- traceId = parsedTraceId;
7511
- spanId = parsedSpanId;
7404
+ remove(binding) {
7405
+ binding.markDead();
7406
+ const stack = this.getStack();
7407
+ const pruned = stack.filter((b) => b !== binding && b.isLive());
7408
+ if (pruned.length !== stack.length) {
7409
+ this.setAmbientStack(pruned);
7512
7410
  }
7513
7411
  }
7514
- if (!traceId && !spanId && langsmithTrace) {
7515
- const segments = langsmithTrace.split(".");
7516
- traceId = (_a = segments[0]) == null ? void 0 : _a.split("Z").pop();
7517
- spanId = (_b = segments[segments.length - 1]) == null ? void 0 : _b.split("Z").pop();
7412
+ runWith(binding, callback) {
7413
+ this.maybeAdoptAsyncLocalStorage();
7414
+ if (this._als) {
7415
+ return this._als.run([...this.getStack(), binding], callback);
7416
+ }
7417
+ const previous = this._syncStack;
7418
+ this._syncStack = [...previous, binding];
7419
+ try {
7420
+ return callback();
7421
+ } finally {
7422
+ this._syncStack = previous;
7423
+ }
7518
7424
  }
7519
- traceId = fields[BAGGAGE_KEYS.traceId] || traceId;
7520
- spanId = fields[BAGGAGE_KEYS.dispatchSpanId] || spanId;
7521
- const eventId = fields[BAGGAGE_KEYS.eventId];
7522
- if (!traceId || !spanId || !eventId) return null;
7523
- if (!isW3CTraceId(traceId) || !isW3CSpanId(spanId)) {
7524
- warnNonConformantIdsOnce(traceId, spanId);
7425
+ current() {
7426
+ const stack = this.getStack();
7427
+ if (stack.length === 0) return void 0;
7428
+ const top = stack[stack.length - 1];
7429
+ if (top.isLive()) return top;
7430
+ const pruned = stack.filter((b) => b.isLive());
7431
+ this.setAmbientStack(pruned);
7432
+ return pruned.length > 0 ? pruned[pruned.length - 1] : void 0;
7525
7433
  }
7526
- const carrier = { traceId, spanId, eventId };
7527
- const childEventId = fields[BAGGAGE_KEYS.childEventId];
7528
- if (childEventId) carrier.childEventId = childEventId;
7529
- const name = fields[BAGGAGE_KEYS.name];
7530
- if (name) carrier.name = name;
7531
- const convoId = fields[BAGGAGE_KEYS.convoId];
7532
- if (convoId) carrier.convoId = convoId;
7533
- const userId = fields[BAGGAGE_KEYS.userId];
7534
- if (userId) carrier.userId = userId;
7535
- return carrier;
7536
- }
7537
- 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.`;
7538
- var UNRESOLVED_CARRIER_WARNED_KEY = /* @__PURE__ */ Symbol.for("raindrop.handoff.warnedUnresolvedCarrier");
7539
- function warnedHolder() {
7540
- return globalThis;
7434
+ warnNoAmbientBindOnce() {
7435
+ if (this._warnedNoAmbientBind) return;
7436
+ this._warnedNoAmbientBind = true;
7437
+ console.warn(this.noAmbientBindWarning);
7438
+ }
7439
+ };
7440
+ 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.";
7441
+ 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.";
7442
+ var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
7443
+ function routingStorage() {
7444
+ const holder = globalThis;
7445
+ let storage = holder[ROUTING_STORAGE_KEY];
7446
+ if (!storage) {
7447
+ storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
7448
+ holder[ROUTING_STORAGE_KEY] = storage;
7449
+ }
7450
+ return storage;
7541
7451
  }
7542
- function warnUnresolvedCarrier(headers, carrier) {
7543
- if (carrier) return;
7544
- if (!headers) return;
7545
- const holder = warnedHolder();
7546
- if (holder[UNRESOLVED_CARRIER_WARNED_KEY]) return;
7547
- holder[UNRESOLVED_CARRIER_WARNED_KEY] = true;
7548
- console.warn(UNRESOLVED_CARRIER_WARNING);
7452
+ function makeBoundContext(binding, owner) {
7453
+ const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
7454
+ const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
7455
+ let dead = false;
7456
+ return {
7457
+ projectId: binding.projectId,
7458
+ authHint: binding.authHint,
7459
+ ...binding.appGit ? { appGit: binding.appGit } : {},
7460
+ ...binding.appGitSource ? { appGitSource: binding.appGitSource } : {},
7461
+ isLive() {
7462
+ if (dead) return false;
7463
+ return ref === void 0 || ref.deref() !== void 0;
7464
+ },
7465
+ markDead() {
7466
+ dead = true;
7467
+ }
7468
+ };
7549
7469
  }
7550
- function safeParseStringRecord(raw) {
7470
+ function bindRoutingContext(binding, owner) {
7471
+ const bound = makeBoundContext(binding, owner);
7551
7472
  try {
7552
- const parsed = JSON.parse(raw);
7553
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
7554
- const result = {};
7555
- for (const [key, value] of Object.entries(parsed)) {
7556
- if (typeof value === "string") result[key] = value;
7557
- }
7558
- return result;
7473
+ routingStorage().push(bound);
7559
7474
  } catch (e) {
7560
- return {};
7561
7475
  }
7476
+ return bound;
7562
7477
  }
7563
- function runWithTracingSuppressed(fn) {
7564
- const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
7565
- if (typeof hook !== "function") return fn();
7566
- let started = false;
7478
+ function unbindRoutingContext(bound) {
7479
+ if (!bound || !isBoundContext(bound)) return;
7567
7480
  try {
7568
- return hook(() => {
7569
- started = true;
7570
- return fn();
7571
- });
7572
- } catch (err) {
7573
- if (started) throw err;
7574
- return fn();
7481
+ routingStorage().remove(bound);
7482
+ } catch (e) {
7575
7483
  }
7576
7484
  }
7577
- var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
7578
- var MAX_RETRY_DELAY_MS = 3e4;
7579
- function wait(ms) {
7580
- return new Promise((resolve2) => setTimeout(resolve2, ms));
7485
+ function withRoutingContext(binding, callback) {
7486
+ const bound = makeBoundContext(binding);
7487
+ return routingStorage().runWith(bound, callback);
7581
7488
  }
7582
- function formatEndpoint(endpoint) {
7583
- if (!endpoint) return void 0;
7584
- return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
7489
+ function currentRoutingBinding() {
7490
+ const bound = routingStorage().current();
7491
+ if (!bound) return void 0;
7492
+ return {
7493
+ projectId: bound.projectId,
7494
+ authHint: bound.authHint,
7495
+ ...bound.appGit ? { appGit: bound.appGit } : {},
7496
+ ...bound.appGitSource ? { appGitSource: bound.appGitSource } : {}
7497
+ };
7585
7498
  }
7586
- function redactUrlForLog(url) {
7587
- try {
7588
- const parsed = new URL(url);
7589
- parsed.username = "";
7590
- parsed.password = "";
7591
- parsed.search = "";
7592
- parsed.hash = "";
7593
- return parsed.toString();
7594
- } catch (e) {
7595
- return "<unparseable-url>";
7596
- }
7499
+ function isBoundContext(value) {
7500
+ return typeof value.isLive === "function";
7597
7501
  }
7598
- var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
7599
- var rateLimitedLogLast = /* @__PURE__ */ new Map();
7600
- function rateLimitedLog(key, log) {
7601
- const now = Date.now();
7602
- const last = rateLimitedLogLast.get(key);
7603
- if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
7604
- return false;
7502
+ var ATTRIBUTE_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.spanAttributeStorage");
7503
+ function attributeStorage() {
7504
+ const holder = globalThis;
7505
+ let storage = holder[ATTRIBUTE_STORAGE_KEY];
7506
+ if (!storage) {
7507
+ storage = new ContextStackStorage(NO_AMBIENT_ATTRIBUTE_BIND_WARNING);
7508
+ holder[ATTRIBUTE_STORAGE_KEY] = storage;
7605
7509
  }
7606
- rateLimitedLogLast.set(key, now);
7607
- log();
7608
- return true;
7510
+ return storage;
7609
7511
  }
7610
- async function raceWithTimeout(promise, timeoutMs) {
7611
- let timer;
7612
- const settledInTime = await Promise.race([
7613
- promise.then(
7614
- () => true,
7615
- () => true
7616
- ),
7617
- new Promise((resolve2) => {
7618
- var _a;
7619
- timer = setTimeout(() => resolve2(false), Math.max(0, timeoutMs));
7620
- (_a = timer.unref) == null ? void 0 : _a.call(timer);
7621
- })
7622
- ]);
7623
- if (timer) clearTimeout(timer);
7624
- return settledInTime;
7512
+ function makeAttributeFrame(attributes, owner) {
7513
+ const snapshot = { ...attributes };
7514
+ const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
7515
+ const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
7516
+ const owned = /* @__PURE__ */ new WeakSet();
7517
+ let dead = false;
7518
+ let sawError = false;
7519
+ return {
7520
+ attributes: snapshot,
7521
+ sawErrorSpan: () => sawError,
7522
+ ownsSpan: (span) => owned.has(span),
7523
+ claimSpan(span) {
7524
+ owned.add(span);
7525
+ },
7526
+ markErrorSpanSeen() {
7527
+ sawError = true;
7528
+ },
7529
+ // A frame whose owner has been collected is dead even though nothing
7530
+ // unbound it: a run that never settles must not keep handing its reverse
7531
+ // reference to whatever runs next on a reused context.
7532
+ isLive() {
7533
+ if (dead) return false;
7534
+ return ref === void 0 || ref.deref() !== void 0;
7535
+ },
7536
+ markDead() {
7537
+ dead = true;
7538
+ }
7539
+ };
7625
7540
  }
7626
- function parseRetryAfter(headers) {
7627
- var _a;
7628
- const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
7629
- if (!value) return void 0;
7630
- const asNumber = Number(value);
7631
- if (value.trim() !== "" && !Number.isNaN(asNumber)) return asNumber * 1e3;
7632
- const asDate = new Date(value).getTime();
7633
- if (!Number.isNaN(asDate)) {
7634
- const delta = asDate - Date.now();
7635
- return delta > 0 ? delta : 0;
7541
+ function bindSpanAttributes(attributes, owner) {
7542
+ if (Object.keys(attributes).length === 0) return void 0;
7543
+ const frame = makeAttributeFrame(attributes, owner);
7544
+ try {
7545
+ attributeStorage().push(frame);
7546
+ } catch (e) {
7547
+ return void 0;
7636
7548
  }
7637
- return void 0;
7549
+ return frame;
7638
7550
  }
7639
- function getRetryDelayMs(attemptNumber, previousError) {
7640
- if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
7641
- const v = previousError.retryAfterMs;
7642
- if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
7551
+ function unbindSpanAttributes(frame) {
7552
+ if (!frame || !isAttributeFrame(frame)) return;
7553
+ try {
7554
+ attributeStorage().remove(frame);
7555
+ } catch (e) {
7643
7556
  }
7644
- if (attemptNumber <= 1) return 0;
7645
- const base = 500;
7646
- const factor = Math.pow(2, attemptNumber - 2);
7647
- return Math.min(base * factor, MAX_RETRY_DELAY_MS);
7648
7557
  }
7649
- async function withRetry(operation, opName, opts) {
7650
- const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
7651
- let lastError = void 0;
7652
- for (let attemptNumber = 1; attemptNumber <= opts.maxAttempts; attemptNumber++) {
7653
- if (attemptNumber > 1) {
7654
- const delay = getRetryDelayMs(attemptNumber, lastError);
7655
- if (opts.debug) {
7656
- console.warn(
7657
- `${prefix} ${opName} retry ${attemptNumber}/${opts.maxAttempts} in ${delay}ms`
7658
- );
7659
- }
7660
- if (delay > 0) await wait(delay);
7661
- } else if (opts.debug) {
7662
- console.log(`${prefix} ${opName} attempt ${attemptNumber}/${opts.maxAttempts}`);
7663
- }
7664
- try {
7665
- return await operation();
7666
- } catch (err) {
7667
- lastError = err;
7668
- if (opts.debug) {
7669
- const msg = err instanceof Error ? err.message : String(err);
7670
- console.warn(
7671
- `${prefix} ${opName} attempt ${attemptNumber} failed: ${msg}${attemptNumber === opts.maxAttempts ? " (no more retries)" : ""}`
7672
- );
7558
+ function applySpanAttributeFrames(span, setAttribute) {
7559
+ try {
7560
+ for (const frame of attributeStorage().getStack()) {
7561
+ if (!frame.isLive()) continue;
7562
+ frame.claimSpan(span);
7563
+ for (const [key, value] of Object.entries(frame.attributes)) {
7564
+ setAttribute(key, value);
7673
7565
  }
7674
- if (lastError && typeof lastError === "object" && "retryable" in lastError && !lastError.retryable)
7675
- break;
7676
- if (attemptNumber === opts.maxAttempts) break;
7677
7566
  }
7567
+ } catch (e) {
7678
7568
  }
7679
- throw lastError instanceof Error ? lastError : new Error(String(lastError));
7680
- }
7681
- async function postJson(url, body, headers, opts) {
7682
- var _a;
7683
- const opName = `POST ${redactUrlForLog(url)}`;
7684
- const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
7685
- await withRetry(
7686
- async () => {
7687
- const resp = await runWithTracingSuppressed(
7688
- () => fetch(url, {
7689
- method: "POST",
7690
- redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
7691
- headers: {
7692
- "Content-Type": "application/json",
7693
- ...headers
7694
- },
7695
- body: JSON.stringify(body),
7696
- signal: AbortSignal.timeout(timeoutMs)
7697
- })
7698
- );
7699
- if (!resp.ok) {
7700
- const text = await resp.text().catch(() => "");
7701
- const err = new Error(
7702
- `HTTP ${resp.status} ${resp.statusText}${text ? `: ${text}` : ""}`
7703
- );
7704
- const retryAfterMs = parseRetryAfter(resp.headers);
7705
- if (typeof retryAfterMs === "number") err.retryAfterMs = retryAfterMs;
7706
- err.retryable = resp.status === 429 || resp.status >= 500;
7707
- throw err;
7708
- }
7709
- },
7710
- opName,
7711
- opts
7712
- );
7713
7569
  }
7714
- var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
7715
- var TRUNCATION_MARKER = "...[truncated by raindrop]";
7716
- var BOUNDED_CLONE_MAX_DEPTH = 12;
7717
- var BOUNDED_CLONE_SLACK = 256;
7718
- var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
7719
- function setDefaultMaxTextFieldChars(value) {
7720
- if (typeof value === "number" && Number.isFinite(value) && value > 0) {
7721
- currentDefaultMaxTextFieldChars = Math.floor(value);
7570
+ function claimSpanForFrames(span) {
7571
+ try {
7572
+ for (const frame of attributeStorage().getStack()) {
7573
+ if (frame.isLive()) frame.claimSpan(span);
7574
+ }
7575
+ } catch (e) {
7722
7576
  }
7723
7577
  }
7724
- function resolveMaxTextFieldChars(value) {
7725
- if (typeof value === "number" && Number.isFinite(value) && value > 0) {
7726
- return Math.floor(value);
7578
+ function currentSpanAttributes() {
7579
+ const merged = {};
7580
+ try {
7581
+ for (const frame of attributeStorage().getStack()) {
7582
+ if (!frame.isLive()) continue;
7583
+ Object.assign(merged, frame.attributes);
7584
+ }
7585
+ } catch (e) {
7727
7586
  }
7728
- return currentDefaultMaxTextFieldChars;
7587
+ return merged;
7729
7588
  }
7730
- function truncateToLimit(text, limit) {
7731
- if (limit > TRUNCATION_MARKER.length) {
7732
- return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
7589
+ function markErrorSpanSeen(span) {
7590
+ try {
7591
+ for (const frame of attributeStorage().getStack()) {
7592
+ if (frame.ownsSpan(span)) frame.markErrorSpanSeen();
7593
+ }
7594
+ } catch (e) {
7733
7595
  }
7734
- return text.slice(0, Math.max(0, limit));
7735
7596
  }
7736
- function capText(value, limit) {
7737
- if (typeof value !== "string") return value;
7738
- const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
7739
- if (value.length <= max) return value;
7740
- return truncateToLimit(value, max);
7597
+ function isAttributeFrame(value) {
7598
+ return typeof value.isLive === "function";
7741
7599
  }
7742
- function boundedClone(obj, charBudget) {
7743
- let budget = charBudget;
7744
- const seen = /* @__PURE__ */ new WeakSet();
7745
- const walk = (node, depth) => {
7746
- if (budget <= 0) return TRUNCATION_MARKER;
7747
- if (typeof node === "string") {
7748
- if (node.length > budget) {
7749
- const taken = node.slice(0, Math.max(0, budget)) + TRUNCATION_MARKER;
7750
- budget = 0;
7751
- return taken;
7600
+ var APP_COMMIT_SHA = "raindrop.app.commit_sha";
7601
+ var APP_COMMIT_DIRTY = "raindrop.app.commit_dirty";
7602
+ var APP_BRANCH = "raindrop.app.branch";
7603
+ var APP_GIT_KEYS = [APP_COMMIT_SHA, APP_COMMIT_DIRTY, APP_BRANCH];
7604
+ var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
7605
+ function appGitExplicitKeys(properties) {
7606
+ const keys = /* @__PURE__ */ new Set();
7607
+ if (!properties) return keys;
7608
+ for (const key of APP_GIT_KEYS) {
7609
+ if (own(properties, key)) keys.add(key);
7610
+ }
7611
+ return keys;
7612
+ }
7613
+ function mergeAppGitSnapshot(base, properties, explicitKeys = /* @__PURE__ */ new Set()) {
7614
+ try {
7615
+ const result = { ...base };
7616
+ if (properties && own(properties, APP_COMMIT_SHA) && properties[APP_COMMIT_SHA] !== result[APP_COMMIT_SHA]) {
7617
+ if (!explicitKeys.has(APP_COMMIT_DIRTY) && !own(properties, APP_COMMIT_DIRTY)) {
7618
+ delete result[APP_COMMIT_DIRTY];
7752
7619
  }
7753
- budget -= Math.max(node.length, 1);
7754
- return node;
7755
- }
7756
- if (node === null || typeof node === "number" || typeof node === "boolean") {
7757
- budget -= 8;
7758
- return node;
7759
- }
7760
- if (typeof node !== "object") {
7761
- budget -= 8;
7762
- return node;
7763
- }
7764
- if (seen.has(node)) return "[CIRCULAR]";
7765
- if (depth >= BOUNDED_CLONE_MAX_DEPTH) {
7766
- budget -= 16;
7767
- return `<max depth>`;
7768
- }
7769
- seen.add(node);
7770
- if (Array.isArray(node)) {
7771
- const out2 = [];
7772
- for (const v of node) {
7773
- if (budget <= 0) {
7774
- out2.push(TRUNCATION_MARKER);
7775
- break;
7776
- }
7777
- out2.push(walk(v, depth + 1));
7620
+ if (!explicitKeys.has(APP_BRANCH) && !own(properties, APP_BRANCH)) {
7621
+ delete result[APP_BRANCH];
7778
7622
  }
7779
- return out2;
7780
7623
  }
7781
- if (typeof node.toJSON === "function") {
7782
- budget -= 16;
7783
- return node;
7784
- }
7785
- const out = {};
7786
- for (const k in node) {
7787
- if (!Object.prototype.hasOwnProperty.call(node, k)) continue;
7788
- if (budget <= 0) {
7789
- out["..."] = TRUNCATION_MARKER;
7790
- break;
7791
- }
7792
- const key = walk(k, depth + 1);
7793
- out[key] = walk(node[k], depth + 1);
7624
+ for (const key of APP_GIT_KEYS) {
7625
+ if (properties && own(properties, key)) result[key] = properties[key];
7794
7626
  }
7795
- return out;
7796
- };
7797
- return walk(obj, 0);
7798
- }
7799
- function stringifyBounded(value, limit) {
7800
- const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
7801
- if (typeof value === "string") {
7802
- return capText(value, max);
7803
- }
7804
- const pruned = boundedClone(value, max + TRUNCATION_MARKER.length + BOUNDED_CLONE_SLACK);
7805
- let json;
7806
- try {
7807
- json = JSON.stringify(pruned);
7627
+ return Object.freeze(result);
7808
7628
  } catch (e) {
7809
- json = void 0;
7810
- }
7811
- if (json === void 0) {
7812
- return capText(String(value), max);
7629
+ return Object.freeze({});
7813
7630
  }
7814
- return json.length <= max ? json : truncateToLimit(json, max);
7815
7631
  }
7816
- function attrString(key, value) {
7817
- if (value === void 0) return void 0;
7818
- return { key, value: { stringValue: value } };
7632
+ function validAppCommitSha(value) {
7633
+ if (typeof value !== "string" || value.length > 128) return void 0;
7634
+ const cleaned = value.trim();
7635
+ return /^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/.test(cleaned) ? cleaned.toLowerCase() : void 0;
7819
7636
  }
7820
- function attrInt(key, value) {
7821
- if (value === void 0) return void 0;
7822
- if (!Number.isFinite(value)) return void 0;
7823
- return { key, value: { intValue: String(Math.trunc(value)) } };
7637
+ function shortString(value) {
7638
+ if (typeof value !== "string" || value.length > 1024) return void 0;
7639
+ const cleaned = value.trim();
7640
+ return cleaned.length > 0 ? cleaned : void 0;
7824
7641
  }
7825
- function attrDouble(key, value) {
7826
- if (value === void 0) return void 0;
7827
- if (!Number.isFinite(value)) return void 0;
7828
- return { key, value: { doubleValue: value } };
7642
+ function flag(value) {
7643
+ if (value === true || value === "true" || value === "1") return true;
7644
+ if (value === false || value === "false" || value === "0") return false;
7645
+ return void 0;
7829
7646
  }
7830
- var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
7831
- function state() {
7832
- const holder = globalThis;
7833
- let current = holder[STATE_KEY];
7834
- if (!current) {
7835
- let storage = null;
7647
+ function environment() {
7648
+ try {
7649
+ return typeof process === "undefined" ? {} : process.env;
7650
+ } catch (e) {
7651
+ return {};
7652
+ }
7653
+ }
7654
+ var DEPLOYMENTS = [
7655
+ ["VERCEL", "VERCEL_GIT_COMMIT_SHA", "VERCEL_GIT_COMMIT_REF"],
7656
+ ["NETLIFY", "COMMIT_REF", "BRANCH"],
7657
+ ["RAILWAY_ENVIRONMENT_NAME", "RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH"],
7658
+ ["RAILWAY_ENVIRONMENT", "RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH"],
7659
+ ["RENDER", "RENDER_GIT_COMMIT", "RENDER_GIT_BRANCH"],
7660
+ ["DYNO", "HEROKU_BUILD_COMMIT", ""],
7661
+ ["DYNO", "HEROKU_SLUG_COMMIT", ""],
7662
+ ["AWS_APP_ID", "AWS_COMMIT_ID", "AWS_BRANCH"],
7663
+ ["WORKERS_CI", "WORKERS_CI_COMMIT_SHA", "WORKERS_CI_BRANCH"]
7664
+ ];
7665
+ var CI_SOURCES = [
7666
+ ["GITHUB_ACTIONS", "GITHUB_SHA", "GITHUB_REF_NAME"],
7667
+ ["GITLAB_CI", "CI_COMMIT_SHA", "CI_COMMIT_REF_NAME"],
7668
+ ["CIRCLECI", "CIRCLE_SHA1", "CIRCLE_BRANCH"]
7669
+ ];
7670
+ function detectedSource(env, table, detectBranch) {
7671
+ for (const row of table) {
7672
+ const [marker, shaKey, branchKey] = row;
7673
+ if (!marker || !shaKey || !shortString(env[marker]) || flag(env[marker]) === false) continue;
7674
+ const commitSha = validAppCommitSha(env[shaKey]);
7675
+ if (commitSha)
7676
+ return {
7677
+ commitSha,
7678
+ ...detectBranch && branchKey ? { branch: shortString(env[branchKey]) } : {}
7679
+ };
7680
+ }
7681
+ return {};
7682
+ }
7683
+ var AppGitContextImpl = class {
7684
+ constructor(options, runtime = {}) {
7685
+ this.defaults = {};
7686
+ this.explicit = {};
7687
+ this.explicitKeys = /* @__PURE__ */ new Set();
7688
+ this.disposed = false;
7689
+ this.operations = /* @__PURE__ */ new Map();
7690
+ this.operationOverflow = false;
7691
+ this.detectBranch = false;
7692
+ this.disabled = false;
7693
+ var _a, _b, _c, _d, _e, _f;
7836
7694
  try {
7837
- const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
7838
- if (Ctor) storage = new Ctor();
7695
+ if (options === false) {
7696
+ this.disabled = true;
7697
+ return;
7698
+ }
7699
+ const opts = options != null ? options : {};
7700
+ const env = (_a = runtime.env) != null ? _a : environment();
7701
+ const detectBranch = (_c = (_b = flag(opts.detectBranch)) != null ? _b : flag(env.RAINDROP_GIT_DETECT_BRANCH)) != null ? _c : false;
7702
+ this.detectBranch = detectBranch;
7703
+ const collectBranch = detectBranch || runtime.captureLegacyReplayBranch === true;
7704
+ const autoDetect = runtime.allowDiscovery === false ? false : (_e = (_d = flag(opts.autoDetect)) != null ? _d : flag(env.RAINDROP_GIT_AUTO_DETECT)) != null ? _e : true;
7705
+ const configuredSha = opts.commitSha;
7706
+ const explicitSha = configuredSha === void 0 ? env.RAINDROP_COMMIT_SHA : configuredSha;
7707
+ if (explicitSha === null) this.explicit[APP_COMMIT_SHA] = null;
7708
+ else if (typeof explicitSha === "string" && validAppCommitSha(explicitSha))
7709
+ this.explicit[APP_COMMIT_SHA] = explicitSha;
7710
+ const configuredDirty = opts.commitDirty;
7711
+ const explicitDirty = configuredDirty === void 0 ? flag(env.RAINDROP_COMMIT_DIRTY) : configuredDirty;
7712
+ if (explicitDirty === null || typeof explicitDirty === "boolean")
7713
+ this.explicit[APP_COMMIT_DIRTY] = explicitDirty;
7714
+ const configuredBranch = opts.branch;
7715
+ const explicitBranch = configuredBranch === void 0 ? env.RAINDROP_BRANCH : configuredBranch;
7716
+ if (explicitBranch === null) this.explicit[APP_BRANCH] = null;
7717
+ else if (typeof explicitBranch === "string" && explicitBranch.length <= 1024)
7718
+ this.explicit[APP_BRANCH] = explicitBranch;
7719
+ this.explicitKeys = appGitExplicitKeys(this.explicit);
7720
+ this.defaults = { ...this.explicit };
7721
+ if (!autoDetect || explicitSha !== void 0) return;
7722
+ const sourceDirectory = (_f = shortString(opts.sourceDirectory)) != null ? _f : shortString(env.RAINDROP_GIT_SOURCE_DIRECTORY);
7723
+ if (runtime.allowLocalGit === false && !sourceDirectory) return;
7724
+ const deploy = sourceDirectory ? {} : detectedSource(env, DEPLOYMENTS, collectBranch);
7725
+ if (deploy.commitSha) {
7726
+ this.accept(deploy);
7727
+ return;
7728
+ }
7729
+ const fallback = sourceDirectory ? {} : detectedSource(env, CI_SOURCES, collectBranch);
7730
+ if (runtime.resolveLocal && (runtime.allowLocalGit !== false || sourceDirectory)) {
7731
+ this.cancel = runtime.resolveLocal(sourceDirectory, collectBranch, (result) => {
7732
+ try {
7733
+ if (!this.disposed) this.accept(result.commitSha ? result : fallback);
7734
+ } catch (e) {
7735
+ }
7736
+ });
7737
+ } else this.accept(fallback);
7839
7738
  } catch (e) {
7840
- storage = null;
7841
7739
  }
7842
- current = {
7843
- storage,
7844
- syncDestination: void 0,
7845
- spanDestinations: /* @__PURE__ */ new WeakMap()
7740
+ }
7741
+ accept(result) {
7742
+ const sha = validAppCommitSha(result.commitSha);
7743
+ if (!sha) return;
7744
+ this.replayBranch = shortString(result.branch);
7745
+ this.defaults = {
7746
+ [APP_COMMIT_SHA]: sha,
7747
+ ...typeof result.commitDirty === "boolean" ? { [APP_COMMIT_DIRTY]: result.commitDirty } : {},
7748
+ ...this.detectBranch && this.replayBranch ? { [APP_BRANCH]: this.replayBranch } : {},
7749
+ ...this.explicit
7846
7750
  };
7847
- holder[STATE_KEY] = current;
7848
7751
  }
7849
- return current;
7850
- }
7851
- function withReplayTraceDestination(destination, callback) {
7852
- const routing = state();
7853
- if (routing.storage) return routing.storage.run(destination, callback);
7854
- const previous = routing.syncDestination;
7855
- routing.syncDestination = destination;
7856
- try {
7857
- return callback();
7858
- } finally {
7859
- routing.syncDestination = previous;
7752
+ snapshot(properties) {
7753
+ var _a;
7754
+ try {
7755
+ const binding = currentRoutingBinding();
7756
+ const base = this.disabled && (binding == null ? void 0 : binding.appGitSource) !== "replay-override" ? {} : (_a = binding == null ? void 0 : binding.appGit) != null ? _a : this.defaults;
7757
+ return this.mergeSnapshot(base, properties);
7758
+ } catch (e) {
7759
+ return Object.freeze({});
7760
+ }
7860
7761
  }
7861
- }
7862
- function currentReplayTraceDestination() {
7863
- var _a, _b;
7864
- const routing = state();
7865
- return (_b = (_a = routing.storage) == null ? void 0 : _a.getStore()) != null ? _b : routing.syncDestination;
7866
- }
7867
- function rememberReplayTraceDestination(span) {
7868
- const destination = currentReplayTraceDestination();
7869
- if (destination) state().spanDestinations.set(span, destination);
7870
- }
7871
- function copyReplayTraceDestination(source, target) {
7872
- const destination = state().spanDestinations.get(source);
7873
- if (destination) state().spanDestinations.set(target, destination);
7874
- }
7875
- function replayTraceDestinationFor(value) {
7876
- if (typeof value !== "object" && typeof value !== "function" || value === null) {
7877
- return void 0;
7762
+ /** Safe defaults when no operation/span start was observed; never infer identity. */
7763
+ snapshotExplicit(properties, eventId) {
7764
+ try {
7765
+ const base = { ...this.explicit };
7766
+ const operation = eventId ? this.operations.get(eventId) : void 0;
7767
+ if (operation) {
7768
+ for (const key of operation.explicitKeys) base[key] = operation.snapshot[key];
7769
+ }
7770
+ return this.mergeSnapshot(base, properties, new Set(Object.keys(base)));
7771
+ } catch (e) {
7772
+ return mergeAppGitSnapshot({}, properties);
7773
+ }
7774
+ }
7775
+ /** Existing replay branch reporting stays separate from opt-in event properties. */
7776
+ snapshotForReplay() {
7777
+ const snapshot = this.snapshot();
7778
+ if (this.replayBranch && !own(snapshot, APP_BRANCH) && snapshot[APP_COMMIT_SHA] === this.defaults[APP_COMMIT_SHA]) {
7779
+ return Object.freeze({ ...snapshot, [APP_BRANCH]: this.replayBranch });
7780
+ }
7781
+ return snapshot;
7782
+ }
7783
+ /** Merge a frozen span snapshot without losing client-authored provenance. */
7784
+ mergeSnapshot(base, properties, explicitKeys = this.explicitKeys) {
7785
+ return mergeAppGitSnapshot(base, properties, /* @__PURE__ */ new Set([...this.explicitKeys, ...explicitKeys]));
7786
+ }
7787
+ /** Shippers belonging to one client share this bounded operation registry. */
7788
+ forOperation(eventId, properties, owner) {
7789
+ if (!eventId) return this.snapshot(properties);
7790
+ let operation = this.operations.get(eventId);
7791
+ if (!operation) {
7792
+ if (this.operations.size >= 1e4) this.operationOverflow = true;
7793
+ if (this.operationOverflow) return this.mergeSnapshot({}, properties);
7794
+ operation = {
7795
+ snapshot: this.snapshot(properties),
7796
+ owners: /* @__PURE__ */ new Set(),
7797
+ unowned: owner === void 0,
7798
+ explicitKeys: appGitExplicitKeys(properties)
7799
+ };
7800
+ this.operations.set(eventId, operation);
7801
+ } else if (properties && APP_GIT_KEYS.some((key) => own(properties, key))) {
7802
+ const explicitKeys = /* @__PURE__ */ new Set([...this.explicitKeys, ...operation.explicitKeys]);
7803
+ operation.snapshot = this.mergeSnapshot(operation.snapshot, properties, explicitKeys);
7804
+ for (const key of appGitExplicitKeys(properties)) operation.explicitKeys.add(key);
7805
+ }
7806
+ if (!owner) operation.unowned = true;
7807
+ if (owner) operation.owners.add(owner);
7808
+ return operation.snapshot;
7809
+ }
7810
+ releaseOperation(eventId, owner) {
7811
+ const operation = this.operations.get(eventId);
7812
+ if (!operation) return;
7813
+ operation.owners.delete(owner);
7814
+ if (operation.owners.size === 0 && !operation.unowned) this.operations.delete(eventId);
7815
+ }
7816
+ finishOperation(eventId) {
7817
+ this.operations.delete(eventId);
7818
+ }
7819
+ dispose() {
7820
+ var _a;
7821
+ this.disposed = true;
7822
+ try {
7823
+ (_a = this.cancel) == null ? void 0 : _a.call(this);
7824
+ } catch (e) {
7825
+ }
7878
7826
  }
7879
- return state().spanDestinations.get(value);
7880
- }
7881
- function recordReplayExportedSpan(destination, spanId, traceId) {
7882
- var _a, _b;
7883
- if (!destination || !spanId || !traceId) return;
7884
- (_a = destination.exportedSpanIds) == null ? void 0 : _a.add(spanId);
7885
- (_b = destination.exportedTraceIds) == null ? void 0 : _b.add(traceId);
7886
- }
7887
- function replayTraceUrl(baseUrl) {
7888
- return `${baseUrl.replace(/\/+$/, "")}/v1/traces`;
7889
- }
7890
- var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
7891
- var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
7892
- var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
7893
- function readEnvVar(name) {
7894
- var _a;
7827
+ };
7828
+ var AppGitContext = AppGitContextImpl;
7829
+ function withAppGitProperties(properties, snapshot) {
7895
7830
  try {
7896
- const env = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env;
7897
- if (env && typeof env[name] === "string" && env[name].length > 0) {
7898
- return env[name];
7899
- }
7831
+ const merged = { ...properties };
7832
+ for (const key of APP_GIT_KEYS)
7833
+ if (!own(merged, key) && own(snapshot, key)) merged[key] = snapshot[key];
7834
+ return merged;
7900
7835
  } catch (e) {
7836
+ return properties != null ? properties : {};
7901
7837
  }
7902
- return void 0;
7903
- }
7904
- function readWorkshopEnv() {
7905
- const raw = readEnvVar(WORKSHOP_ENV_VAR);
7906
- if (raw === void 0) return void 0;
7907
- const trimmed = raw.trim();
7908
- if (trimmed.length === 0) return void 0;
7909
- if (/^https?:\/\//i.test(trimmed)) return { url: trimmed };
7910
- if (/^(1|true|yes|on)$/i.test(trimmed)) return "enable";
7911
- if (/^(0|false|no|off)$/i.test(trimmed)) return "disable";
7912
- return void 0;
7913
- }
7914
- function isLocalDevHost(hostname) {
7915
- if (!hostname) return false;
7916
- if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1") {
7917
- return true;
7918
- }
7919
- if (hostname.endsWith(".localhost")) return true;
7920
- return false;
7921
7838
  }
7922
- function readRuntimeHostname() {
7839
+ function withAppGitAttributes(attributes, snapshot) {
7923
7840
  try {
7924
- const loc = globalThis == null ? void 0 : globalThis.location;
7925
- if (loc && typeof loc.hostname === "string" && loc.hostname.length > 0) {
7926
- return loc.hostname;
7841
+ const present = new Set(attributes.flatMap((attr) => attr ? [attr.key] : []));
7842
+ const added = [];
7843
+ for (const key of APP_GIT_KEYS) {
7844
+ if (present.has(key) || present.has(`ai.telemetry.metadata.${key}`) || present.has(`traceloop.association.properties.${key}`))
7845
+ continue;
7846
+ const value = snapshot[key];
7847
+ if (typeof value === "string") added.push({ key, value: { stringValue: value } });
7848
+ else if (typeof value === "boolean") added.push({ key, value: { boolValue: value } });
7927
7849
  }
7850
+ return added.length ? [...attributes, ...added] : attributes;
7928
7851
  } catch (e) {
7852
+ return attributes;
7929
7853
  }
7930
- return void 0;
7931
7854
  }
7932
- function shouldAutoEnableLocalWorkshop() {
7933
- if (isLocalDevHost(readRuntimeHostname())) return true;
7934
- if (readEnvVar("NODE_ENV") === "development") return true;
7935
- return false;
7855
+ function normalizeFeatureFlags(input) {
7856
+ if (Array.isArray(input)) {
7857
+ return Object.fromEntries(input.map((name) => [name, "true"]));
7858
+ }
7859
+ return Object.fromEntries(
7860
+ Object.entries(input).map(([k, v]) => [k, String(v)])
7861
+ );
7936
7862
  }
7937
- function resolveLocalDebuggerBaseUrl(baseUrl) {
7938
- var _a, _b, _c;
7939
- if (baseUrl === null) return null;
7940
- if (typeof baseUrl === "string" && baseUrl.length > 0) {
7941
- return (_a = formatEndpoint(baseUrl)) != null ? _a : null;
7863
+ function getCrypto() {
7864
+ const c = globalThis.crypto;
7865
+ return c;
7866
+ }
7867
+ function randomBytes(length) {
7868
+ const cryptoObj = getCrypto();
7869
+ const out = new Uint8Array(length);
7870
+ if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
7871
+ cryptoObj.getRandomValues(out);
7872
+ return out;
7942
7873
  }
7943
- const explicitUrlEnv = readEnvVar(LOCAL_DEBUGGER_ENV_VAR);
7944
- if (explicitUrlEnv) return (_b = formatEndpoint(explicitUrlEnv)) != null ? _b : null;
7945
- const workshopEnv = readWorkshopEnv();
7946
- if (workshopEnv === "disable") return null;
7947
- if (workshopEnv === "enable") return DEFAULT_LOCAL_WORKSHOP_URL;
7948
- if (workshopEnv && "url" in workshopEnv) return (_c = formatEndpoint(workshopEnv.url)) != null ? _c : null;
7949
- if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
7950
- return null;
7874
+ for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
7875
+ return out;
7951
7876
  }
7952
- function localDebuggerEnabled(baseUrl) {
7953
- return resolveLocalDebuggerBaseUrl(baseUrl) !== null;
7877
+ function randomUUID() {
7878
+ const cryptoObj = getCrypto();
7879
+ if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
7880
+ return cryptoObj.randomUUID();
7881
+ }
7882
+ const b = randomBytes(16);
7883
+ b[6] = b[6] & 15 | 64;
7884
+ b[8] = b[8] & 63 | 128;
7885
+ const hex = [...b].map((x) => x.toString(16).padStart(2, "0")).join("");
7886
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
7954
7887
  }
7955
- var LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS = 2e3;
7956
- function mirrorTraceExportToLocalDebugger(body, options = {}) {
7957
- var _a;
7958
- const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
7959
- if (!baseUrl) return Promise.resolve();
7960
- return postJson(`${baseUrl}traces`, body, {}, {
7961
- maxAttempts: 1,
7962
- debug: (_a = options.debug) != null ? _a : false,
7963
- sdkName: options.sdkName,
7964
- timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
7965
- }).catch(() => {
7966
- });
7888
+ var HANDOFF_ATTRIBUTE_SUFFIXES = {
7889
+ mode: "raindrop.handoff.mode",
7890
+ childEventId: "raindrop.handoff.childEventId",
7891
+ parentEventId: "raindrop.handoff.parentEventId",
7892
+ parentSpanId: "raindrop.handoff.parentSpanId",
7893
+ name: "raindrop.handoff.name",
7894
+ terminal: "raindrop.handoff.terminal",
7895
+ agentRole: "raindrop.agent.role"
7896
+ };
7897
+ var AI_SDK_METADATA_PREFIX = "ai.telemetry.metadata.";
7898
+ var DETACHED_MODE = "detached";
7899
+ var SUBAGENT_AGENT_ROLE = "subagent";
7900
+ var HANDOFF_TERMINAL_CANCELLED = "cancelled";
7901
+ var TOOL_EVENTS_SUFFIX = "raindrop.toolEvents";
7902
+ var TOOL_EVENTS_ALLOW = "allow";
7903
+ var OPERATION_ID_ATTRIBUTE = "ai.operationId";
7904
+ var SUBAGENT_ABORT_OPERATION_ID = "ai.subagent.failed";
7905
+ var SUBAGENT_ABORT_SPAN_NAME = SUBAGENT_ABORT_OPERATION_ID;
7906
+ var DEFAULT_DISPATCH_SPAN_NAME = "launch_subagent";
7907
+ var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
7908
+ function defined(entries) {
7909
+ const out = {};
7910
+ for (const [key, value] of Object.entries(entries)) {
7911
+ if (typeof value === "string" && value.trim().length > 0) out[key] = value;
7912
+ }
7913
+ return out;
7967
7914
  }
7968
- function mirrorPartialEventToLocalDebugger(event, options = {}) {
7969
- var _a;
7970
- const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
7971
- if (!baseUrl) return Promise.resolve();
7972
- const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
7973
- return postJson(`${baseUrl}events/track_partial`, event, headers, {
7974
- maxAttempts: 1,
7975
- debug: (_a = options.debug) != null ? _a : false,
7976
- sdkName: options.sdkName,
7977
- timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
7978
- }).catch(() => {
7915
+ function detachedDispatchMetadata(dispatch) {
7916
+ return defined({
7917
+ [HANDOFF_ATTRIBUTE_SUFFIXES.mode]: DETACHED_MODE,
7918
+ [HANDOFF_ATTRIBUTE_SUFFIXES.childEventId]: dispatch.childEventId,
7919
+ [HANDOFF_ATTRIBUTE_SUFFIXES.name]: dispatch.name
7979
7920
  });
7980
7921
  }
7981
- function sendLocalDebuggerLiveEvent(event, options = {}) {
7982
- var _a, _b;
7983
- const destination = currentReplayTraceDestination();
7984
- if (destination && destination.kind !== "workshop") return;
7985
- const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
7986
- if (!baseUrl) return;
7987
- void postJson(
7988
- `${baseUrl}live`,
7989
- {
7990
- ...event,
7991
- type: event.type,
7992
- timestamp: (_a = event.timestamp) != null ? _a : Date.now()
7993
- },
7994
- {},
7995
- {
7996
- maxAttempts: 1,
7997
- debug: (_b = options.debug) != null ? _b : false,
7998
- sdkName: options.sdkName
7999
- }
8000
- ).catch(() => {
7922
+ function detachedChildMetadata(link) {
7923
+ return defined({
7924
+ [HANDOFF_ATTRIBUTE_SUFFIXES.parentEventId]: link.parentEventId,
7925
+ [HANDOFF_ATTRIBUTE_SUFFIXES.parentSpanId]: link.parentSpanId,
7926
+ [HANDOFF_ATTRIBUTE_SUFFIXES.name]: link.name,
7927
+ [HANDOFF_ATTRIBUTE_SUFFIXES.agentRole]: SUBAGENT_AGENT_ROLE
8001
7928
  });
8002
7929
  }
8003
- var PROJECT_ID_HEADER = "X-Raindrop-Project-Id";
8004
- var PROJECT_ID_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
8005
- function isValidProjectIdSlug(value) {
8006
- return PROJECT_ID_SLUG_PATTERN.test(value);
7930
+ function cancelledTerminalMetadata() {
7931
+ return { [HANDOFF_ATTRIBUTE_SUFFIXES.terminal]: HANDOFF_TERMINAL_CANCELLED };
8007
7932
  }
8008
- function normalizeProjectId(raw, opts) {
8009
- if (typeof raw !== "string") return void 0;
8010
- const trimmed = raw.trim();
8011
- if (!trimmed) return void 0;
8012
- if (!isValidProjectIdSlug(trimmed) && opts.debug) {
8013
- console.warn(
8014
- `${opts.prefix} projectId "${trimmed}" does not match slug ${PROJECT_ID_SLUG_PATTERN.source}; sending anyway \u2014 backend may reject with HTTP 400`
8015
- );
8016
- }
8017
- return trimmed;
7933
+ function cancelledTerminalAttributes() {
7934
+ const bare = cancelledTerminalMetadata();
7935
+ return { ...bare, ...withMetadataPrefix(bare) };
8018
7936
  }
8019
- function projectIdHeaders(projectId) {
8020
- return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
7937
+ function toolEventsAllowMetadata() {
7938
+ return { [TOOL_EVENTS_SUFFIX]: TOOL_EVENTS_ALLOW };
8021
7939
  }
8022
- var SHUTDOWN_DEADLINE_MS = 1e4;
8023
- var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
8024
- var MODEL_USAGE_ATTRIBUTES = {
8025
- usageSource: "raindrop.usage.source",
8026
- providerName: "gen_ai.provider.name",
8027
- requestModel: "gen_ai.request.model",
8028
- responseModel: "gen_ai.response.model",
8029
- inputTokens: "gen_ai.usage.input_tokens",
8030
- outputTokens: "gen_ai.usage.output_tokens",
8031
- reasoningTokens: "gen_ai.usage.reasoning_tokens",
8032
- cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
8033
- cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
8034
- providerReportedCost: "gen_ai.usage.provider_reported_cost",
8035
- nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
8036
- nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
8037
- totalOutputTokens: "raindrop.usage.output_tokens.total",
8038
- reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
8039
- cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
8040
- cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
7940
+ function withMetadataPrefix(metadata) {
7941
+ const out = {};
7942
+ for (const [key, value] of Object.entries(metadata)) {
7943
+ out[key.startsWith(AI_SDK_METADATA_PREFIX) ? key : `${AI_SDK_METADATA_PREFIX}${key}`] = value;
7944
+ }
7945
+ return out;
7946
+ }
7947
+ var BAGGAGE_KEYS = {
7948
+ eventId: "raindrop-event-id",
7949
+ childEventId: "raindrop-child-event-id",
7950
+ name: "raindrop-handoff-name",
7951
+ convoId: "raindrop-convo-id",
7952
+ userId: "raindrop-user-id",
7953
+ // The dispatch span and its trace, carried as fields as well as in
7954
+ // `traceparent`. Not redundant: OTel's HTTP instrumentations rewrite
7955
+ // `traceparent` from whatever span is current as the request goes out, so by
7956
+ // the time the child reads it the ids name the HTTP client span and its trace
7957
+ // instead of the dispatch. The child would then point its reverse reference at
7958
+ // a span the launcher's UI does not know as a dispatch. A field states which
7959
+ // span it means rather than meaning "whoever wrote this header last".
7960
+ dispatchSpanId: "raindrop-dispatch-span-id",
7961
+ traceId: "raindrop-trace-id"
8041
7962
  };
8042
- var MODEL_PROVIDER_NAMES = [
8043
- ["google.vertex.anthropic", "google-vertex"],
8044
- ["google.vertex_ai", "google-vertex"],
8045
- ["google.vertex", "google-vertex"],
8046
- ["gcp.vertex_ai", "google-vertex"],
8047
- ["gcp.gemini", "google"],
8048
- ["google_vertexai", "google-vertex"],
8049
- ["vertex.anthropic", "google-vertex"],
8050
- ["amazon-bedrock", "amazon-bedrock"],
8051
- ["aws.bedrock", "amazon-bedrock"],
8052
- ["bedrock", "amazon-bedrock"],
8053
- ["anthropic", "anthropic"],
8054
- ["openai", "openai"],
8055
- ["google", "google"],
8056
- ["az.ai.openai", "azure"],
8057
- ["azure", "azure"],
8058
- ["openrouter", "openrouter"]
8059
- ];
8060
- function canonicalModelProvider(provider) {
8061
- var _a;
8062
- const normalizedProvider = provider.trim().toLowerCase();
8063
- if (normalizedProvider === "gateway") return "vercel";
8064
- const match = MODEL_PROVIDER_NAMES.find(
8065
- ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
7963
+ var TRACEPARENT_HEADER = "traceparent";
7964
+ var BAGGAGE_HEADER = "baggage";
7965
+ var W3C_TRACE_ID = /^[0-9a-f]{32}$/;
7966
+ var W3C_SPAN_ID = /^[0-9a-f]{16}$/;
7967
+ function isW3CTraceId(value) {
7968
+ return value !== void 0 && W3C_TRACE_ID.test(value);
7969
+ }
7970
+ function isW3CSpanId(value) {
7971
+ return value !== void 0 && W3C_SPAN_ID.test(value);
7972
+ }
7973
+ var warnedNonConformantIds = false;
7974
+ function warnNonConformantIdsOnce(traceId, spanId) {
7975
+ if (warnedNonConformantIds) return;
7976
+ warnedNonConformantIds = true;
7977
+ console.warn(
7978
+ `[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.`
8066
7979
  );
8067
- return (_a = match == null ? void 0 : match[1]) != null ? _a : normalizedProvider;
8068
7980
  }
8069
- function tokenCount(value) {
8070
- return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
7981
+ var HANDOFF_HEADER = "x-raindrop-handoff";
7982
+ var LANGSMITH_TRACE_HEADER = "langsmith-trace";
7983
+ var LANGSMITH_METADATA_BAGGAGE_KEY = "langsmith-metadata";
7984
+ function encodeBaggage(entries) {
7985
+ return Object.entries(entries).filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join(",");
8071
7986
  }
8072
- function buildRawModelUsageAttributes(usage) {
8073
- const providerReportedCost = typeof usage.providerReportedCost === "number" && Number.isFinite(usage.providerReportedCost) && usage.providerReportedCost >= 0 ? usage.providerReportedCost : void 0;
8074
- return [
8075
- attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, tokenCount(usage.inputTokens)),
8076
- attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, tokenCount(usage.outputTokens)),
8077
- attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, tokenCount(usage.reasoningTokens)),
8078
- attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, tokenCount(usage.cacheReadInputTokens)),
8079
- attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, tokenCount(usage.cacheWriteInputTokens)),
8080
- attrDouble(MODEL_USAGE_ATTRIBUTES.providerReportedCost, providerReportedCost)
8081
- ].filter((attribute) => attribute !== void 0);
7987
+ function decodeBaggage(header) {
7988
+ if (!header) return {};
7989
+ const parsed = {};
7990
+ for (const part of header.split(",")) {
7991
+ const [rawKey, ...rest] = part.split("=");
7992
+ if (!rawKey || rest.length === 0) continue;
7993
+ const key = rawKey.trim();
7994
+ let value;
7995
+ try {
7996
+ value = decodeURIComponent(rest.join("=").trim());
7997
+ } catch (e) {
7998
+ value = rest.join("=").trim();
7999
+ }
8000
+ if (key in parsed && parsed[key] !== value) throw new ConflictingCarrierError(key);
8001
+ parsed[key] = value;
8002
+ }
8003
+ return parsed;
8082
8004
  }
8083
- var STRING_ALIASES = {
8084
- [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
8085
- };
8086
- var NUMBER_ALIASES = {
8087
- [MODEL_USAGE_ATTRIBUTES.inputTokens]: [
8088
- "gen_ai.usage.prompt_tokens",
8089
- "ai.usage.prompt_tokens",
8090
- "ai.usage.promptTokens",
8091
- "ai.usage.input_tokens",
8092
- "ai.usage.inputTokens"
8093
- ],
8094
- [MODEL_USAGE_ATTRIBUTES.outputTokens]: [
8095
- "gen_ai.usage.completion_tokens",
8096
- "ai.usage.completion_tokens",
8097
- "ai.usage.completionTokens",
8098
- "ai.usage.output_tokens",
8099
- "ai.usage.outputTokens"
8100
- ],
8101
- [MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
8102
- "gen_ai.usage.reasoning.output_tokens",
8103
- "ai.usage.reasoningTokens",
8104
- "ai.usage.thoughts_tokens"
8105
- ],
8106
- [MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
8107
- "gen_ai.usage.cache_read_tokens",
8108
- "gen_ai.usage.cache_read.input_tokens",
8109
- "ai.usage.cachedInputTokens",
8110
- "ai.usage.cached_tokens",
8111
- "ai.usage.cache_read_tokens",
8112
- "ai.usage.cache_read_input_tokens",
8113
- "ai.usage.cacheReadInputTokens"
8114
- ],
8115
- [MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
8116
- "gen_ai.usage.cache_creation_input_tokens",
8117
- "gen_ai.usage.cache_creation.input_tokens",
8118
- "ai.usage.cacheWriteInputTokens",
8119
- "ai.usage.cache_creation_input_tokens",
8120
- "ai.usage.cacheCreationInputTokens"
8121
- ]
8005
+ function carrierBaggage(carrier) {
8006
+ return {
8007
+ [BAGGAGE_KEYS.eventId]: carrier.eventId,
8008
+ [BAGGAGE_KEYS.childEventId]: carrier.childEventId,
8009
+ [BAGGAGE_KEYS.name]: carrier.name,
8010
+ [BAGGAGE_KEYS.convoId]: carrier.convoId,
8011
+ [BAGGAGE_KEYS.userId]: carrier.userId,
8012
+ [BAGGAGE_KEYS.dispatchSpanId]: carrier.spanId,
8013
+ [BAGGAGE_KEYS.traceId]: carrier.traceId
8014
+ };
8015
+ }
8016
+ function toHeaders(carrier) {
8017
+ const fields = encodeBaggage(carrierBaggage(carrier));
8018
+ const headers = {
8019
+ [BAGGAGE_HEADER]: fields,
8020
+ [HANDOFF_HEADER]: fields
8021
+ };
8022
+ if (isW3CTraceId(carrier.traceId) && isW3CSpanId(carrier.spanId)) {
8023
+ headers[TRACEPARENT_HEADER] = `00-${carrier.traceId}-${carrier.spanId}-01`;
8024
+ }
8025
+ return headers;
8026
+ }
8027
+ function langsmithStamp() {
8028
+ return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "000000");
8029
+ }
8030
+ function toLangSmithHeaders(carrier) {
8031
+ const stamp = langsmithStamp();
8032
+ const dottedOrder = `${stamp}Z${carrier.traceId}.${stamp}Z${carrier.spanId}`;
8033
+ return {
8034
+ [LANGSMITH_TRACE_HEADER]: dottedOrder,
8035
+ [BAGGAGE_HEADER]: encodeBaggage({
8036
+ [LANGSMITH_METADATA_BAGGAGE_KEY]: JSON.stringify(carrierBaggage(carrier))
8037
+ }),
8038
+ // Carried here too: a LangSmith-shaped carrier is rewritten in transit
8039
+ // exactly like the native one, and an injected `traceparent` is preferred
8040
+ // over the dotted order on read.
8041
+ [HANDOFF_HEADER]: encodeBaggage(carrierBaggage(carrier))
8042
+ };
8043
+ }
8044
+ function isHeaderLookup(headers) {
8045
+ return "get" in headers && typeof headers.get === "function";
8046
+ }
8047
+ var ConflictingCarrierError = class extends Error {
8122
8048
  };
8123
- var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
8124
- "ai.request.providerOptions",
8125
- "ai.response.providerMetadata"
8126
- ];
8127
- var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
8128
- function getAsyncLocalStorageCtor() {
8129
- return globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
8049
+ function readHeader(headers, name) {
8050
+ var _a, _b, _c;
8051
+ if (isHeaderLookup(headers)) return (_a = headers.get(name)) != null ? _a : void 0;
8052
+ const value = (_c = headers[name]) != null ? _c : (_b = Object.entries(headers).find(([key]) => key.toLowerCase() === name)) == null ? void 0 : _b[1];
8053
+ if (!Array.isArray(value)) return value;
8054
+ const distinct = new Set(value);
8055
+ if (distinct.size > 1) throw new ConflictingCarrierError(name);
8056
+ return value[0];
8130
8057
  }
8131
- function getWeakRefCtor() {
8132
- const ctor = globalThis.WeakRef;
8133
- return typeof ctor === "function" ? ctor : void 0;
8058
+ function readTraceHeader(headers, name) {
8059
+ const value = readHeader(headers, name);
8060
+ if (!(value == null ? void 0 : value.includes(","))) return value;
8061
+ const copies = value.split(",").map((copy) => copy.trim());
8062
+ if (new Set(copies).size > 1) throw new ConflictingCarrierError(name);
8063
+ return copies[0];
8064
+ }
8065
+ function fromHeaders(headers) {
8066
+ var _a, _b;
8067
+ let baggage;
8068
+ let handoff;
8069
+ let traceparent;
8070
+ let langsmithTrace;
8071
+ try {
8072
+ baggage = decodeBaggage(readHeader(headers, BAGGAGE_HEADER));
8073
+ handoff = decodeBaggage(readHeader(headers, HANDOFF_HEADER));
8074
+ traceparent = readTraceHeader(headers, TRACEPARENT_HEADER);
8075
+ langsmithTrace = readTraceHeader(headers, LANGSMITH_TRACE_HEADER);
8076
+ } catch (error) {
8077
+ if (error instanceof ConflictingCarrierError) return null;
8078
+ throw error;
8079
+ }
8080
+ const langsmithMetadata = baggage[LANGSMITH_METADATA_BAGGAGE_KEY];
8081
+ const nested = langsmithMetadata ? safeParseStringRecord(langsmithMetadata) : {};
8082
+ for (const [key, value] of Object.entries(nested)) {
8083
+ if (key in baggage && baggage[key] !== value) return null;
8084
+ }
8085
+ const fields = {
8086
+ ...baggage,
8087
+ ...nested,
8088
+ // Last, so it wins: the standard headers may have been rewritten in transit
8089
+ // by a propagator, this one is only ever written by us.
8090
+ ...handoff
8091
+ };
8092
+ let traceId;
8093
+ let spanId;
8094
+ if (traceparent) {
8095
+ const [, parsedTraceId, parsedSpanId] = traceparent.split("-");
8096
+ if (isW3CTraceId(parsedTraceId) && isW3CSpanId(parsedSpanId)) {
8097
+ traceId = parsedTraceId;
8098
+ spanId = parsedSpanId;
8099
+ }
8100
+ }
8101
+ if (!traceId && !spanId && langsmithTrace) {
8102
+ const segments = langsmithTrace.split(".");
8103
+ traceId = (_a = segments[0]) == null ? void 0 : _a.split("Z").pop();
8104
+ spanId = (_b = segments[segments.length - 1]) == null ? void 0 : _b.split("Z").pop();
8105
+ }
8106
+ traceId = fields[BAGGAGE_KEYS.traceId] || traceId;
8107
+ spanId = fields[BAGGAGE_KEYS.dispatchSpanId] || spanId;
8108
+ const eventId = fields[BAGGAGE_KEYS.eventId];
8109
+ if (!traceId || !spanId || !eventId) return null;
8110
+ if (!isW3CTraceId(traceId) || !isW3CSpanId(spanId)) {
8111
+ warnNonConformantIdsOnce(traceId, spanId);
8112
+ }
8113
+ const carrier = { traceId, spanId, eventId };
8114
+ const childEventId = fields[BAGGAGE_KEYS.childEventId];
8115
+ if (childEventId) carrier.childEventId = childEventId;
8116
+ const name = fields[BAGGAGE_KEYS.name];
8117
+ if (name) carrier.name = name;
8118
+ const convoId = fields[BAGGAGE_KEYS.convoId];
8119
+ if (convoId) carrier.convoId = convoId;
8120
+ const userId = fields[BAGGAGE_KEYS.userId];
8121
+ if (userId) carrier.userId = userId;
8122
+ return carrier;
8123
+ }
8124
+ 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.`;
8125
+ var UNRESOLVED_CARRIER_WARNED_KEY = /* @__PURE__ */ Symbol.for("raindrop.handoff.warnedUnresolvedCarrier");
8126
+ function warnedHolder() {
8127
+ return globalThis;
8128
+ }
8129
+ function warnUnresolvedCarrier(headers, carrier) {
8130
+ if (carrier) return;
8131
+ if (!headers) return;
8132
+ const holder = warnedHolder();
8133
+ if (holder[UNRESOLVED_CARRIER_WARNED_KEY]) return;
8134
+ holder[UNRESOLVED_CARRIER_WARNED_KEY] = true;
8135
+ console.warn(UNRESOLVED_CARRIER_WARNING);
8136
+ }
8137
+ function safeParseStringRecord(raw) {
8138
+ try {
8139
+ const parsed = JSON.parse(raw);
8140
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
8141
+ const result = {};
8142
+ for (const [key, value] of Object.entries(parsed)) {
8143
+ if (typeof value === "string") result[key] = value;
8144
+ }
8145
+ return result;
8146
+ } catch (e) {
8147
+ return {};
8148
+ }
8149
+ }
8150
+ function runWithTracingSuppressed(fn) {
8151
+ const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
8152
+ if (typeof hook !== "function") return fn();
8153
+ let started = false;
8154
+ try {
8155
+ return hook(() => {
8156
+ started = true;
8157
+ return fn();
8158
+ });
8159
+ } catch (err) {
8160
+ if (started) throw err;
8161
+ return fn();
8162
+ }
8163
+ }
8164
+ var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
8165
+ var MAX_RETRY_DELAY_MS = 3e4;
8166
+ function wait(ms) {
8167
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
8168
+ }
8169
+ function formatEndpoint(endpoint) {
8170
+ if (!endpoint) return void 0;
8171
+ return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
8172
+ }
8173
+ function redactUrlForLog(url) {
8174
+ try {
8175
+ const parsed = new URL(url);
8176
+ parsed.username = "";
8177
+ parsed.password = "";
8178
+ parsed.search = "";
8179
+ parsed.hash = "";
8180
+ return parsed.toString();
8181
+ } catch (e) {
8182
+ return "<unparseable-url>";
8183
+ }
8184
+ }
8185
+ var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
8186
+ var rateLimitedLogLast = /* @__PURE__ */ new Map();
8187
+ function rateLimitedLog(key, log) {
8188
+ const now = Date.now();
8189
+ const last = rateLimitedLogLast.get(key);
8190
+ if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
8191
+ return false;
8192
+ }
8193
+ rateLimitedLogLast.set(key, now);
8194
+ log();
8195
+ return true;
8196
+ }
8197
+ async function raceWithTimeout(promise, timeoutMs) {
8198
+ let timer;
8199
+ const settledInTime = await Promise.race([
8200
+ promise.then(
8201
+ () => true,
8202
+ () => true
8203
+ ),
8204
+ new Promise((resolve3) => {
8205
+ var _a;
8206
+ timer = setTimeout(() => resolve3(false), Math.max(0, timeoutMs));
8207
+ (_a = timer.unref) == null ? void 0 : _a.call(timer);
8208
+ })
8209
+ ]);
8210
+ if (timer) clearTimeout(timer);
8211
+ return settledInTime;
8212
+ }
8213
+ function parseRetryAfter(headers) {
8214
+ var _a;
8215
+ const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
8216
+ if (!value) return void 0;
8217
+ const asNumber = Number(value);
8218
+ if (value.trim() !== "" && !Number.isNaN(asNumber)) return asNumber * 1e3;
8219
+ const asDate = new Date(value).getTime();
8220
+ if (!Number.isNaN(asDate)) {
8221
+ const delta = asDate - Date.now();
8222
+ return delta > 0 ? delta : 0;
8223
+ }
8224
+ return void 0;
8134
8225
  }
8135
- var MAX_BINDING_STACK = 128;
8136
- var EMPTY_STACK = [];
8137
- var ContextStackStorage = class {
8138
- /** Emitted once when this runtime cannot support an ambient push. */
8139
- constructor(noAmbientBindWarning) {
8140
- this.noAmbientBindWarning = noAmbientBindWarning;
8141
- this._als = null;
8142
- this._enterWithUsable = false;
8143
- this._syncStack = EMPTY_STACK;
8144
- this._adoptionChecked = false;
8145
- this._warnedNoAmbientBind = false;
8226
+ function getRetryDelayMs(attemptNumber, previousError) {
8227
+ if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
8228
+ const v = previousError.retryAfterMs;
8229
+ if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
8146
8230
  }
8147
- maybeAdoptAsyncLocalStorage() {
8148
- if (this._adoptionChecked) return;
8149
- this._adoptionChecked = true;
8150
- const Ctor = getAsyncLocalStorageCtor();
8151
- if (!Ctor) return;
8152
- const als = new Ctor();
8153
- try {
8154
- als.run(EMPTY_STACK, () => {
8155
- });
8156
- } catch (e) {
8157
- return;
8231
+ if (attemptNumber <= 1) return 0;
8232
+ const base = 500;
8233
+ const factor = Math.pow(2, attemptNumber - 2);
8234
+ return Math.min(base * factor, MAX_RETRY_DELAY_MS);
8235
+ }
8236
+ async function withRetry(operation, opName, opts) {
8237
+ const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
8238
+ let lastError = void 0;
8239
+ for (let attemptNumber = 1; attemptNumber <= opts.maxAttempts; attemptNumber++) {
8240
+ if (attemptNumber > 1) {
8241
+ const delay = getRetryDelayMs(attemptNumber, lastError);
8242
+ if (opts.debug) {
8243
+ console.warn(
8244
+ `${prefix} ${opName} retry ${attemptNumber}/${opts.maxAttempts} in ${delay}ms`
8245
+ );
8246
+ }
8247
+ if (delay > 0) await wait(delay);
8248
+ } else if (opts.debug) {
8249
+ console.log(`${prefix} ${opName} attempt ${attemptNumber}/${opts.maxAttempts}`);
8158
8250
  }
8159
- this._als = als;
8160
- if (typeof als.enterWith === "function") {
8161
- try {
8162
- als.enterWith(EMPTY_STACK);
8163
- this._enterWithUsable = true;
8164
- } catch (e) {
8165
- this._enterWithUsable = false;
8251
+ try {
8252
+ return await operation();
8253
+ } catch (err) {
8254
+ lastError = err;
8255
+ if (opts.debug) {
8256
+ const msg = err instanceof Error ? err.message : String(err);
8257
+ console.warn(
8258
+ `${prefix} ${opName} attempt ${attemptNumber} failed: ${msg}${attemptNumber === opts.maxAttempts ? " (no more retries)" : ""}`
8259
+ );
8166
8260
  }
8261
+ if (lastError && typeof lastError === "object" && "retryable" in lastError && !lastError.retryable)
8262
+ break;
8263
+ if (attemptNumber === opts.maxAttempts) break;
8167
8264
  }
8168
8265
  }
8169
- getStack() {
8170
- var _a;
8171
- this.maybeAdoptAsyncLocalStorage();
8172
- if (this._als) return (_a = this._als.getStore()) != null ? _a : this._syncStack;
8173
- return this._syncStack;
8266
+ throw lastError instanceof Error ? lastError : new Error(String(lastError));
8267
+ }
8268
+ async function postJson(url, body, headers, opts) {
8269
+ var _a;
8270
+ const opName = `POST ${redactUrlForLog(url)}`;
8271
+ const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
8272
+ await withRetry(
8273
+ async () => {
8274
+ const resp = await runWithTracingSuppressed(
8275
+ () => fetch(url, {
8276
+ method: "POST",
8277
+ redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
8278
+ headers: {
8279
+ "Content-Type": "application/json",
8280
+ ...headers
8281
+ },
8282
+ body: JSON.stringify(body),
8283
+ signal: AbortSignal.timeout(timeoutMs)
8284
+ })
8285
+ );
8286
+ if (!resp.ok) {
8287
+ const text = await resp.text().catch(() => "");
8288
+ const err = new Error(
8289
+ `HTTP ${resp.status} ${resp.statusText}${text ? `: ${text}` : ""}`
8290
+ );
8291
+ const retryAfterMs = parseRetryAfter(resp.headers);
8292
+ if (typeof retryAfterMs === "number") err.retryAfterMs = retryAfterMs;
8293
+ err.retryable = resp.status === 429 || resp.status >= 500;
8294
+ throw err;
8295
+ }
8296
+ },
8297
+ opName,
8298
+ opts
8299
+ );
8300
+ }
8301
+ var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
8302
+ var TRUNCATION_MARKER = "...[truncated by raindrop]";
8303
+ var BOUNDED_CLONE_MAX_DEPTH = 12;
8304
+ var BOUNDED_CLONE_SLACK = 256;
8305
+ var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
8306
+ function setDefaultMaxTextFieldChars(value) {
8307
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
8308
+ currentDefaultMaxTextFieldChars = Math.floor(value);
8174
8309
  }
8175
- /**
8176
- * Persist an ambient stack mutation. Returns false when an ALS is adopted but
8177
- * `enterWith` is unusable (workerd): callers must NOT fall back to the
8178
- * process-shared sync stack, since concurrent isolates would cross-stamp.
8179
- */
8180
- setAmbientStack(stack) {
8181
- if (this._als) {
8182
- if (!this._enterWithUsable) return false;
8183
- this._als.enterWith(stack);
8184
- return true;
8185
- }
8186
- this._syncStack = stack;
8187
- return true;
8310
+ }
8311
+ function resolveMaxTextFieldChars(value) {
8312
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
8313
+ return Math.floor(value);
8188
8314
  }
8189
- push(binding) {
8190
- this.maybeAdoptAsyncLocalStorage();
8191
- if (this._als && !this._enterWithUsable) {
8192
- this.warnNoAmbientBindOnce();
8193
- return;
8194
- }
8195
- let stack = this.getStack();
8196
- if (stack.length >= MAX_BINDING_STACK) {
8197
- stack = stack.filter((b) => b.isLive());
8198
- if (stack.length >= MAX_BINDING_STACK) {
8199
- stack = stack.slice(stack.length - (MAX_BINDING_STACK - 1));
8315
+ return currentDefaultMaxTextFieldChars;
8316
+ }
8317
+ function truncateToLimit(text, limit) {
8318
+ if (limit > TRUNCATION_MARKER.length) {
8319
+ return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
8320
+ }
8321
+ return text.slice(0, Math.max(0, limit));
8322
+ }
8323
+ function capText(value, limit) {
8324
+ if (typeof value !== "string") return value;
8325
+ const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
8326
+ if (value.length <= max) return value;
8327
+ return truncateToLimit(value, max);
8328
+ }
8329
+ function boundedClone(obj, charBudget) {
8330
+ let budget = charBudget;
8331
+ const seen = /* @__PURE__ */ new WeakSet();
8332
+ const walk = (node, depth) => {
8333
+ if (budget <= 0) return TRUNCATION_MARKER;
8334
+ if (typeof node === "string") {
8335
+ if (node.length > budget) {
8336
+ const taken = node.slice(0, Math.max(0, budget)) + TRUNCATION_MARKER;
8337
+ budget = 0;
8338
+ return taken;
8200
8339
  }
8340
+ budget -= Math.max(node.length, 1);
8341
+ return node;
8201
8342
  }
8202
- this.setAmbientStack([...stack, binding]);
8203
- }
8204
- remove(binding) {
8205
- binding.markDead();
8206
- const stack = this.getStack();
8207
- const pruned = stack.filter((b) => b !== binding && b.isLive());
8208
- if (pruned.length !== stack.length) {
8209
- this.setAmbientStack(pruned);
8343
+ if (node === null || typeof node === "number" || typeof node === "boolean") {
8344
+ budget -= 8;
8345
+ return node;
8210
8346
  }
8211
- }
8212
- runWith(binding, callback) {
8213
- this.maybeAdoptAsyncLocalStorage();
8214
- if (this._als) {
8215
- return this._als.run([...this.getStack(), binding], callback);
8347
+ if (typeof node !== "object") {
8348
+ budget -= 8;
8349
+ return node;
8216
8350
  }
8217
- const previous = this._syncStack;
8218
- this._syncStack = [...previous, binding];
8219
- try {
8220
- return callback();
8221
- } finally {
8222
- this._syncStack = previous;
8351
+ if (seen.has(node)) return "[CIRCULAR]";
8352
+ if (depth >= BOUNDED_CLONE_MAX_DEPTH) {
8353
+ budget -= 16;
8354
+ return `<max depth>`;
8223
8355
  }
8224
- }
8225
- current() {
8226
- const stack = this.getStack();
8227
- if (stack.length === 0) return void 0;
8228
- const top = stack[stack.length - 1];
8229
- if (top.isLive()) return top;
8230
- const pruned = stack.filter((b) => b.isLive());
8231
- this.setAmbientStack(pruned);
8232
- return pruned.length > 0 ? pruned[pruned.length - 1] : void 0;
8233
- }
8234
- warnNoAmbientBindOnce() {
8235
- if (this._warnedNoAmbientBind) return;
8236
- this._warnedNoAmbientBind = true;
8237
- console.warn(this.noAmbientBindWarning);
8238
- }
8239
- };
8240
- 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.";
8241
- 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.";
8242
- var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
8243
- function routingStorage() {
8244
- const holder = globalThis;
8245
- let storage = holder[ROUTING_STORAGE_KEY];
8246
- if (!storage) {
8247
- storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
8248
- holder[ROUTING_STORAGE_KEY] = storage;
8249
- }
8250
- return storage;
8251
- }
8252
- function makeBoundContext(binding, owner) {
8253
- const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
8254
- const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
8255
- let dead = false;
8256
- return {
8257
- projectId: binding.projectId,
8258
- authHint: binding.authHint,
8259
- isLive() {
8260
- if (dead) return false;
8261
- return ref === void 0 || ref.deref() !== void 0;
8262
- },
8263
- markDead() {
8264
- dead = true;
8356
+ seen.add(node);
8357
+ if (Array.isArray(node)) {
8358
+ const out2 = [];
8359
+ for (const v of node) {
8360
+ if (budget <= 0) {
8361
+ out2.push(TRUNCATION_MARKER);
8362
+ break;
8363
+ }
8364
+ out2.push(walk(v, depth + 1));
8365
+ }
8366
+ return out2;
8265
8367
  }
8368
+ if (typeof node.toJSON === "function") {
8369
+ budget -= 16;
8370
+ return node;
8371
+ }
8372
+ const out = {};
8373
+ for (const k in node) {
8374
+ if (!Object.prototype.hasOwnProperty.call(node, k)) continue;
8375
+ if (budget <= 0) {
8376
+ out["..."] = TRUNCATION_MARKER;
8377
+ break;
8378
+ }
8379
+ const key = walk(k, depth + 1);
8380
+ out[key] = walk(node[k], depth + 1);
8381
+ }
8382
+ return out;
8266
8383
  };
8384
+ return walk(obj, 0);
8267
8385
  }
8268
- function bindRoutingContext(binding, owner) {
8269
- const bound = makeBoundContext(binding, owner);
8386
+ function stringifyBounded(value, limit) {
8387
+ const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
8388
+ if (typeof value === "string") {
8389
+ return capText(value, max);
8390
+ }
8391
+ const pruned = boundedClone(value, max + TRUNCATION_MARKER.length + BOUNDED_CLONE_SLACK);
8392
+ let json;
8270
8393
  try {
8271
- routingStorage().push(bound);
8394
+ json = JSON.stringify(pruned);
8272
8395
  } catch (e) {
8396
+ json = void 0;
8273
8397
  }
8274
- return bound;
8398
+ if (json === void 0) {
8399
+ return capText(String(value), max);
8400
+ }
8401
+ return json.length <= max ? json : truncateToLimit(json, max);
8275
8402
  }
8276
- function unbindRoutingContext(bound) {
8277
- if (!bound || !isBoundContext(bound)) return;
8403
+ function attrString(key, value) {
8404
+ if (value === void 0) return void 0;
8405
+ return { key, value: { stringValue: value } };
8406
+ }
8407
+ function attrInt(key, value) {
8408
+ if (value === void 0) return void 0;
8409
+ if (!Number.isFinite(value)) return void 0;
8410
+ return { key, value: { intValue: String(Math.trunc(value)) } };
8411
+ }
8412
+ function attrDouble(key, value) {
8413
+ if (value === void 0) return void 0;
8414
+ if (!Number.isFinite(value)) return void 0;
8415
+ return { key, value: { doubleValue: value } };
8416
+ }
8417
+ var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
8418
+ var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
8419
+ var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
8420
+ function readEnvVar(name) {
8421
+ var _a;
8278
8422
  try {
8279
- routingStorage().remove(bound);
8423
+ const env = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env;
8424
+ if (env && typeof env[name] === "string" && env[name].length > 0) {
8425
+ return env[name];
8426
+ }
8280
8427
  } catch (e) {
8281
8428
  }
8429
+ return void 0;
8282
8430
  }
8283
- function withRoutingContext(binding, callback) {
8284
- const bound = makeBoundContext(binding);
8285
- return routingStorage().runWith(bound, callback);
8431
+ function readWorkshopEnv() {
8432
+ const raw = readEnvVar(WORKSHOP_ENV_VAR);
8433
+ if (raw === void 0) return void 0;
8434
+ const trimmed = raw.trim();
8435
+ if (trimmed.length === 0) return void 0;
8436
+ if (/^https?:\/\//i.test(trimmed)) return { url: trimmed };
8437
+ if (/^(1|true|yes|on)$/i.test(trimmed)) return "enable";
8438
+ if (/^(0|false|no|off)$/i.test(trimmed)) return "disable";
8439
+ return void 0;
8286
8440
  }
8287
- function currentRoutingBinding() {
8288
- const bound = routingStorage().current();
8289
- if (!bound) return void 0;
8290
- return { projectId: bound.projectId, authHint: bound.authHint };
8441
+ function isLocalDevHost(hostname) {
8442
+ if (!hostname) return false;
8443
+ if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1") {
8444
+ return true;
8445
+ }
8446
+ if (hostname.endsWith(".localhost")) return true;
8447
+ return false;
8291
8448
  }
8292
- function isBoundContext(value) {
8293
- return typeof value.isLive === "function";
8449
+ function readRuntimeHostname() {
8450
+ try {
8451
+ const loc = globalThis == null ? void 0 : globalThis.location;
8452
+ if (loc && typeof loc.hostname === "string" && loc.hostname.length > 0) {
8453
+ return loc.hostname;
8454
+ }
8455
+ } catch (e) {
8456
+ }
8457
+ return void 0;
8294
8458
  }
8295
- var ATTRIBUTE_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.spanAttributeStorage");
8296
- function attributeStorage() {
8297
- const holder = globalThis;
8298
- let storage = holder[ATTRIBUTE_STORAGE_KEY];
8299
- if (!storage) {
8300
- storage = new ContextStackStorage(NO_AMBIENT_ATTRIBUTE_BIND_WARNING);
8301
- holder[ATTRIBUTE_STORAGE_KEY] = storage;
8459
+ function shouldAutoEnableLocalWorkshop() {
8460
+ if (isLocalDevHost(readRuntimeHostname())) return true;
8461
+ if (readEnvVar("NODE_ENV") === "development") return true;
8462
+ return false;
8463
+ }
8464
+ function resolveLocalDebuggerBaseUrl(baseUrl) {
8465
+ var _a, _b, _c;
8466
+ if (baseUrl === null) return null;
8467
+ if (typeof baseUrl === "string" && baseUrl.length > 0) {
8468
+ return (_a = formatEndpoint(baseUrl)) != null ? _a : null;
8302
8469
  }
8303
- return storage;
8470
+ const explicitUrlEnv = readEnvVar(LOCAL_DEBUGGER_ENV_VAR);
8471
+ if (explicitUrlEnv) return (_b = formatEndpoint(explicitUrlEnv)) != null ? _b : null;
8472
+ const workshopEnv = readWorkshopEnv();
8473
+ if (workshopEnv === "disable") return null;
8474
+ if (workshopEnv === "enable") return DEFAULT_LOCAL_WORKSHOP_URL;
8475
+ if (workshopEnv && "url" in workshopEnv) return (_c = formatEndpoint(workshopEnv.url)) != null ? _c : null;
8476
+ if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
8477
+ return null;
8304
8478
  }
8305
- function makeAttributeFrame(attributes, owner) {
8306
- const snapshot = { ...attributes };
8307
- const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
8308
- const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
8309
- const owned = /* @__PURE__ */ new WeakSet();
8310
- let dead = false;
8311
- let sawError = false;
8312
- return {
8313
- attributes: snapshot,
8314
- sawErrorSpan: () => sawError,
8315
- ownsSpan: (span) => owned.has(span),
8316
- claimSpan(span) {
8317
- owned.add(span);
8318
- },
8319
- markErrorSpanSeen() {
8320
- sawError = true;
8321
- },
8322
- // A frame whose owner has been collected is dead even though nothing
8323
- // unbound it: a run that never settles must not keep handing its reverse
8324
- // reference to whatever runs next on a reused context.
8325
- isLive() {
8326
- if (dead) return false;
8327
- return ref === void 0 || ref.deref() !== void 0;
8479
+ function localDebuggerEnabled(baseUrl) {
8480
+ return resolveLocalDebuggerBaseUrl(baseUrl) !== null;
8481
+ }
8482
+ var LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS = 2e3;
8483
+ function mirrorTraceExportToLocalDebugger(body, options = {}) {
8484
+ var _a;
8485
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
8486
+ if (!baseUrl) return Promise.resolve();
8487
+ return postJson(`${baseUrl}traces`, body, {}, {
8488
+ maxAttempts: 1,
8489
+ debug: (_a = options.debug) != null ? _a : false,
8490
+ sdkName: options.sdkName,
8491
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
8492
+ }).catch(() => {
8493
+ });
8494
+ }
8495
+ function mirrorPartialEventToLocalDebugger(event, options = {}) {
8496
+ var _a;
8497
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
8498
+ if (!baseUrl) return Promise.resolve();
8499
+ const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
8500
+ return postJson(`${baseUrl}events/track_partial`, event, headers, {
8501
+ maxAttempts: 1,
8502
+ debug: (_a = options.debug) != null ? _a : false,
8503
+ sdkName: options.sdkName,
8504
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
8505
+ }).catch(() => {
8506
+ });
8507
+ }
8508
+ function sendLocalDebuggerLiveEvent(event, options = {}) {
8509
+ var _a, _b;
8510
+ const destination = currentReplayTraceDestination();
8511
+ if (destination && destination.kind !== "workshop") return;
8512
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
8513
+ if (!baseUrl) return;
8514
+ void postJson(
8515
+ `${baseUrl}live`,
8516
+ {
8517
+ ...event,
8518
+ type: event.type,
8519
+ timestamp: (_a = event.timestamp) != null ? _a : Date.now()
8328
8520
  },
8329
- markDead() {
8330
- dead = true;
8521
+ {},
8522
+ {
8523
+ maxAttempts: 1,
8524
+ debug: (_b = options.debug) != null ? _b : false,
8525
+ sdkName: options.sdkName
8331
8526
  }
8332
- };
8527
+ ).catch(() => {
8528
+ });
8529
+ }
8530
+ var PROJECT_ID_HEADER = "X-Raindrop-Project-Id";
8531
+ var PROJECT_ID_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
8532
+ function isValidProjectIdSlug(value) {
8533
+ return PROJECT_ID_SLUG_PATTERN.test(value);
8534
+ }
8535
+ function normalizeProjectId(raw, opts) {
8536
+ if (typeof raw !== "string") return void 0;
8537
+ const trimmed = raw.trim();
8538
+ if (!trimmed) return void 0;
8539
+ if (!isValidProjectIdSlug(trimmed) && opts.debug) {
8540
+ console.warn(
8541
+ `${opts.prefix} projectId "${trimmed}" does not match slug ${PROJECT_ID_SLUG_PATTERN.source}; sending anyway \u2014 backend may reject with HTTP 400`
8542
+ );
8543
+ }
8544
+ return trimmed;
8545
+ }
8546
+ function projectIdHeaders(projectId) {
8547
+ return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
8548
+ }
8549
+ var SHUTDOWN_DEADLINE_MS = 1e4;
8550
+ var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
8551
+ var MODEL_USAGE_ATTRIBUTES = {
8552
+ usageSource: "raindrop.usage.source",
8553
+ providerName: "gen_ai.provider.name",
8554
+ requestModel: "gen_ai.request.model",
8555
+ responseModel: "gen_ai.response.model",
8556
+ inputTokens: "gen_ai.usage.input_tokens",
8557
+ outputTokens: "gen_ai.usage.output_tokens",
8558
+ reasoningTokens: "gen_ai.usage.reasoning_tokens",
8559
+ cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
8560
+ cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
8561
+ providerReportedCost: "gen_ai.usage.provider_reported_cost",
8562
+ nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
8563
+ nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
8564
+ totalOutputTokens: "raindrop.usage.output_tokens.total",
8565
+ reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
8566
+ cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
8567
+ cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
8568
+ };
8569
+ var MODEL_PROVIDER_NAMES = [
8570
+ ["google.vertex.anthropic", "google-vertex"],
8571
+ ["google.vertex_ai", "google-vertex"],
8572
+ ["google.vertex", "google-vertex"],
8573
+ ["gcp.vertex_ai", "google-vertex"],
8574
+ ["gcp.gemini", "google"],
8575
+ ["google_vertexai", "google-vertex"],
8576
+ ["vertex.anthropic", "google-vertex"],
8577
+ ["amazon-bedrock", "amazon-bedrock"],
8578
+ ["aws.bedrock", "amazon-bedrock"],
8579
+ ["bedrock", "amazon-bedrock"],
8580
+ ["anthropic", "anthropic"],
8581
+ ["openai", "openai"],
8582
+ ["google", "google"],
8583
+ ["az.ai.openai", "azure"],
8584
+ ["azure", "azure"],
8585
+ ["openrouter", "openrouter"]
8586
+ ];
8587
+ function canonicalModelProvider(provider) {
8588
+ var _a;
8589
+ const normalizedProvider = provider.trim().toLowerCase();
8590
+ if (normalizedProvider === "gateway") return "vercel";
8591
+ const match = MODEL_PROVIDER_NAMES.find(
8592
+ ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
8593
+ );
8594
+ return (_a = match == null ? void 0 : match[1]) != null ? _a : normalizedProvider;
8333
8595
  }
8334
- function bindSpanAttributes(attributes, owner) {
8335
- if (Object.keys(attributes).length === 0) return void 0;
8336
- const frame = makeAttributeFrame(attributes, owner);
8337
- try {
8338
- attributeStorage().push(frame);
8339
- } catch (e) {
8340
- return void 0;
8341
- }
8342
- return frame;
8596
+ function tokenCount(value) {
8597
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
8343
8598
  }
8344
- function unbindSpanAttributes(frame) {
8345
- if (!frame || !isAttributeFrame(frame)) return;
8346
- try {
8347
- attributeStorage().remove(frame);
8348
- } catch (e) {
8349
- }
8599
+ function buildRawModelUsageAttributes(usage) {
8600
+ const providerReportedCost = typeof usage.providerReportedCost === "number" && Number.isFinite(usage.providerReportedCost) && usage.providerReportedCost >= 0 ? usage.providerReportedCost : void 0;
8601
+ return [
8602
+ attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, tokenCount(usage.inputTokens)),
8603
+ attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, tokenCount(usage.outputTokens)),
8604
+ attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, tokenCount(usage.reasoningTokens)),
8605
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, tokenCount(usage.cacheReadInputTokens)),
8606
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, tokenCount(usage.cacheWriteInputTokens)),
8607
+ attrDouble(MODEL_USAGE_ATTRIBUTES.providerReportedCost, providerReportedCost)
8608
+ ].filter((attribute) => attribute !== void 0);
8350
8609
  }
8351
- function applySpanAttributeFrames(span, setAttribute) {
8352
- try {
8353
- for (const frame of attributeStorage().getStack()) {
8354
- if (!frame.isLive()) continue;
8355
- frame.claimSpan(span);
8356
- for (const [key, value] of Object.entries(frame.attributes)) {
8357
- setAttribute(key, value);
8358
- }
8610
+ var STRING_ALIASES = {
8611
+ [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
8612
+ };
8613
+ var NUMBER_ALIASES = {
8614
+ [MODEL_USAGE_ATTRIBUTES.inputTokens]: [
8615
+ "gen_ai.usage.prompt_tokens",
8616
+ "ai.usage.prompt_tokens",
8617
+ "ai.usage.promptTokens",
8618
+ "ai.usage.input_tokens",
8619
+ "ai.usage.inputTokens"
8620
+ ],
8621
+ [MODEL_USAGE_ATTRIBUTES.outputTokens]: [
8622
+ "gen_ai.usage.completion_tokens",
8623
+ "ai.usage.completion_tokens",
8624
+ "ai.usage.completionTokens",
8625
+ "ai.usage.output_tokens",
8626
+ "ai.usage.outputTokens"
8627
+ ],
8628
+ [MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
8629
+ "gen_ai.usage.reasoning.output_tokens",
8630
+ "ai.usage.reasoningTokens",
8631
+ "ai.usage.thoughts_tokens"
8632
+ ],
8633
+ [MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
8634
+ "gen_ai.usage.cache_read_tokens",
8635
+ "gen_ai.usage.cache_read.input_tokens",
8636
+ "ai.usage.cachedInputTokens",
8637
+ "ai.usage.cached_tokens",
8638
+ "ai.usage.cache_read_tokens",
8639
+ "ai.usage.cache_read_input_tokens",
8640
+ "ai.usage.cacheReadInputTokens"
8641
+ ],
8642
+ [MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
8643
+ "gen_ai.usage.cache_creation_input_tokens",
8644
+ "gen_ai.usage.cache_creation.input_tokens",
8645
+ "ai.usage.cacheWriteInputTokens",
8646
+ "ai.usage.cache_creation_input_tokens",
8647
+ "ai.usage.cacheCreationInputTokens"
8648
+ ]
8649
+ };
8650
+ var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
8651
+ "ai.request.providerOptions",
8652
+ "ai.response.providerMetadata"
8653
+ ];
8654
+ var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
8655
+
8656
+ // ../core/dist/index.node.js
8657
+ var import_async_hooks = require("async_hooks");
8658
+ var import_child_process = require("child_process");
8659
+ var import_path = require("path");
8660
+ var resolveLocal = (sourceDirectory, detectBranch, completed) => {
8661
+ const cwd = sourceDirectory ? (0, import_path.resolve)(sourceDirectory) : process.cwd();
8662
+ let cancelled = false;
8663
+ let active;
8664
+ let deadlineTimer;
8665
+ const deadline = Date.now() + 1e3;
8666
+ const run = (args, done) => {
8667
+ if (cancelled) return;
8668
+ const remaining = deadline - Date.now();
8669
+ if (remaining <= 0) {
8670
+ done(void 0);
8671
+ return;
8359
8672
  }
8360
- } catch (e) {
8361
- }
8362
- }
8363
- function claimSpanForFrames(span) {
8364
- try {
8365
- for (const frame of attributeStorage().getStack()) {
8366
- if (frame.isLive()) frame.claimSpan(span);
8673
+ try {
8674
+ active = (0, import_child_process.execFile)(
8675
+ "git",
8676
+ args,
8677
+ {
8678
+ cwd,
8679
+ encoding: "utf8",
8680
+ maxBuffer: 4096,
8681
+ windowsHide: true,
8682
+ env: {
8683
+ ...process.env,
8684
+ // The selected application cwd, not the invoking shell's Git plumbing, owns identity.
8685
+ GIT_DIR: void 0,
8686
+ GIT_WORK_TREE: void 0,
8687
+ GIT_COMMON_DIR: void 0,
8688
+ GIT_NAMESPACE: void 0,
8689
+ GIT_INDEX_FILE: void 0,
8690
+ GIT_OBJECT_DIRECTORY: void 0,
8691
+ GIT_ALTERNATE_OBJECT_DIRECTORIES: void 0,
8692
+ GIT_CONFIG_PARAMETERS: void 0,
8693
+ GIT_CONFIG_COUNT: "0",
8694
+ GIT_TERMINAL_PROMPT: "0",
8695
+ GIT_OPTIONAL_LOCKS: "0"
8696
+ }
8697
+ },
8698
+ (error, stdout) => {
8699
+ clearTimeout(deadlineTimer);
8700
+ deadlineTimer = void 0;
8701
+ active = void 0;
8702
+ if (!cancelled) {
8703
+ try {
8704
+ done(error ? void 0 : stdout.trim());
8705
+ } catch (e) {
8706
+ }
8707
+ }
8708
+ }
8709
+ );
8710
+ active.unref();
8711
+ if (active.stdout && "unref" in active.stdout && typeof active.stdout.unref === "function")
8712
+ active.stdout.unref();
8713
+ if (active.stderr && "unref" in active.stderr && typeof active.stderr.unref === "function")
8714
+ active.stderr.unref();
8715
+ deadlineTimer = setTimeout(() => {
8716
+ try {
8717
+ active == null ? void 0 : active.kill("SIGKILL");
8718
+ } catch (e) {
8719
+ }
8720
+ }, remaining);
8721
+ deadlineTimer.unref();
8722
+ } catch (e) {
8723
+ try {
8724
+ done(void 0);
8725
+ } catch (e2) {
8726
+ }
8367
8727
  }
8368
- } catch (e) {
8369
- }
8370
- }
8371
- function currentSpanAttributes() {
8372
- const merged = {};
8373
- try {
8374
- for (const frame of attributeStorage().getStack()) {
8375
- if (!frame.isLive()) continue;
8376
- Object.assign(merged, frame.attributes);
8728
+ };
8729
+ const scheduled = setTimeout(() => {
8730
+ try {
8731
+ run(["rev-parse", "HEAD"], (rawSha) => {
8732
+ const commitSha = validAppCommitSha(rawSha);
8733
+ if (!commitSha) {
8734
+ completed({});
8735
+ return;
8736
+ }
8737
+ run(["-c", "core.fsmonitor=false", "status", "--porcelain"], (status) => {
8738
+ const finish = (branch) => {
8739
+ run(["rev-parse", "HEAD"], (finalSha) => {
8740
+ const coherent = validAppCommitSha(finalSha) === commitSha;
8741
+ completed({
8742
+ commitSha,
8743
+ ...coherent && status !== void 0 ? { commitDirty: status !== "" } : {},
8744
+ ...coherent && branch && branch !== "HEAD" ? { branch } : {}
8745
+ });
8746
+ });
8747
+ };
8748
+ if (detectBranch) run(["symbolic-ref", "--quiet", "--short", "HEAD"], finish);
8749
+ else finish();
8750
+ });
8751
+ });
8752
+ } catch (e) {
8753
+ completed({});
8377
8754
  }
8378
- } catch (e) {
8379
- }
8380
- return merged;
8381
- }
8382
- function markErrorSpanSeen(span) {
8383
- try {
8384
- for (const frame of attributeStorage().getStack()) {
8385
- if (frame.ownsSpan(span)) frame.markErrorSpanSeen();
8755
+ }, 0);
8756
+ scheduled.unref();
8757
+ return () => {
8758
+ cancelled = true;
8759
+ clearTimeout(scheduled);
8760
+ clearTimeout(deadlineTimer);
8761
+ try {
8762
+ active == null ? void 0 : active.kill("SIGKILL");
8763
+ } catch (e) {
8386
8764
  }
8387
- } catch (e) {
8388
- }
8389
- }
8390
- function isAttributeFrame(value) {
8391
- return typeof value.isLive === "function";
8765
+ };
8766
+ };
8767
+ function createAppGitContext(options, runtime = {}) {
8768
+ return new AppGitContext(options, { ...runtime, resolveLocal });
8392
8769
  }
8393
-
8394
- // ../core/dist/index.node.js
8395
- var import_async_hooks = require("async_hooks");
8396
8770
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
8397
8771
  var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
8398
8772
  "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
@@ -8420,7 +8794,7 @@ function installTracingSuppressionHook() {
8420
8794
  }
8421
8795
  installTracingSuppressionHook();
8422
8796
 
8423
- // ../schemas/dist/chunk-VQNKQ4ZC.js
8797
+ // ../schemas/dist/chunk-HA2X6666.js
8424
8798
  var __defProp2 = Object.defineProperty;
8425
8799
  var __export2 = (target, all) => {
8426
8800
  for (var name in all)
@@ -12460,6 +12834,14 @@ var coerce = {
12460
12834
  date: ((arg) => ZodDate.create({ ...arg, coerce: true }))
12461
12835
  };
12462
12836
  var NEVER = INVALID;
12837
+ var AppGitOptionsSchema = external_exports.object({
12838
+ commitSha: external_exports.string().nullable().optional(),
12839
+ commitDirty: external_exports.boolean().nullable().optional(),
12840
+ branch: external_exports.string().nullable().optional(),
12841
+ detectBranch: external_exports.boolean().optional(),
12842
+ sourceDirectory: external_exports.string().optional(),
12843
+ autoDetect: external_exports.boolean().optional()
12844
+ });
12463
12845
  var BaseAttachmentSchema = external_exports.object({
12464
12846
  attachment_id: external_exports.string().optional(),
12465
12847
  name: external_exports.string().default("").optional(),
@@ -12529,7 +12911,7 @@ var SignalEventSchema = external_exports.object({
12529
12911
  // package.json
12530
12912
  var package_default = {
12531
12913
  name: "raindrop-ai",
12532
- version: "0.7.0",
12914
+ version: "0.7.1",
12533
12915
  main: "dist/index.js",
12534
12916
  module: "dist/index.mjs",
12535
12917
  types: "dist/index.d.ts",
@@ -12957,11 +13339,38 @@ function stampContextAttributes(span) {
12957
13339
  }
12958
13340
  }
12959
13341
  var RaindropContextSpanProcessor = class {
13342
+ constructor(appGitSnapshot) {
13343
+ this.appGitSnapshot = appGitSnapshot;
13344
+ this.appGitAdded = /* @__PURE__ */ new WeakMap();
13345
+ }
12960
13346
  onStart(span) {
13347
+ var _a, _b, _c, _d;
12961
13348
  try {
12962
13349
  stampContextAttributes(span);
12963
13350
  rememberReplayTraceDestination(span);
12964
13351
  const bound = currentRoutingBinding();
13352
+ const keys = ["raindrop.app.commit_sha", "raindrop.app.commit_dirty", "raindrop.app.branch"];
13353
+ const attrs = (_a = span.attributes) != null ? _a : {};
13354
+ const properties = {};
13355
+ for (const key of keys) {
13356
+ const existing = [key, `ai.telemetry.metadata.${key}`, `traceloop.association.properties.${key}`].find((name) => Object.prototype.hasOwnProperty.call(attrs, name));
13357
+ if (existing) properties[key] = attrs[existing];
13358
+ }
13359
+ const source = { ...(_d = (_c = (_b = this.appGitSnapshot) == null ? void 0 : _b.call(this, properties)) != null ? _c : bound == null ? void 0 : bound.appGit) != null ? _d : {} };
13360
+ if (Object.prototype.hasOwnProperty.call(properties, "raindrop.app.commit_sha") && properties["raindrop.app.commit_sha"] !== source["raindrop.app.commit_sha"]) {
13361
+ delete source["raindrop.app.commit_dirty"];
13362
+ delete source["raindrop.app.branch"];
13363
+ }
13364
+ const added = /* @__PURE__ */ new Map();
13365
+ for (const key of keys) {
13366
+ if ([key, `ai.telemetry.metadata.${key}`, `traceloop.association.properties.${key}`].some((name) => Object.prototype.hasOwnProperty.call(attrs, name))) continue;
13367
+ const value = source == null ? void 0 : source[key];
13368
+ if (typeof value === "string" || typeof value === "boolean") {
13369
+ span.setAttribute(key, value);
13370
+ added.set(key, value);
13371
+ }
13372
+ }
13373
+ this.appGitAdded.set(span, added);
12965
13374
  if (!bound) return;
12966
13375
  stampSpan(span, bound.projectId, bound.authHint);
12967
13376
  } catch (e) {
@@ -12970,6 +13379,16 @@ var RaindropContextSpanProcessor = class {
12970
13379
  onEnd(span) {
12971
13380
  var _a;
12972
13381
  try {
13382
+ if (span == null ? void 0 : span.attributes) {
13383
+ const attrs = span.attributes;
13384
+ const added = this.appGitAdded.get(span);
13385
+ const shaKey = "raindrop.app.commit_sha";
13386
+ const hasAlias = (key) => [`ai.telemetry.metadata.${key}`, `traceloop.association.properties.${key}`].some((name) => Object.prototype.hasOwnProperty.call(attrs, name));
13387
+ const shaChanged = (added == null ? void 0 : added.has(shaKey)) && (attrs[shaKey] !== added.get(shaKey) || hasAlias(shaKey));
13388
+ for (const [key, value] of added != null ? added : []) {
13389
+ if (attrs[key] === value && (hasAlias(key) || shaChanged && key !== shaKey)) delete attrs[key];
13390
+ }
13391
+ }
12973
13392
  if (((_a = span == null ? void 0 : span.status) == null ? void 0 : _a.code) !== SPAN_STATUS_ERROR) return;
12974
13393
  markErrorSpanSeen(span);
12975
13394
  } catch (e) {
@@ -13097,7 +13516,7 @@ var import_weakref = require("weakref");
13097
13516
 
13098
13517
  // src/tracing/direct/http.ts
13099
13518
  function wait2(ms) {
13100
- return new Promise((resolve2) => setTimeout(resolve2, ms));
13519
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
13101
13520
  }
13102
13521
  function formatEndpoint2(endpoint) {
13103
13522
  if (!endpoint) return void 0;
@@ -13342,6 +13761,7 @@ var DirectTraceShipper = class {
13342
13761
  */
13343
13762
  this.hasShutdown = false;
13344
13763
  var _a, _b, _c, _d, _e, _f;
13764
+ this.appGitSnapshot = opts.appGitSnapshot;
13345
13765
  this.writeKey = opts.writeKey;
13346
13766
  this.baseUrl = (_a = formatEndpoint2(opts.endpoint)) != null ? _a : "https://api.raindrop.ai/v1/";
13347
13767
  this.enabled = (_b = opts.enabled) != null ? _b : true;
@@ -13356,6 +13776,14 @@ var DirectTraceShipper = class {
13356
13776
  this.localDebuggerUrl = resolved;
13357
13777
  }
13358
13778
  }
13779
+ applicationGitSnapshot(properties) {
13780
+ var _a, _b, _c, _d;
13781
+ try {
13782
+ return (_d = (_c = (_a = this.appGitSnapshot) == null ? void 0 : _a.call(this, properties)) != null ? _c : (_b = currentRoutingBinding()) == null ? void 0 : _b.appGit) != null ? _d : {};
13783
+ } catch (e) {
13784
+ return {};
13785
+ }
13786
+ }
13359
13787
  /**
13360
13788
  * Build and enqueue an OTLP span for a trackTool call.
13361
13789
  *
@@ -13430,7 +13858,10 @@ var DirectTraceShipper = class {
13430
13858
  name: params.name,
13431
13859
  startTimeUnixNano,
13432
13860
  endTimeUnixNano,
13433
- attributes,
13861
+ attributes: withAppGitAttributes(
13862
+ attributes,
13863
+ params.appGitSnapshot !== void 0 ? params.appGitSnapshot : this.applicationGitSnapshot(params.properties)
13864
+ ),
13434
13865
  status
13435
13866
  });
13436
13867
  const replayDestination = currentReplayTraceDestination();
@@ -15051,7 +15482,7 @@ var LiveInteraction = class {
15051
15482
  span.end();
15052
15483
  }
15053
15484
  async finish(resultEvent) {
15054
- var _a, _b, _c, _d, _e, _f, _g;
15485
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
15055
15486
  unbindRoutingContext(this.boundContext);
15056
15487
  this.boundContext = void 0;
15057
15488
  unbindSpanAttributes(this.toolEventsFrame);
@@ -15061,10 +15492,19 @@ var LiveInteraction = class {
15061
15492
  }
15062
15493
  const eventId = resultEvent.eventId || this.context.eventId;
15063
15494
  if (!this.usageEmitted) {
15064
- (_c = this.analytics) == null ? void 0 : _c._recordModelUsage(
15495
+ const appGitSnapshot = (_b = (_a = this.analytics) == null ? void 0 : _a.getAppGitSnapshot(resultEvent.properties, eventId)) != null ? _b : {};
15496
+ const usageProperties = {};
15497
+ for (const key of [APP_COMMIT_SHA, APP_COMMIT_DIRTY, APP_BRANCH]) {
15498
+ if (resultEvent.properties && Object.prototype.hasOwnProperty.call(resultEvent.properties, key)) {
15499
+ usageProperties[key] = resultEvent.properties[key];
15500
+ }
15501
+ }
15502
+ (_e = this.analytics) == null ? void 0 : _e._recordModelUsage(
15065
15503
  eventId,
15066
- (_a = resultEvent.model) != null ? _a : this.context.model,
15067
- (_b = resultEvent.usage) != null ? _b : this.context.usage
15504
+ (_c = resultEvent.model) != null ? _c : this.context.model,
15505
+ (_d = resultEvent.usage) != null ? _d : this.context.usage,
15506
+ withAppGitProperties(usageProperties, appGitSnapshot),
15507
+ appGitSnapshot
15068
15508
  );
15069
15509
  this.usageEmitted = true;
15070
15510
  }
@@ -15075,20 +15515,20 @@ var LiveInteraction = class {
15075
15515
  } catch (e) {
15076
15516
  }
15077
15517
  })();
15078
- if ((_e = (_d = this.analytics) == null ? void 0 : _d._shouldBlockFinishFlush()) != null ? _e : true) {
15518
+ if ((_g = (_f = this.analytics) == null ? void 0 : _f._shouldBlockFinishFlush()) != null ? _g : true) {
15079
15519
  await spanFlush;
15080
15520
  } else {
15081
15521
  void spanFlush.catch(() => {
15082
15522
  });
15083
15523
  }
15084
15524
  try {
15085
- await ((_f = this.analytics) == null ? void 0 : _f._drainInFlightPartials(eventId));
15525
+ await ((_h = this.analytics) == null ? void 0 : _h._drainInFlightPartials(eventId));
15086
15526
  } catch (e) {
15087
15527
  }
15088
15528
  if (this.traceId) {
15089
15529
  this.setProperty("trace_id", this.traceId);
15090
15530
  }
15091
- await ((_g = this.analytics) == null ? void 0 : _g._trackAiPartial({
15531
+ await ((_i = this.analytics) == null ? void 0 : _i._trackAiPartial({
15092
15532
  ...resultEvent,
15093
15533
  eventId,
15094
15534
  isPending: false
@@ -15895,6 +16335,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
15895
16335
  }
15896
16336
  }
15897
16337
  const directShipper = bypassOtelForTools ? new DirectTraceShipper({
16338
+ appGitSnapshot: (properties) => analytics.getAppGitSnapshot(properties),
15898
16339
  writeKey,
15899
16340
  endpoint: directEndpoint != null ? directEndpoint : apiUrl,
15900
16341
  debug: isDebug,
@@ -15954,7 +16395,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
15954
16395
  tracingEnabled: tracingEnabled !== false,
15955
16396
  disableBatch: disableBatching,
15956
16397
  processor: new CompositeSpanProcessor(
15957
- [new RaindropContextSpanProcessor(), localDebuggerProcessor],
16398
+ [new RaindropContextSpanProcessor((properties) => analytics.getAppGitSnapshot(properties)), localDebuggerProcessor],
15958
16399
  []
15959
16400
  ),
15960
16401
  instrumentations: createConfiguredInstrumentations(otherOptions)
@@ -15978,7 +16419,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
15978
16419
  tracingEnabled: tracingEnabled !== false,
15979
16420
  disableBatch: disableBatching,
15980
16421
  processor: new CompositeSpanProcessor(
15981
- [new RaindropContextSpanProcessor(), replayOnlyProcessor],
16422
+ [new RaindropContextSpanProcessor((properties) => analytics.getAppGitSnapshot(properties)), replayOnlyProcessor],
15982
16423
  []
15983
16424
  ),
15984
16425
  instrumentations: createConfiguredInstrumentations(otherOptions)
@@ -16019,7 +16460,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
16019
16460
  );
16020
16461
  }
16021
16462
  if (guardedExporter) {
16022
- const contextProcessor = new RaindropContextSpanProcessor();
16463
+ const contextProcessor = new RaindropContextSpanProcessor((properties) => analytics.getAppGitSnapshot(properties));
16023
16464
  const callerProcessor = otherOptions.processor;
16024
16465
  const processor = new CompositeSpanProcessor(
16025
16466
  callerProcessor ? [contextProcessor, callerProcessor] : [contextProcessor],
@@ -16082,9 +16523,9 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
16082
16523
  traceId,
16083
16524
  analytics,
16084
16525
  directShipper,
16085
- { projectId, authHint }
16526
+ { projectId, authHint, appGit: analytics.getAppGitSnapshot(traceContext.properties, traceContext.eventId) }
16086
16527
  );
16087
- const bound = bindRoutingContext({ projectId, authHint }, interaction);
16528
+ const bound = bindRoutingContext({ projectId, authHint, appGit: analytics.getAppGitSnapshot(traceContext.properties, traceContext.eventId) }, interaction);
16088
16529
  interaction.setBoundContext(bound);
16089
16530
  try {
16090
16531
  analytics._trackAiPartial({
@@ -16161,7 +16602,8 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
16161
16602
  directShipper.sendSpan({
16162
16603
  name: "ai.model.usage",
16163
16604
  spanKind: "llm",
16164
- properties: { event_id: params.eventId },
16605
+ properties: { ...params.appGitProperties, event_id: params.eventId },
16606
+ appGitSnapshot: params.appGitSnapshot,
16165
16607
  startTimeMs: timeMs,
16166
16608
  endTimeMs: timeMs,
16167
16609
  durationMs: 0,
@@ -16169,9 +16611,23 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
16169
16611
  });
16170
16612
  return;
16171
16613
  }
16172
- const span = import_api6.trace.getTracer("traceloop.tracer").startSpan("ai.model.usage");
16173
- span.setAttribute("traceloop.span.kind", "llm");
16174
- span.setAttribute("traceloop.association.properties.event_id", params.eventId);
16614
+ const initialAttributes = {
16615
+ "traceloop.span.kind": "llm"
16616
+ };
16617
+ for (const [key, value] of Object.entries({
16618
+ ...params.appGitProperties,
16619
+ event_id: params.eventId
16620
+ })) {
16621
+ if (value !== void 0) {
16622
+ initialAttributes[`traceloop.association.properties.${key}`] = stringifyBounded(value);
16623
+ }
16624
+ }
16625
+ for (const [key, value] of Object.entries(params.appGitSnapshot)) {
16626
+ if (typeof value === "string" || typeof value === "boolean") {
16627
+ initialAttributes[key] = value;
16628
+ }
16629
+ }
16630
+ const span = import_api6.trace.getTracer("traceloop.tracer").startSpan("ai.model.usage", { attributes: initialAttributes });
16175
16631
  stampSpan(span, projectId, authHint);
16176
16632
  for (const attribute of attributes) {
16177
16633
  const value = attribute.value;
@@ -16292,7 +16748,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
16292
16748
  allowedInstrumentationLibraries: processorOptions == null ? void 0 : processorOptions.allowedInstrumentationLibraries,
16293
16749
  localDebuggerUrlOpt: localDebuggerUrl
16294
16750
  });
16295
- const contextProcessor = new RaindropContextSpanProcessor();
16751
+ const contextProcessor = new RaindropContextSpanProcessor((properties) => analytics.getAppGitSnapshot(properties));
16296
16752
  return withV2SpanCompat(
16297
16753
  new CompositeSpanProcessor(
16298
16754
  [contextProcessor, productionProcessor],
@@ -16529,32 +16985,6 @@ function replayIngestEndpoint(value, trustedUrl) {
16529
16985
  return url.href;
16530
16986
  }
16531
16987
 
16532
- // src/replay/git.ts
16533
- var import_node_child_process = require("child_process");
16534
- function readGitCommit() {
16535
- const sha = git(["rev-parse", "HEAD"]);
16536
- if (sha === null) return null;
16537
- const branch = git(["rev-parse", "--abbrev-ref", "HEAD"]);
16538
- const status = git(["status", "--porcelain"]);
16539
- if (status === null) return null;
16540
- return {
16541
- sha,
16542
- branch: branch === null || branch === "HEAD" ? null : branch,
16543
- dirty: status !== ""
16544
- };
16545
- }
16546
- function git(args) {
16547
- try {
16548
- return (0, import_node_child_process.execFileSync)("git", [...args], {
16549
- encoding: "utf8",
16550
- stdio: ["ignore", "pipe", "ignore"],
16551
- timeout: 2e3
16552
- }).trim();
16553
- } catch (e) {
16554
- return null;
16555
- }
16556
- }
16557
-
16558
16988
  // src/replay/index.ts
16559
16989
  var DEFAULT_QUERY_URL = "https://query.raindrop.ai";
16560
16990
  var DEFAULT_TRACE_WAIT_MS = 3e4;
@@ -16946,6 +17376,47 @@ var WireEvalCaseTracePairReadSchema = import_zod5.z.discriminatedUnion("state",
16946
17376
  reason: import_zod5.z.string()
16947
17377
  })
16948
17378
  ]);
17379
+ var replayGitSnapshots = /* @__PURE__ */ new WeakMap();
17380
+ var replayPropertySnapshots = /* @__PURE__ */ new WeakMap();
17381
+ var replayOverrideSnapshots = /* @__PURE__ */ new WeakSet();
17382
+ function withReplayGit(snapshot, run) {
17383
+ var _a;
17384
+ return withRoutingContext({
17385
+ ...currentRoutingBinding(),
17386
+ appGit: (_a = replayPropertySnapshots.get(snapshot)) != null ? _a : snapshot,
17387
+ appGitSource: replayOverrideSnapshots.has(snapshot) ? "replay-override" : void 0
17388
+ }, run);
17389
+ }
17390
+ function captureReplayGit(client, options) {
17391
+ var _a, _b;
17392
+ try {
17393
+ if (options === void 0 && client.getAppGitSnapshot) {
17394
+ const properties = Object.freeze({ ...client.getAppGitSnapshot() });
17395
+ const snapshot2 = Object.freeze({ ...(_b = (_a = client.getReplayAppGitSnapshot) == null ? void 0 : _a.call(client)) != null ? _b : properties });
17396
+ replayPropertySnapshots.set(snapshot2, properties);
17397
+ return snapshot2;
17398
+ }
17399
+ const resolver = createAppGitContext(options, { allowLocalGit: false });
17400
+ const snapshot = resolver.snapshot();
17401
+ resolver.dispose();
17402
+ if (options && typeof options === "object") replayOverrideSnapshots.add(snapshot);
17403
+ return snapshot;
17404
+ } catch (e) {
17405
+ return {};
17406
+ }
17407
+ }
17408
+ function replayGitFields(snapshot) {
17409
+ const rawSha = snapshot[APP_COMMIT_SHA];
17410
+ const sha = typeof rawSha === "string" && validAppCommitSha(rawSha) ? rawSha.trim() : void 0;
17411
+ const dirty = snapshot[APP_COMMIT_DIRTY];
17412
+ const rawBranch = snapshot[APP_BRANCH];
17413
+ const branch = typeof rawBranch === "string" && rawBranch.length <= 1024 ? rawBranch.trim() : "";
17414
+ return {
17415
+ commit_sha: sha != null ? sha : null,
17416
+ commit_dirty: sha && typeof dirty === "boolean" ? dirty : null,
17417
+ commit_branch: branch.length > 0 && branch.length <= 400 ? branch : null
17418
+ };
17419
+ }
16949
17420
  function toReading(wire) {
16950
17421
  return {
16951
17422
  replayId: wire.replay.id,
@@ -16963,11 +17434,11 @@ function toReading(wire) {
16963
17434
  };
16964
17435
  }
16965
17436
  async function createReplay(client, options) {
16966
- var _a, _b, _c, _d;
17437
+ var _a;
17438
+ const gitSnapshot = captureReplayGit(client, options.appGit);
16967
17439
  const queryUrl = (_a = options.queryUrl) != null ? _a : DEFAULT_QUERY_URL;
16968
17440
  const manifest = typeof options.dataset === "string" ? await readEvalDataset(client, options.dataset, { queryUrl }) : validateEvalDatasetManifest(options.dataset);
16969
17441
  const rowIds = selectEvalDatasetRows(manifest, options.rowIds);
16970
- const commit = readGitCommit();
16971
17442
  const created = await queryApi({
16972
17443
  client,
16973
17444
  queryUrl,
@@ -16980,9 +17451,7 @@ async function createReplay(client, options) {
16980
17451
  dataset_version_id: manifest.version.id,
16981
17452
  dataset_fingerprint: manifest.version.fingerprint,
16982
17453
  request_key: randomUUID(),
16983
- commit_sha: (_b = commit == null ? void 0 : commit.sha) != null ? _b : null,
16984
- commit_branch: (_c = commit == null ? void 0 : commit.branch) != null ? _c : null,
16985
- commit_dirty: (_d = commit == null ? void 0 : commit.dirty) != null ? _d : null,
17454
+ ...replayGitFields(gitSnapshot),
16986
17455
  row_ids: rowIds
16987
17456
  }
16988
17457
  });
@@ -16994,6 +17463,7 @@ async function createReplay(client, options) {
16994
17463
  }
16995
17464
  };
16996
17465
  assertSelectedRows(handle, rowIds);
17466
+ replayGitSnapshots.set(handle, gitSnapshot);
16997
17467
  return handle;
16998
17468
  }
16999
17469
  function selectEvalDatasetRows(manifest, requestedRowIds) {
@@ -17042,15 +17512,19 @@ function toHandle(wire) {
17042
17512
  }
17043
17513
  async function claimReplay(client, replayId, options) {
17044
17514
  var _a;
17045
- return toHandle(
17515
+ const gitSnapshot = captureReplayGit(client, options == null ? void 0 : options.appGit);
17516
+ const handle = toHandle(
17046
17517
  await queryApi({
17047
17518
  client,
17048
17519
  queryUrl: (_a = options == null ? void 0 : options.queryUrl) != null ? _a : DEFAULT_QUERY_URL,
17049
17520
  path: `/v1/replays/${encodeURIComponent(replayId)}/claim`,
17050
17521
  method: "POST",
17051
- schema: WirePlanSchema
17522
+ schema: WirePlanSchema,
17523
+ body: replayGitFields(gitSnapshot)
17052
17524
  })
17053
17525
  );
17526
+ replayGitSnapshots.set(handle, gitSnapshot);
17527
+ return handle;
17054
17528
  }
17055
17529
  async function readReplay(client, replayId, options) {
17056
17530
  var _a;
@@ -17102,7 +17576,7 @@ function validateEvalDatasetManifest(value) {
17102
17576
  return manifest;
17103
17577
  }
17104
17578
  async function runReplayRows(client, handle, options) {
17105
- var _a, _b, _c, _d, _e, _f, _g;
17579
+ var _a, _b, _c, _d, _e, _f, _g, _h;
17106
17580
  if (!Number.isSafeInteger((_a = options.concurrency) != null ? _a : DEFAULT_CONCURRENCY) || ((_b = options.concurrency) != null ? _b : DEFAULT_CONCURRENCY) < 1) {
17107
17581
  throw new Error("Raindrop replay concurrency must be a positive safe integer");
17108
17582
  }
@@ -17123,7 +17597,9 @@ async function runReplayRows(client, handle, options) {
17123
17597
  body,
17124
17598
  maxRetries: MAX_REPLAY_REPORT_RETRIES
17125
17599
  });
17126
- await report({ type: "started" });
17600
+ const gitSnapshot = (_e = replayGitSnapshots.get(handle)) != null ? _e : captureReplayGit(client);
17601
+ replayGitSnapshots.set(handle, gitSnapshot);
17602
+ await report({ type: "started", ...replayGitFields(gitSnapshot) });
17127
17603
  const rows = [...handle.rows];
17128
17604
  const results = /* @__PURE__ */ new Map();
17129
17605
  const captures = /* @__PURE__ */ new Map();
@@ -17147,7 +17623,7 @@ async function runReplayRows(client, handle, options) {
17147
17623
  exportedSpanIds: spanIds,
17148
17624
  exportedTraceIds: traceIds
17149
17625
  };
17150
- const result = await import_api7.context.with(
17626
+ const result = await withReplayGit(gitSnapshot, () => import_api7.context.with(
17151
17627
  import_api7.trace.deleteSpan(import_api7.context.active()),
17152
17628
  () => withReplayTraceDestination(
17153
17629
  replayDestination,
@@ -17174,7 +17650,7 @@ async function runReplayRows(client, handle, options) {
17174
17650
  )
17175
17651
  )
17176
17652
  )
17177
- );
17653
+ ));
17178
17654
  results.set(row.id, { value: result });
17179
17655
  } catch (cause) {
17180
17656
  const error = describeError(cause);
@@ -17205,7 +17681,7 @@ async function runReplayRows(client, handle, options) {
17205
17681
  };
17206
17682
  await Promise.all(
17207
17683
  Array.from(
17208
- { length: Math.min((_e = options.concurrency) != null ? _e : DEFAULT_CONCURRENCY, rows.length) },
17684
+ { length: Math.min((_f = options.concurrency) != null ? _f : DEFAULT_CONCURRENCY, rows.length) },
17209
17685
  () => worker()
17210
17686
  )
17211
17687
  );
@@ -17231,7 +17707,7 @@ async function runReplayRows(client, handle, options) {
17231
17707
  client,
17232
17708
  queryUrl,
17233
17709
  replayId: handle.replayId,
17234
- deadlineAt: Date.now() + ((_f = options.traceWaitMs) != null ? _f : DEFAULT_TRACE_WAIT_MS)
17710
+ deadlineAt: Date.now() + ((_g = options.traceWaitMs) != null ? _g : DEFAULT_TRACE_WAIT_MS)
17235
17711
  });
17236
17712
  for (const row of settled.rows) {
17237
17713
  if (row.status !== "pending") continue;
@@ -17240,7 +17716,7 @@ async function runReplayRows(client, handle, options) {
17240
17716
  row_id: row.rowId,
17241
17717
  attempt: row.attempt,
17242
17718
  error: `no trace arrived for this row within ${Math.round(
17243
- ((_g = options.traceWaitMs) != null ? _g : DEFAULT_TRACE_WAIT_MS) / 1e3
17719
+ ((_h = options.traceWaitMs) != null ? _h : DEFAULT_TRACE_WAIT_MS) / 1e3
17244
17720
  )}s`
17245
17721
  });
17246
17722
  }
@@ -17314,7 +17790,7 @@ async function readEvalCaseTracePair(input, evidence, attempt) {
17314
17790
  });
17315
17791
  }
17316
17792
  async function createLocalEvalReplaySession(client, options) {
17317
- var _a;
17793
+ var _a, _b, _c;
17318
17794
  if (options.evaluators.length === 0) {
17319
17795
  throw new Error("Raindrop local eval replay requires at least one evaluator");
17320
17796
  }
@@ -17335,7 +17811,8 @@ async function createLocalEvalReplaySession(client, options) {
17335
17811
  ...handle,
17336
17812
  ingestUrl: replayIngestEndpoint(requireReplayIngestUrl(handle), options.replayIngestUrl)
17337
17813
  };
17338
- await reportReplay(client, queryUrl, handle.replayId, { type: "started" });
17814
+ replayGitSnapshots.set(validatedHandle, (_b = replayGitSnapshots.get(handle)) != null ? _b : {});
17815
+ await reportReplay(client, queryUrl, handle.replayId, { type: "started", ...replayGitFields((_c = replayGitSnapshots.get(handle)) != null ? _c : {}) });
17339
17816
  const parents = await Promise.all(
17340
17817
  options.evaluators.map(async (evaluator) => {
17341
17818
  const registered = await registerLocalEvaluator(client, evaluator, queryUrl);
@@ -17380,7 +17857,7 @@ var LocalEvalReplaySessionImpl = class {
17380
17857
  );
17381
17858
  }
17382
17859
  async run(input) {
17383
- var _a, _b, _c, _d;
17860
+ var _a, _b, _c, _d, _e;
17384
17861
  validateWait("traceWaitMs", (_a = input.traceWaitMs) != null ? _a : DEFAULT_TRACE_WAIT_MS, true);
17385
17862
  if (this.state !== "open") {
17386
17863
  throw new Error(`Raindrop replay ${this.replayId} is already finishing`);
@@ -17406,30 +17883,33 @@ var LocalEvalReplaySessionImpl = class {
17406
17883
  exportedTraceIds: traceIds
17407
17884
  };
17408
17885
  try {
17409
- result = await import_api7.context.with(
17410
- import_api7.trace.deleteSpan(import_api7.context.active()),
17411
- () => withReplayTraceDestination(
17412
- replayDestination,
17413
- () => withEvalScope(
17414
- { correlationId },
17415
- () => withTask(
17416
- { name: "raindrop.replay.row", inputParameters: [row], traceContent: true },
17417
- () => {
17418
- var _a2;
17419
- const parent = (_a2 = import_api7.trace.getSpan(import_api7.context.active())) == null ? void 0 : _a2.spanContext();
17420
- if (!parent) return input.run(row);
17421
- return withReplayTraceDestination(
17422
- {
17423
- ...replayDestination,
17424
- parentSpanContext: {
17425
- traceIdB64: Buffer.from(parent.traceId, "hex").toString("base64"),
17426
- spanIdB64: Buffer.from(parent.spanId, "hex").toString("base64"),
17427
- eventId: parent.traceId
17428
- }
17429
- },
17430
- () => input.run(row)
17431
- );
17432
- }
17886
+ result = await withReplayGit(
17887
+ (_c = replayGitSnapshots.get(this.handle)) != null ? _c : {},
17888
+ () => import_api7.context.with(
17889
+ import_api7.trace.deleteSpan(import_api7.context.active()),
17890
+ () => withReplayTraceDestination(
17891
+ replayDestination,
17892
+ () => withEvalScope(
17893
+ { correlationId },
17894
+ () => withTask(
17895
+ { name: "raindrop.replay.row", inputParameters: [row], traceContent: true },
17896
+ () => {
17897
+ var _a2;
17898
+ const parent = (_a2 = import_api7.trace.getSpan(import_api7.context.active())) == null ? void 0 : _a2.spanContext();
17899
+ if (!parent) return input.run(row);
17900
+ return withReplayTraceDestination(
17901
+ {
17902
+ ...replayDestination,
17903
+ parentSpanContext: {
17904
+ traceIdB64: Buffer.from(parent.traceId, "hex").toString("base64"),
17905
+ spanIdB64: Buffer.from(parent.spanId, "hex").toString("base64"),
17906
+ eventId: parent.traceId
17907
+ }
17908
+ },
17909
+ () => input.run(row)
17910
+ );
17911
+ }
17912
+ )
17433
17913
  )
17434
17914
  )
17435
17915
  )
@@ -17453,7 +17933,7 @@ var LocalEvalReplaySessionImpl = class {
17453
17933
  attempt: input.attempt,
17454
17934
  traceIds,
17455
17935
  spanIds,
17456
- waitMs: (_c = input.traceWaitMs) != null ? _c : DEFAULT_TRACE_WAIT_MS
17936
+ waitMs: (_d = input.traceWaitMs) != null ? _d : DEFAULT_TRACE_WAIT_MS
17457
17937
  });
17458
17938
  } catch (retentionCause) {
17459
17939
  throw failedTraceRetentionError(cause, retentionCause, row.id);
@@ -17478,7 +17958,7 @@ var LocalEvalReplaySessionImpl = class {
17478
17958
  replayId: this.replayId,
17479
17959
  datasetVersion: this.handle.datasetVersion,
17480
17960
  evidence: [evidence],
17481
- waitMs: (_d = input.traceWaitMs) != null ? _d : DEFAULT_TRACE_WAIT_MS,
17961
+ waitMs: (_e = input.traceWaitMs) != null ? _e : DEFAULT_TRACE_WAIT_MS,
17482
17962
  attempt: input.attempt
17483
17963
  });
17484
17964
  const captured = pairs.get(row.id);
@@ -18103,7 +18583,7 @@ function failedTraceRetentionError(taskCause, retentionCause, rowId) {
18103
18583
  );
18104
18584
  }
18105
18585
  function sleep(ms) {
18106
- return new Promise((resolve2) => setTimeout(resolve2, ms));
18586
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
18107
18587
  }
18108
18588
  function isRetryableStatus(status) {
18109
18589
  return status === 408 || status === 429 || status >= 500;
@@ -18332,7 +18812,7 @@ function isRetryableStatus2(status) {
18332
18812
  return status === 408 || status === 429 || status >= 500;
18333
18813
  }
18334
18814
  function sleep2(ms) {
18335
- return new Promise((resolve2) => setTimeout(resolve2, ms));
18815
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
18336
18816
  }
18337
18817
 
18338
18818
  // src/evals/publish.ts
@@ -18735,7 +19215,7 @@ async function readWorkshopTrace(input) {
18735
19215
  throw new Error(`Workshop trace ${input.correlationId} remained ${state2}`);
18736
19216
  }
18737
19217
  await new Promise(
18738
- (resolve2) => setTimeout(resolve2, Math.min(50, Math.max(0, deadlineAt - Date.now())))
19218
+ (resolve3) => setTimeout(resolve3, Math.min(50, Math.max(0, deadlineAt - Date.now())))
18739
19219
  );
18740
19220
  }
18741
19221
  }
@@ -19041,7 +19521,7 @@ var RaindropRowEvalRun = class {
19041
19521
  var _a;
19042
19522
  const limit = (_a = this.definition.concurrency) != null ? _a : 4;
19043
19523
  if (this.runningRows >= limit) {
19044
- await new Promise((resolve2) => this.rowWaiters.push(resolve2));
19524
+ await new Promise((resolve3) => this.rowWaiters.push(resolve3));
19045
19525
  } else {
19046
19526
  this.runningRows += 1;
19047
19527
  }
@@ -19325,7 +19805,7 @@ var WorkshopEvalRun = class {
19325
19805
  var _a;
19326
19806
  const limit = (_a = this.definition.concurrency) != null ? _a : 4;
19327
19807
  if (this.runningRows >= limit) {
19328
- await new Promise((resolve2) => this.rowWaiters.push(resolve2));
19808
+ await new Promise((resolve3) => this.rowWaiters.push(resolve3));
19329
19809
  } else {
19330
19810
  this.runningRows += 1;
19331
19811
  }
@@ -19721,6 +20201,15 @@ function isPortableEvaluator(value) {
19721
20201
  }
19722
20202
 
19723
20203
  // src/index.ts
20204
+ function rawAppGitProperties(properties) {
20205
+ const result = {};
20206
+ for (const key of [APP_COMMIT_SHA, APP_COMMIT_DIRTY, APP_BRANCH]) {
20207
+ if (properties && Object.prototype.hasOwnProperty.call(properties, key)) {
20208
+ result[key] = properties[key];
20209
+ }
20210
+ }
20211
+ return result;
20212
+ }
19724
20213
  var MAX_INGEST_SIZE_BYTES = 1 * 1024 * 1024;
19725
20214
  var SELF_DIAGNOSTICS_TOOL_NAME_DEFAULT = "__raindrop_report";
19726
20215
  var SELF_DIAGNOSTICS_SOURCE_DEFAULT = "agent_reporting_tool";
@@ -19828,7 +20317,7 @@ function resolveEventIdFromMetadata(metadata) {
19828
20317
  return (_c = (_b = (_a = normalizeEventId(metadata["raindrop.eventId"])) != null ? _a : normalizeEventId(metadata["event_id"])) != null ? _b : normalizeEventId(metadata["eventId"])) != null ? _c : normalizeEventId(metadata["traceloop.association.properties.event_id"]);
19829
20318
  }
19830
20319
  async function wait3(ms) {
19831
- return new Promise((resolve2) => setTimeout(resolve2, ms));
20320
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
19832
20321
  }
19833
20322
  function parseRetryAfter3(headers) {
19834
20323
  var _a;
@@ -19903,18 +20392,23 @@ var Raindrop = class {
19903
20392
  */
19904
20393
  this.hasShutdown = false;
19905
20394
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
20395
+ const writeKey = (_a = config.writeKey) != null ? _a : "";
20396
+ const disabled = (_b = config.disabled) != null ? _b : false;
20397
+ const localDebuggerUrlOpt = config.localWorkshopUrl === false ? null : config.localWorkshopUrl;
20398
+ const apiUrl = (_c = this.formatEndpoint(config.endpoint)) != null ? _c : `https://api.raindrop.ai/v1/`;
20399
+ const url = new URL(apiUrl);
19906
20400
  setDefaultMaxTextFieldChars(config.maxTextFieldChars);
19907
20401
  this.maxTextFieldChars = resolveMaxTextFieldChars(config.maxTextFieldChars);
19908
- this.writeKey = (_a = config.writeKey) != null ? _a : "";
20402
+ this.writeKey = writeKey;
19909
20403
  this.apiKey = config.apiKey;
19910
20404
  this.authHint = authHintForKey(this.writeKey);
19911
- this.localDebuggerUrlOpt = config.localWorkshopUrl === false ? null : config.localWorkshopUrl;
20405
+ this.localDebuggerUrlOpt = localDebuggerUrlOpt;
19912
20406
  this.awaitSpanFlushOpt = config.awaitSpanFlush;
19913
- this.apiUrl = (_b = this.formatEndpoint(config.endpoint)) != null ? _b : `https://api.raindrop.ai/v1/`;
19914
- this.bufferSize = (_c = config.bufferSize) != null ? _c : 50;
19915
- this.bufferTimeout = (_d = config.bufferTimeout) != null ? _d : 1e3;
20407
+ this.apiUrl = apiUrl;
20408
+ this.bufferSize = (_d = config.bufferSize) != null ? _d : 50;
20409
+ this.bufferTimeout = (_e = config.bufferTimeout) != null ? _e : 1e3;
19916
20410
  this.buffer = new Array();
19917
- this.disabled = (_e = config.disabled) != null ? _e : false;
20411
+ this.disabled = disabled;
19918
20412
  this.wizardSession = (_f = config.wizardSession) != null ? _f : null;
19919
20413
  this.debugLogs = (_g = config.debugLogs) != null ? _g : false;
19920
20414
  this.projectId = normalizeProjectId(config.projectId, {
@@ -19932,34 +20426,43 @@ var Raindrop = class {
19932
20426
  );
19933
20427
  }
19934
20428
  const useExternalOtel = (_j = (_i = config.useExternalOtel) != null ? _i : config.disableTracing) != null ? _j : false;
19935
- const url = new URL(this.apiUrl);
19936
20429
  const disableBatching = resolveDisableBatching(config.disableBatching, {
19937
20430
  isProduction: process.env.NODE_ENV === "production",
19938
20431
  isLocalDebugger: localDebuggerEnabled(this.localDebuggerUrlOpt)
19939
20432
  });
19940
- this._tracing = tracing(
19941
- this,
19942
- url.origin,
19943
- this.writeKey,
19944
- {
19945
- disableBatch: disableBatching,
19946
- disableBatching,
19947
- logLevel: this.debugLogs ? "debug" : "error",
19948
- // Pass through disabled state - tracer-core handles no-op exporter logic
19949
- tracingEnabled: !this.disabled,
19950
- useExternalOtel,
19951
- instrumentModules: config.instrumentModules,
19952
- bypassOtelForTools: config.bypassOtelForTools,
19953
- // Pass full API URL (with /v1/ path) for DirectTraceShipper.
19954
- // url.origin strips the path, which traceloop handles internally,
19955
- // but the direct shipper needs the full path to post to /v1/traces.
19956
- directEndpoint: this.apiUrl,
19957
- localDebuggerUrl: this.localDebuggerUrlOpt,
19958
- cloudEnabled: !this.localOnly,
19959
- projectId: this.projectId
19960
- },
19961
- this.debugLogs
19962
- );
20433
+ const hasDestination = !this.disabled && (this.writeKey.trim().length > 0 || localDebuggerEnabled(this.localDebuggerUrlOpt));
20434
+ this.appGitContext = createAppGitContext(this.disabled ? false : config.appGit, {
20435
+ captureLegacyReplayBranch: true,
20436
+ allowDiscovery: hasDestination
20437
+ });
20438
+ try {
20439
+ this._tracing = tracing(
20440
+ this,
20441
+ url.origin,
20442
+ this.writeKey,
20443
+ {
20444
+ disableBatch: disableBatching,
20445
+ disableBatching,
20446
+ logLevel: this.debugLogs ? "debug" : "error",
20447
+ // Pass through disabled state - tracer-core handles no-op exporter logic
20448
+ tracingEnabled: !this.disabled,
20449
+ useExternalOtel,
20450
+ instrumentModules: config.instrumentModules,
20451
+ bypassOtelForTools: config.bypassOtelForTools,
20452
+ // Pass full API URL (with /v1/ path) for DirectTraceShipper.
20453
+ // url.origin strips the path, which traceloop handles internally,
20454
+ // but the direct shipper needs the full path to post to /v1/traces.
20455
+ directEndpoint: this.apiUrl,
20456
+ localDebuggerUrl: this.localDebuggerUrlOpt,
20457
+ cloudEnabled: !this.localOnly,
20458
+ projectId: this.projectId
20459
+ },
20460
+ this.debugLogs
20461
+ );
20462
+ } catch (error) {
20463
+ this.appGitContext.dispose();
20464
+ throw error;
20465
+ }
19963
20466
  }
19964
20467
  get localOnly() {
19965
20468
  return !this.writeKey;
@@ -20000,7 +20503,29 @@ var Raindrop = class {
20000
20503
  * @returns The interaction object.
20001
20504
  */
20002
20505
  begin(traceContext) {
20003
- return this._tracing.begin(traceContext);
20506
+ var _a;
20507
+ const eventId = (_a = traceContext.eventId) != null ? _a : crypto.randomUUID();
20508
+ return this._tracing.begin({
20509
+ ...traceContext,
20510
+ eventId,
20511
+ properties: this.propertiesWithAppGit(traceContext.properties, eventId)
20512
+ });
20513
+ }
20514
+ /** @internal Snapshot only; this never waits for optional Git discovery. */
20515
+ getAppGitSnapshot(properties, eventId) {
20516
+ return this.appGitContext.forOperation(eventId, properties);
20517
+ }
20518
+ /** @internal Existing replay branch reporting; ordinary properties remain opt-in. */
20519
+ getReplayAppGitSnapshot() {
20520
+ return this.appGitContext.snapshotForReplay();
20521
+ }
20522
+ propertiesWithAppGit(properties, eventId) {
20523
+ const result = { ...properties };
20524
+ for (const [key, value] of Object.entries(this.getAppGitSnapshot(properties, eventId))) {
20525
+ if (Object.prototype.hasOwnProperty.call(result, key)) continue;
20526
+ if (value === null || typeof value === "string" || typeof value === "boolean") result[key] = value;
20527
+ }
20528
+ return result;
20004
20529
  }
20005
20530
  /**
20006
20531
  * Returns a tracer object that can be used to create spans and tools that aren't tied to any interaction.
@@ -20027,7 +20552,7 @@ var Raindrop = class {
20027
20552
  * ```
20028
20553
  */
20029
20554
  asCurrent(fn) {
20030
- return withRoutingContext({ projectId: this.projectId, authHint: this.authHint }, fn);
20555
+ return withRoutingContext({ projectId: this.projectId, authHint: this.authHint, appGit: this.appGitContext.snapshot() }, fn);
20031
20556
  }
20032
20557
  createSpanProcessor(processorOptions) {
20033
20558
  return this._tracing.createSpanProcessor(processorOptions);
@@ -20233,7 +20758,6 @@ var Raindrop = class {
20233
20758
  const destination = currentReplayTraceDestination();
20234
20759
  const cloudReplay = destination !== void 0 && destination.kind !== "workshop";
20235
20760
  const addEvent = (e) => {
20236
- var _a;
20237
20761
  const {
20238
20762
  eventId,
20239
20763
  userId,
@@ -20279,28 +20803,38 @@ var Raindrop = class {
20279
20803
  return;
20280
20804
  }
20281
20805
  const toSave = this.redactPii ? redactPII(parsed.data) : parsed.data;
20282
- toSave.properties = {
20283
- ...(_a = toSave.properties) != null ? _a : {},
20284
- ...this.wizardSession ? { "raindrop.wizardSession": this.wizardSession } : {},
20285
- // Last, so a caller-supplied property cannot shadow the replay row the
20286
- // event belongs to.
20287
- ...evalScopeAttributes(),
20288
- $context: this.context
20289
- };
20290
- const size = this.getSize(toSave);
20291
- if (size > MAX_INGEST_SIZE_BYTES) {
20292
- console.warn(
20293
- `[raindrop] Events larger than ${(MAX_INGEST_SIZE_BYTES / (1024 * 1024)).toFixed(2)} MB may have properties truncated - an event of size ${(size / (1024 * 1024)).toFixed(2)} MB was logged`
20294
- );
20295
- }
20296
- if (!cloudReplay) {
20297
- this.saveToBuffer({
20298
- type: "ai",
20299
- ...toSave
20300
- });
20806
+ const appGitSnapshot = this.getAppGitSnapshot(toSave.properties, customEventId);
20807
+ const eventProperties = withAppGitProperties(toSave.properties, appGitSnapshot);
20808
+ const usageProperties = withAppGitProperties(
20809
+ rawAppGitProperties(toSave.properties),
20810
+ appGitSnapshot
20811
+ );
20812
+ try {
20813
+ toSave.properties = {
20814
+ ...eventProperties,
20815
+ ...this.wizardSession ? { "raindrop.wizardSession": this.wizardSession } : {},
20816
+ // Last, so a caller-supplied property cannot shadow the replay row the
20817
+ // event belongs to.
20818
+ ...evalScopeAttributes(),
20819
+ $context: this.context
20820
+ };
20821
+ const size = this.getSize(toSave);
20822
+ if (size > MAX_INGEST_SIZE_BYTES) {
20823
+ console.warn(
20824
+ `[raindrop] Events larger than ${(MAX_INGEST_SIZE_BYTES / (1024 * 1024)).toFixed(2)} MB may have properties truncated - an event of size ${(size / (1024 * 1024)).toFixed(2)} MB was logged`
20825
+ );
20826
+ }
20827
+ if (!cloudReplay) {
20828
+ this.saveToBuffer({
20829
+ type: "ai",
20830
+ ...toSave
20831
+ });
20832
+ }
20833
+ this._recordModelUsage(customEventId, model, usage, usageProperties, appGitSnapshot);
20834
+ return customEventId;
20835
+ } finally {
20836
+ this.appGitContext.finishOperation(customEventId);
20301
20837
  }
20302
- this._recordModelUsage(customEventId, model, usage);
20303
- return customEventId;
20304
20838
  };
20305
20839
  if (Array.isArray(event)) {
20306
20840
  return event.map(addEvent);
@@ -20615,6 +21149,7 @@ var Raindrop = class {
20615
21149
  return Promise.resolve();
20616
21150
  }
20617
21151
  const { eventId, isPending, ...rest } = event;
21152
+ rest.properties = this.propertiesWithAppGit(rest.properties, eventId);
20618
21153
  if (!eventId) {
20619
21154
  console.warn("[raindrop] trackAiPartial requires an eventId.");
20620
21155
  return Promise.resolve();
@@ -20626,6 +21161,7 @@ var Raindrop = class {
20626
21161
  if (replayOnly) {
20627
21162
  if (isPending === false) {
20628
21163
  this.partialEventBuffer.delete(eventId);
21164
+ this.appGitContext.finishOperation(eventId);
20629
21165
  } else if (!captured) {
20630
21166
  this.partialEventBuffer.set(eventId, {
20631
21167
  replayOnly: true
@@ -20730,6 +21266,7 @@ var Raindrop = class {
20730
21266
  });
20731
21267
  } else {
20732
21268
  this.partialEventBuffer.delete(eventId);
21269
+ this.appGitContext.finishOperation(eventId);
20733
21270
  }
20734
21271
  if (!accumulatedEvent) {
20735
21272
  if (this.debugLogs) {
@@ -20900,7 +21437,7 @@ var Raindrop = class {
20900
21437
  * Emit one model span carrying raw provider usage facts.
20901
21438
  * @internal
20902
21439
  */
20903
- _recordModelUsage(eventId, model, usage) {
21440
+ _recordModelUsage(eventId, model, usage, properties = {}, appGitSnapshot = this.getAppGitSnapshot(properties)) {
20904
21441
  if (usage === void 0) return;
20905
21442
  const parsed = AiTrackUsageSchema.safeParse(usage);
20906
21443
  if (!parsed.success) {
@@ -20916,7 +21453,9 @@ var Raindrop = class {
20916
21453
  this._tracing.recordModelUsage({
20917
21454
  eventId,
20918
21455
  model,
20919
- usage: parsed.data
21456
+ usage: parsed.data,
21457
+ appGitProperties: rawAppGitProperties(properties),
21458
+ appGitSnapshot
20920
21459
  });
20921
21460
  }
20922
21461
  /**
@@ -20948,6 +21487,7 @@ var Raindrop = class {
20948
21487
  return this.awaitSpanFlushOpt !== false;
20949
21488
  }
20950
21489
  async close() {
21490
+ this.appGitContext.dispose();
20951
21491
  this.hasShutdown = true;
20952
21492
  this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
20953
21493
  try {