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/index.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(resolve) {
5680
- resolve(value);
5679
+ return value instanceof P ? value : new P(function(resolve2) {
5680
+ resolve2(value);
5681
5681
  });
5682
5682
  }
5683
- return new (P || (P = Promise))(function(resolve, reject) {
5683
+ return new (P || (P = Promise))(function(resolve2, 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 ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
5699
+ result.done ? resolve2(result.value) : adopt(result.value).then(fulfilled, rejected);
5700
5700
  }
5701
5701
  step((generator = generator.apply(thisArg, _arguments || [])).next());
5702
5702
  });
@@ -6944,7 +6944,7 @@ async function drivePromise(runtime, context9, promise, deadline) {
6944
6944
  const state2 = context9.getPromiseState(promise);
6945
6945
  if (state2.type === "fulfilled") return { value: state2.value };
6946
6946
  if (state2.type === "rejected") return { error: state2.error };
6947
- await new Promise((resolve) => setTimeout(resolve, 0));
6947
+ await new Promise((resolve2) => setTimeout(resolve2, 0));
6948
6948
  if (Date.now() >= deadline) throw new Error("interrupted");
6949
6949
  }
6950
6950
  }
@@ -7209,7 +7209,7 @@ __export(index_exports, {
7209
7209
  DEFAULT_EVAL_WAIT_MS: () => DEFAULT_EVAL_WAIT_MS,
7210
7210
  DEFAULT_QUERY_URL: () => DEFAULT_QUERY_URL,
7211
7211
  DEFAULT_TRACE_WAIT_MS: () => DEFAULT_TRACE_WAIT_MS,
7212
- EVAL_CORRELATION_ID_ATTRIBUTE: () => EVAL_CORRELATION_ID_ATTRIBUTE,
7212
+ EVAL_CORRELATION_ID_ATTRIBUTE: () => EVAL_CORRELATION_ID_ATTRIBUTE2,
7213
7213
  EvalDatasetPublishConflictError: () => EvalDatasetPublishConflictError,
7214
7214
  EvalDatasetRowInputSchema: () => EvalDatasetRowInputSchema,
7215
7215
  EvalPublishError: () => EvalPublishError,
@@ -7232,7 +7232,7 @@ __export(index_exports, {
7232
7232
  compareEvalRuns: () => compareEvalRuns,
7233
7233
  createEvalSuiteRun: () => createEvalSuiteRun,
7234
7234
  createReplay: () => createReplay,
7235
- currentEvalScope: () => currentEvalScope,
7235
+ currentEvalScope: () => currentEvalScope2,
7236
7236
  default: () => index_default,
7237
7237
  defineDataset: () => defineDataset,
7238
7238
  defineEvalSuite: () => defineEvalSuite,
@@ -7263,7 +7263,7 @@ __export(index_exports, {
7263
7263
  traceToolCalls: () => traceToolCalls,
7264
7264
  traceTools: () => traceTools,
7265
7265
  verifyPortableEvalArtifact: () => verifyPortableEvalArtifact,
7266
- withEvalScope: () => withEvalScope
7266
+ withEvalScope: () => withEvalScope2
7267
7267
  });
7268
7268
  module.exports = __toCommonJS(index_exports);
7269
7269
 
@@ -7348,1135 +7348,1509 @@ function evalScopeAttributes() {
7348
7348
  return { [EVAL_CORRELATION_ID_ATTRIBUTE]: scope.correlationId };
7349
7349
  }
7350
7350
 
7351
- // ../core/dist/chunk-T53LQAH7.js
7352
- function normalizeFeatureFlags(input) {
7353
- if (Array.isArray(input)) {
7354
- return Object.fromEntries(input.map((name) => [name, "true"]));
7355
- }
7356
- return Object.fromEntries(
7357
- Object.entries(input).map(([k, v]) => [k, String(v)])
7358
- );
7359
- }
7360
- function getCrypto() {
7361
- const c = globalThis.crypto;
7362
- return c;
7363
- }
7364
- function randomBytes(length) {
7365
- const cryptoObj = getCrypto();
7366
- const out = new Uint8Array(length);
7367
- if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
7368
- cryptoObj.getRandomValues(out);
7369
- return out;
7370
- }
7371
- for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
7372
- return out;
7373
- }
7374
- function randomUUID() {
7375
- const cryptoObj = getCrypto();
7376
- if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
7377
- return cryptoObj.randomUUID();
7351
+ // ../core/dist/chunk-WJOR6M7H.js
7352
+ var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
7353
+ function state() {
7354
+ const holder = globalThis;
7355
+ let current = holder[STATE_KEY];
7356
+ if (!current) {
7357
+ let storage = null;
7358
+ try {
7359
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
7360
+ if (Ctor) storage = new Ctor();
7361
+ } catch (e) {
7362
+ storage = null;
7363
+ }
7364
+ current = {
7365
+ storage,
7366
+ syncDestination: void 0,
7367
+ spanDestinations: /* @__PURE__ */ new WeakMap()
7368
+ };
7369
+ holder[STATE_KEY] = current;
7378
7370
  }
7379
- const b = randomBytes(16);
7380
- b[6] = b[6] & 15 | 64;
7381
- b[8] = b[8] & 63 | 128;
7382
- const hex = [...b].map((x) => x.toString(16).padStart(2, "0")).join("");
7383
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
7371
+ return current;
7384
7372
  }
7385
- var HANDOFF_ATTRIBUTE_SUFFIXES = {
7386
- mode: "raindrop.handoff.mode",
7387
- childEventId: "raindrop.handoff.childEventId",
7388
- parentEventId: "raindrop.handoff.parentEventId",
7389
- parentSpanId: "raindrop.handoff.parentSpanId",
7390
- name: "raindrop.handoff.name",
7391
- terminal: "raindrop.handoff.terminal",
7392
- agentRole: "raindrop.agent.role"
7393
- };
7394
- var AI_SDK_METADATA_PREFIX = "ai.telemetry.metadata.";
7395
- var DETACHED_MODE = "detached";
7396
- var SUBAGENT_AGENT_ROLE = "subagent";
7397
- var HANDOFF_TERMINAL_CANCELLED = "cancelled";
7398
- var TOOL_EVENTS_SUFFIX = "raindrop.toolEvents";
7399
- var TOOL_EVENTS_ALLOW = "allow";
7400
- var OPERATION_ID_ATTRIBUTE = "ai.operationId";
7401
- var SUBAGENT_ABORT_OPERATION_ID = "ai.subagent.failed";
7402
- var SUBAGENT_ABORT_SPAN_NAME = SUBAGENT_ABORT_OPERATION_ID;
7403
- var DEFAULT_DISPATCH_SPAN_NAME = "launch_subagent";
7404
- var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
7405
- function defined(entries) {
7406
- const out = {};
7407
- for (const [key, value] of Object.entries(entries)) {
7408
- if (typeof value === "string" && value.trim().length > 0) out[key] = value;
7373
+ function withReplayTraceDestination(destination, callback) {
7374
+ const routing = state();
7375
+ if (routing.storage) return routing.storage.run(destination, callback);
7376
+ const previous = routing.syncDestination;
7377
+ routing.syncDestination = destination;
7378
+ try {
7379
+ return callback();
7380
+ } finally {
7381
+ routing.syncDestination = previous;
7409
7382
  }
7410
- return out;
7411
- }
7412
- function detachedDispatchMetadata(dispatch) {
7413
- return defined({
7414
- [HANDOFF_ATTRIBUTE_SUFFIXES.mode]: DETACHED_MODE,
7415
- [HANDOFF_ATTRIBUTE_SUFFIXES.childEventId]: dispatch.childEventId,
7416
- [HANDOFF_ATTRIBUTE_SUFFIXES.name]: dispatch.name
7417
- });
7418
- }
7419
- function detachedChildMetadata(link) {
7420
- return defined({
7421
- [HANDOFF_ATTRIBUTE_SUFFIXES.parentEventId]: link.parentEventId,
7422
- [HANDOFF_ATTRIBUTE_SUFFIXES.parentSpanId]: link.parentSpanId,
7423
- [HANDOFF_ATTRIBUTE_SUFFIXES.name]: link.name,
7424
- [HANDOFF_ATTRIBUTE_SUFFIXES.agentRole]: SUBAGENT_AGENT_ROLE
7425
- });
7426
7383
  }
7427
- function cancelledTerminalMetadata() {
7428
- return { [HANDOFF_ATTRIBUTE_SUFFIXES.terminal]: HANDOFF_TERMINAL_CANCELLED };
7384
+ function currentReplayTraceDestination() {
7385
+ var _a, _b;
7386
+ const routing = state();
7387
+ return (_b = (_a = routing.storage) == null ? void 0 : _a.getStore()) != null ? _b : routing.syncDestination;
7429
7388
  }
7430
- function cancelledTerminalAttributes() {
7431
- const bare = cancelledTerminalMetadata();
7432
- return { ...bare, ...withMetadataPrefix(bare) };
7389
+ function rememberReplayTraceDestination(span) {
7390
+ const destination = currentReplayTraceDestination();
7391
+ if (destination) state().spanDestinations.set(span, destination);
7433
7392
  }
7434
- function toolEventsAllowMetadata() {
7435
- return { [TOOL_EVENTS_SUFFIX]: TOOL_EVENTS_ALLOW };
7393
+ function copyReplayTraceDestination(source, target) {
7394
+ const destination = state().spanDestinations.get(source);
7395
+ if (destination) state().spanDestinations.set(target, destination);
7436
7396
  }
7437
- function withMetadataPrefix(metadata) {
7438
- const out = {};
7439
- for (const [key, value] of Object.entries(metadata)) {
7440
- out[key.startsWith(AI_SDK_METADATA_PREFIX) ? key : `${AI_SDK_METADATA_PREFIX}${key}`] = value;
7397
+ function replayTraceDestinationFor(value) {
7398
+ if (typeof value !== "object" && typeof value !== "function" || value === null) {
7399
+ return void 0;
7441
7400
  }
7442
- return out;
7401
+ return state().spanDestinations.get(value);
7443
7402
  }
7444
- var BAGGAGE_KEYS = {
7445
- eventId: "raindrop-event-id",
7446
- childEventId: "raindrop-child-event-id",
7447
- name: "raindrop-handoff-name",
7448
- convoId: "raindrop-convo-id",
7449
- userId: "raindrop-user-id",
7450
- // The dispatch span and its trace, carried as fields as well as in
7451
- // `traceparent`. Not redundant: OTel's HTTP instrumentations rewrite
7452
- // `traceparent` from whatever span is current as the request goes out, so by
7453
- // the time the child reads it the ids name the HTTP client span and its trace
7454
- // instead of the dispatch. The child would then point its reverse reference at
7455
- // a span the launcher's UI does not know as a dispatch. A field states which
7456
- // span it means rather than meaning "whoever wrote this header last".
7457
- dispatchSpanId: "raindrop-dispatch-span-id",
7458
- traceId: "raindrop-trace-id"
7459
- };
7460
- var TRACEPARENT_HEADER = "traceparent";
7461
- var BAGGAGE_HEADER = "baggage";
7462
- var W3C_TRACE_ID = /^[0-9a-f]{32}$/;
7463
- var W3C_SPAN_ID = /^[0-9a-f]{16}$/;
7464
- function isW3CTraceId(value) {
7465
- return value !== void 0 && W3C_TRACE_ID.test(value);
7403
+ function recordReplayExportedSpan(destination, spanId, traceId) {
7404
+ var _a, _b;
7405
+ if (!destination || !spanId || !traceId) return;
7406
+ (_a = destination.exportedSpanIds) == null ? void 0 : _a.add(spanId);
7407
+ (_b = destination.exportedTraceIds) == null ? void 0 : _b.add(traceId);
7466
7408
  }
7467
- function isW3CSpanId(value) {
7468
- return value !== void 0 && W3C_SPAN_ID.test(value);
7409
+ function replayTraceUrl(baseUrl) {
7410
+ return `${baseUrl.replace(/\/+$/, "")}/v1/traces`;
7469
7411
  }
7470
- var warnedNonConformantIds = false;
7471
- function warnNonConformantIdsOnce(traceId, spanId) {
7472
- if (warnedNonConformantIds) return;
7473
- warnedNonConformantIds = true;
7474
- console.warn(
7475
- `[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.`
7476
- );
7412
+ function getAsyncLocalStorageCtor() {
7413
+ return globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
7477
7414
  }
7478
- var HANDOFF_HEADER = "x-raindrop-handoff";
7479
- var LANGSMITH_TRACE_HEADER = "langsmith-trace";
7480
- var LANGSMITH_METADATA_BAGGAGE_KEY = "langsmith-metadata";
7481
- function encodeBaggage(entries) {
7482
- return Object.entries(entries).filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join(",");
7415
+ function getWeakRefCtor() {
7416
+ const ctor = globalThis.WeakRef;
7417
+ return typeof ctor === "function" ? ctor : void 0;
7483
7418
  }
7484
- function decodeBaggage(header) {
7485
- if (!header) return {};
7486
- const parsed = {};
7487
- for (const part of header.split(",")) {
7488
- const [rawKey, ...rest] = part.split("=");
7489
- if (!rawKey || rest.length === 0) continue;
7490
- const key = rawKey.trim();
7491
- let value;
7419
+ var MAX_BINDING_STACK = 128;
7420
+ var EMPTY_STACK = [];
7421
+ var ContextStackStorage = class {
7422
+ /** Emitted once when this runtime cannot support an ambient push. */
7423
+ constructor(noAmbientBindWarning) {
7424
+ this.noAmbientBindWarning = noAmbientBindWarning;
7425
+ this._als = null;
7426
+ this._enterWithUsable = false;
7427
+ this._syncStack = EMPTY_STACK;
7428
+ this._adoptionChecked = false;
7429
+ this._warnedNoAmbientBind = false;
7430
+ }
7431
+ maybeAdoptAsyncLocalStorage() {
7432
+ if (this._adoptionChecked) return;
7433
+ this._adoptionChecked = true;
7434
+ const Ctor = getAsyncLocalStorageCtor();
7435
+ if (!Ctor) return;
7436
+ const als = new Ctor();
7492
7437
  try {
7493
- value = decodeURIComponent(rest.join("=").trim());
7438
+ als.run(EMPTY_STACK, () => {
7439
+ });
7494
7440
  } catch (e) {
7495
- value = rest.join("=").trim();
7441
+ return;
7442
+ }
7443
+ this._als = als;
7444
+ if (typeof als.enterWith === "function") {
7445
+ try {
7446
+ als.enterWith(EMPTY_STACK);
7447
+ this._enterWithUsable = true;
7448
+ } catch (e) {
7449
+ this._enterWithUsable = false;
7450
+ }
7496
7451
  }
7497
- if (key in parsed && parsed[key] !== value) throw new ConflictingCarrierError(key);
7498
- parsed[key] = value;
7499
7452
  }
7500
- return parsed;
7501
- }
7502
- function carrierBaggage(carrier) {
7503
- return {
7504
- [BAGGAGE_KEYS.eventId]: carrier.eventId,
7505
- [BAGGAGE_KEYS.childEventId]: carrier.childEventId,
7506
- [BAGGAGE_KEYS.name]: carrier.name,
7507
- [BAGGAGE_KEYS.convoId]: carrier.convoId,
7508
- [BAGGAGE_KEYS.userId]: carrier.userId,
7509
- [BAGGAGE_KEYS.dispatchSpanId]: carrier.spanId,
7510
- [BAGGAGE_KEYS.traceId]: carrier.traceId
7511
- };
7512
- }
7513
- function toHeaders(carrier) {
7514
- const fields = encodeBaggage(carrierBaggage(carrier));
7515
- const headers = {
7516
- [BAGGAGE_HEADER]: fields,
7517
- [HANDOFF_HEADER]: fields
7518
- };
7519
- if (isW3CTraceId(carrier.traceId) && isW3CSpanId(carrier.spanId)) {
7520
- headers[TRACEPARENT_HEADER] = `00-${carrier.traceId}-${carrier.spanId}-01`;
7453
+ getStack() {
7454
+ var _a;
7455
+ this.maybeAdoptAsyncLocalStorage();
7456
+ if (this._als) return (_a = this._als.getStore()) != null ? _a : this._syncStack;
7457
+ return this._syncStack;
7521
7458
  }
7522
- return headers;
7523
- }
7524
- function langsmithStamp() {
7525
- return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "000000");
7526
- }
7527
- function toLangSmithHeaders(carrier) {
7528
- const stamp = langsmithStamp();
7529
- const dottedOrder = `${stamp}Z${carrier.traceId}.${stamp}Z${carrier.spanId}`;
7530
- return {
7531
- [LANGSMITH_TRACE_HEADER]: dottedOrder,
7532
- [BAGGAGE_HEADER]: encodeBaggage({
7533
- [LANGSMITH_METADATA_BAGGAGE_KEY]: JSON.stringify(carrierBaggage(carrier))
7534
- }),
7535
- // Carried here too: a LangSmith-shaped carrier is rewritten in transit
7536
- // exactly like the native one, and an injected `traceparent` is preferred
7537
- // over the dotted order on read.
7538
- [HANDOFF_HEADER]: encodeBaggage(carrierBaggage(carrier))
7539
- };
7540
- }
7541
- function isHeaderLookup(headers) {
7542
- return "get" in headers && typeof headers.get === "function";
7543
- }
7544
- var ConflictingCarrierError = class extends Error {
7545
- };
7546
- function readHeader(headers, name) {
7547
- var _a, _b, _c;
7548
- if (isHeaderLookup(headers)) return (_a = headers.get(name)) != null ? _a : void 0;
7549
- const value = (_c = headers[name]) != null ? _c : (_b = Object.entries(headers).find(([key]) => key.toLowerCase() === name)) == null ? void 0 : _b[1];
7550
- if (!Array.isArray(value)) return value;
7551
- const distinct = new Set(value);
7552
- if (distinct.size > 1) throw new ConflictingCarrierError(name);
7553
- return value[0];
7554
- }
7555
- function readTraceHeader(headers, name) {
7556
- const value = readHeader(headers, name);
7557
- if (!(value == null ? void 0 : value.includes(","))) return value;
7558
- const copies = value.split(",").map((copy) => copy.trim());
7559
- if (new Set(copies).size > 1) throw new ConflictingCarrierError(name);
7560
- return copies[0];
7561
- }
7562
- function fromHeaders(headers) {
7563
- var _a, _b;
7564
- let baggage;
7565
- let handoff;
7566
- let traceparent;
7567
- let langsmithTrace;
7568
- try {
7569
- baggage = decodeBaggage(readHeader(headers, BAGGAGE_HEADER));
7570
- handoff = decodeBaggage(readHeader(headers, HANDOFF_HEADER));
7571
- traceparent = readTraceHeader(headers, TRACEPARENT_HEADER);
7572
- langsmithTrace = readTraceHeader(headers, LANGSMITH_TRACE_HEADER);
7573
- } catch (error) {
7574
- if (error instanceof ConflictingCarrierError) return null;
7575
- throw error;
7459
+ /**
7460
+ * Persist an ambient stack mutation. Returns false when an ALS is adopted but
7461
+ * `enterWith` is unusable (workerd): callers must NOT fall back to the
7462
+ * process-shared sync stack, since concurrent isolates would cross-stamp.
7463
+ */
7464
+ setAmbientStack(stack) {
7465
+ if (this._als) {
7466
+ if (!this._enterWithUsable) return false;
7467
+ this._als.enterWith(stack);
7468
+ return true;
7469
+ }
7470
+ this._syncStack = stack;
7471
+ return true;
7576
7472
  }
7577
- const langsmithMetadata = baggage[LANGSMITH_METADATA_BAGGAGE_KEY];
7578
- const nested = langsmithMetadata ? safeParseStringRecord(langsmithMetadata) : {};
7579
- for (const [key, value] of Object.entries(nested)) {
7580
- if (key in baggage && baggage[key] !== value) return null;
7473
+ push(binding) {
7474
+ this.maybeAdoptAsyncLocalStorage();
7475
+ if (this._als && !this._enterWithUsable) {
7476
+ this.warnNoAmbientBindOnce();
7477
+ return;
7478
+ }
7479
+ let stack = this.getStack();
7480
+ if (stack.length >= MAX_BINDING_STACK) {
7481
+ stack = stack.filter((b) => b.isLive());
7482
+ if (stack.length >= MAX_BINDING_STACK) {
7483
+ stack = stack.slice(stack.length - (MAX_BINDING_STACK - 1));
7484
+ }
7485
+ }
7486
+ this.setAmbientStack([...stack, binding]);
7581
7487
  }
7582
- const fields = {
7583
- ...baggage,
7584
- ...nested,
7585
- // Last, so it wins: the standard headers may have been rewritten in transit
7586
- // by a propagator, this one is only ever written by us.
7587
- ...handoff
7588
- };
7589
- let traceId;
7590
- let spanId;
7591
- if (traceparent) {
7592
- const [, parsedTraceId, parsedSpanId] = traceparent.split("-");
7593
- if (isW3CTraceId(parsedTraceId) && isW3CSpanId(parsedSpanId)) {
7594
- traceId = parsedTraceId;
7595
- spanId = parsedSpanId;
7488
+ remove(binding) {
7489
+ binding.markDead();
7490
+ const stack = this.getStack();
7491
+ const pruned = stack.filter((b) => b !== binding && b.isLive());
7492
+ if (pruned.length !== stack.length) {
7493
+ this.setAmbientStack(pruned);
7596
7494
  }
7597
7495
  }
7598
- if (!traceId && !spanId && langsmithTrace) {
7599
- const segments = langsmithTrace.split(".");
7600
- traceId = (_a = segments[0]) == null ? void 0 : _a.split("Z").pop();
7601
- spanId = (_b = segments[segments.length - 1]) == null ? void 0 : _b.split("Z").pop();
7496
+ runWith(binding, callback) {
7497
+ this.maybeAdoptAsyncLocalStorage();
7498
+ if (this._als) {
7499
+ return this._als.run([...this.getStack(), binding], callback);
7500
+ }
7501
+ const previous = this._syncStack;
7502
+ this._syncStack = [...previous, binding];
7503
+ try {
7504
+ return callback();
7505
+ } finally {
7506
+ this._syncStack = previous;
7507
+ }
7602
7508
  }
7603
- traceId = fields[BAGGAGE_KEYS.traceId] || traceId;
7604
- spanId = fields[BAGGAGE_KEYS.dispatchSpanId] || spanId;
7605
- const eventId = fields[BAGGAGE_KEYS.eventId];
7606
- if (!traceId || !spanId || !eventId) return null;
7607
- if (!isW3CTraceId(traceId) || !isW3CSpanId(spanId)) {
7608
- warnNonConformantIdsOnce(traceId, spanId);
7509
+ current() {
7510
+ const stack = this.getStack();
7511
+ if (stack.length === 0) return void 0;
7512
+ const top = stack[stack.length - 1];
7513
+ if (top.isLive()) return top;
7514
+ const pruned = stack.filter((b) => b.isLive());
7515
+ this.setAmbientStack(pruned);
7516
+ return pruned.length > 0 ? pruned[pruned.length - 1] : void 0;
7609
7517
  }
7610
- const carrier = { traceId, spanId, eventId };
7611
- const childEventId = fields[BAGGAGE_KEYS.childEventId];
7612
- if (childEventId) carrier.childEventId = childEventId;
7613
- const name = fields[BAGGAGE_KEYS.name];
7614
- if (name) carrier.name = name;
7615
- const convoId = fields[BAGGAGE_KEYS.convoId];
7616
- if (convoId) carrier.convoId = convoId;
7617
- const userId = fields[BAGGAGE_KEYS.userId];
7618
- if (userId) carrier.userId = userId;
7619
- return carrier;
7620
- }
7621
- 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.`;
7622
- var UNRESOLVED_CARRIER_WARNED_KEY = /* @__PURE__ */ Symbol.for("raindrop.handoff.warnedUnresolvedCarrier");
7623
- function warnedHolder() {
7624
- return globalThis;
7518
+ warnNoAmbientBindOnce() {
7519
+ if (this._warnedNoAmbientBind) return;
7520
+ this._warnedNoAmbientBind = true;
7521
+ console.warn(this.noAmbientBindWarning);
7522
+ }
7523
+ };
7524
+ 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.";
7525
+ 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.";
7526
+ var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
7527
+ function routingStorage() {
7528
+ const holder = globalThis;
7529
+ let storage = holder[ROUTING_STORAGE_KEY];
7530
+ if (!storage) {
7531
+ storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
7532
+ holder[ROUTING_STORAGE_KEY] = storage;
7533
+ }
7534
+ return storage;
7625
7535
  }
7626
- function warnUnresolvedCarrier(headers, carrier) {
7627
- if (carrier) return;
7628
- if (!headers) return;
7629
- const holder = warnedHolder();
7630
- if (holder[UNRESOLVED_CARRIER_WARNED_KEY]) return;
7631
- holder[UNRESOLVED_CARRIER_WARNED_KEY] = true;
7632
- console.warn(UNRESOLVED_CARRIER_WARNING);
7536
+ function makeBoundContext(binding, owner) {
7537
+ const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
7538
+ const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
7539
+ let dead = false;
7540
+ return {
7541
+ projectId: binding.projectId,
7542
+ authHint: binding.authHint,
7543
+ ...binding.appGit ? { appGit: binding.appGit } : {},
7544
+ ...binding.appGitSource ? { appGitSource: binding.appGitSource } : {},
7545
+ isLive() {
7546
+ if (dead) return false;
7547
+ return ref === void 0 || ref.deref() !== void 0;
7548
+ },
7549
+ markDead() {
7550
+ dead = true;
7551
+ }
7552
+ };
7633
7553
  }
7634
- function safeParseStringRecord(raw) {
7554
+ function bindRoutingContext(binding, owner) {
7555
+ const bound = makeBoundContext(binding, owner);
7635
7556
  try {
7636
- const parsed = JSON.parse(raw);
7637
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
7638
- const result = {};
7639
- for (const [key, value] of Object.entries(parsed)) {
7640
- if (typeof value === "string") result[key] = value;
7641
- }
7642
- return result;
7557
+ routingStorage().push(bound);
7643
7558
  } catch (e) {
7644
- return {};
7645
7559
  }
7560
+ return bound;
7646
7561
  }
7647
- function runWithTracingSuppressed(fn) {
7648
- const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
7649
- if (typeof hook !== "function") return fn();
7650
- let started = false;
7562
+ function unbindRoutingContext(bound) {
7563
+ if (!bound || !isBoundContext(bound)) return;
7651
7564
  try {
7652
- return hook(() => {
7653
- started = true;
7654
- return fn();
7655
- });
7656
- } catch (err) {
7657
- if (started) throw err;
7658
- return fn();
7565
+ routingStorage().remove(bound);
7566
+ } catch (e) {
7659
7567
  }
7660
7568
  }
7661
- var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
7662
- var MAX_RETRY_DELAY_MS = 3e4;
7663
- function wait(ms) {
7664
- return new Promise((resolve) => setTimeout(resolve, ms));
7569
+ function withRoutingContext(binding, callback) {
7570
+ const bound = makeBoundContext(binding);
7571
+ return routingStorage().runWith(bound, callback);
7665
7572
  }
7666
- function formatEndpoint(endpoint) {
7667
- if (!endpoint) return void 0;
7668
- return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
7573
+ function currentRoutingBinding() {
7574
+ const bound = routingStorage().current();
7575
+ if (!bound) return void 0;
7576
+ return {
7577
+ projectId: bound.projectId,
7578
+ authHint: bound.authHint,
7579
+ ...bound.appGit ? { appGit: bound.appGit } : {},
7580
+ ...bound.appGitSource ? { appGitSource: bound.appGitSource } : {}
7581
+ };
7669
7582
  }
7670
- function redactUrlForLog(url) {
7671
- try {
7672
- const parsed = new URL(url);
7673
- parsed.username = "";
7674
- parsed.password = "";
7675
- parsed.search = "";
7676
- parsed.hash = "";
7677
- return parsed.toString();
7678
- } catch (e) {
7679
- return "<unparseable-url>";
7680
- }
7583
+ function isBoundContext(value) {
7584
+ return typeof value.isLive === "function";
7681
7585
  }
7682
- var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
7683
- var rateLimitedLogLast = /* @__PURE__ */ new Map();
7684
- function rateLimitedLog(key, log) {
7685
- const now = Date.now();
7686
- const last = rateLimitedLogLast.get(key);
7687
- if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
7688
- return false;
7586
+ var ATTRIBUTE_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.spanAttributeStorage");
7587
+ function attributeStorage() {
7588
+ const holder = globalThis;
7589
+ let storage = holder[ATTRIBUTE_STORAGE_KEY];
7590
+ if (!storage) {
7591
+ storage = new ContextStackStorage(NO_AMBIENT_ATTRIBUTE_BIND_WARNING);
7592
+ holder[ATTRIBUTE_STORAGE_KEY] = storage;
7689
7593
  }
7690
- rateLimitedLogLast.set(key, now);
7691
- log();
7692
- return true;
7594
+ return storage;
7693
7595
  }
7694
- async function raceWithTimeout(promise, timeoutMs) {
7695
- let timer;
7696
- const settledInTime = await Promise.race([
7697
- promise.then(
7698
- () => true,
7699
- () => true
7700
- ),
7701
- new Promise((resolve) => {
7702
- var _a;
7703
- timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
7704
- (_a = timer.unref) == null ? void 0 : _a.call(timer);
7705
- })
7706
- ]);
7707
- if (timer) clearTimeout(timer);
7708
- return settledInTime;
7596
+ function makeAttributeFrame(attributes, owner) {
7597
+ const snapshot = { ...attributes };
7598
+ const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
7599
+ const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
7600
+ const owned = /* @__PURE__ */ new WeakSet();
7601
+ let dead = false;
7602
+ let sawError = false;
7603
+ return {
7604
+ attributes: snapshot,
7605
+ sawErrorSpan: () => sawError,
7606
+ ownsSpan: (span) => owned.has(span),
7607
+ claimSpan(span) {
7608
+ owned.add(span);
7609
+ },
7610
+ markErrorSpanSeen() {
7611
+ sawError = true;
7612
+ },
7613
+ // A frame whose owner has been collected is dead even though nothing
7614
+ // unbound it: a run that never settles must not keep handing its reverse
7615
+ // reference to whatever runs next on a reused context.
7616
+ isLive() {
7617
+ if (dead) return false;
7618
+ return ref === void 0 || ref.deref() !== void 0;
7619
+ },
7620
+ markDead() {
7621
+ dead = true;
7622
+ }
7623
+ };
7709
7624
  }
7710
- function parseRetryAfter(headers) {
7711
- var _a;
7712
- const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
7713
- if (!value) return void 0;
7714
- const asNumber = Number(value);
7715
- if (value.trim() !== "" && !Number.isNaN(asNumber)) return asNumber * 1e3;
7716
- const asDate = new Date(value).getTime();
7717
- if (!Number.isNaN(asDate)) {
7718
- const delta = asDate - Date.now();
7719
- return delta > 0 ? delta : 0;
7625
+ function bindSpanAttributes(attributes, owner) {
7626
+ if (Object.keys(attributes).length === 0) return void 0;
7627
+ const frame = makeAttributeFrame(attributes, owner);
7628
+ try {
7629
+ attributeStorage().push(frame);
7630
+ } catch (e) {
7631
+ return void 0;
7720
7632
  }
7721
- return void 0;
7633
+ return frame;
7722
7634
  }
7723
- function getRetryDelayMs(attemptNumber, previousError) {
7724
- if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
7725
- const v = previousError.retryAfterMs;
7726
- if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
7635
+ function unbindSpanAttributes(frame) {
7636
+ if (!frame || !isAttributeFrame(frame)) return;
7637
+ try {
7638
+ attributeStorage().remove(frame);
7639
+ } catch (e) {
7727
7640
  }
7728
- if (attemptNumber <= 1) return 0;
7729
- const base = 500;
7730
- const factor = Math.pow(2, attemptNumber - 2);
7731
- return Math.min(base * factor, MAX_RETRY_DELAY_MS);
7732
7641
  }
7733
- async function withRetry(operation, opName, opts) {
7734
- const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
7735
- let lastError = void 0;
7736
- for (let attemptNumber = 1; attemptNumber <= opts.maxAttempts; attemptNumber++) {
7737
- if (attemptNumber > 1) {
7738
- const delay = getRetryDelayMs(attemptNumber, lastError);
7739
- if (opts.debug) {
7740
- console.warn(
7741
- `${prefix} ${opName} retry ${attemptNumber}/${opts.maxAttempts} in ${delay}ms`
7742
- );
7743
- }
7744
- if (delay > 0) await wait(delay);
7745
- } else if (opts.debug) {
7746
- console.log(`${prefix} ${opName} attempt ${attemptNumber}/${opts.maxAttempts}`);
7747
- }
7748
- try {
7749
- return await operation();
7750
- } catch (err) {
7751
- lastError = err;
7752
- if (opts.debug) {
7753
- const msg = err instanceof Error ? err.message : String(err);
7754
- console.warn(
7755
- `${prefix} ${opName} attempt ${attemptNumber} failed: ${msg}${attemptNumber === opts.maxAttempts ? " (no more retries)" : ""}`
7756
- );
7642
+ function applySpanAttributeFrames(span, setAttribute) {
7643
+ try {
7644
+ for (const frame of attributeStorage().getStack()) {
7645
+ if (!frame.isLive()) continue;
7646
+ frame.claimSpan(span);
7647
+ for (const [key, value] of Object.entries(frame.attributes)) {
7648
+ setAttribute(key, value);
7757
7649
  }
7758
- if (lastError && typeof lastError === "object" && "retryable" in lastError && !lastError.retryable)
7759
- break;
7760
- if (attemptNumber === opts.maxAttempts) break;
7761
7650
  }
7651
+ } catch (e) {
7762
7652
  }
7763
- throw lastError instanceof Error ? lastError : new Error(String(lastError));
7764
- }
7765
- async function postJson(url, body, headers, opts) {
7766
- var _a;
7767
- const opName = `POST ${redactUrlForLog(url)}`;
7768
- const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
7769
- await withRetry(
7770
- async () => {
7771
- const resp = await runWithTracingSuppressed(
7772
- () => fetch(url, {
7773
- method: "POST",
7774
- redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
7775
- headers: {
7776
- "Content-Type": "application/json",
7777
- ...headers
7778
- },
7779
- body: JSON.stringify(body),
7780
- signal: AbortSignal.timeout(timeoutMs)
7781
- })
7782
- );
7783
- if (!resp.ok) {
7784
- const text = await resp.text().catch(() => "");
7785
- const err = new Error(
7786
- `HTTP ${resp.status} ${resp.statusText}${text ? `: ${text}` : ""}`
7787
- );
7788
- const retryAfterMs = parseRetryAfter(resp.headers);
7789
- if (typeof retryAfterMs === "number") err.retryAfterMs = retryAfterMs;
7790
- err.retryable = resp.status === 429 || resp.status >= 500;
7791
- throw err;
7792
- }
7793
- },
7794
- opName,
7795
- opts
7796
- );
7797
7653
  }
7798
- var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
7799
- var TRUNCATION_MARKER = "...[truncated by raindrop]";
7800
- var BOUNDED_CLONE_MAX_DEPTH = 12;
7801
- var BOUNDED_CLONE_SLACK = 256;
7802
- var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
7803
- function setDefaultMaxTextFieldChars(value) {
7804
- if (typeof value === "number" && Number.isFinite(value) && value > 0) {
7805
- currentDefaultMaxTextFieldChars = Math.floor(value);
7654
+ function claimSpanForFrames(span) {
7655
+ try {
7656
+ for (const frame of attributeStorage().getStack()) {
7657
+ if (frame.isLive()) frame.claimSpan(span);
7658
+ }
7659
+ } catch (e) {
7806
7660
  }
7807
7661
  }
7808
- function resolveMaxTextFieldChars(value) {
7809
- if (typeof value === "number" && Number.isFinite(value) && value > 0) {
7810
- return Math.floor(value);
7662
+ function currentSpanAttributes() {
7663
+ const merged = {};
7664
+ try {
7665
+ for (const frame of attributeStorage().getStack()) {
7666
+ if (!frame.isLive()) continue;
7667
+ Object.assign(merged, frame.attributes);
7668
+ }
7669
+ } catch (e) {
7811
7670
  }
7812
- return currentDefaultMaxTextFieldChars;
7671
+ return merged;
7813
7672
  }
7814
- function truncateToLimit(text, limit) {
7815
- if (limit > TRUNCATION_MARKER.length) {
7816
- return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
7673
+ function markErrorSpanSeen(span) {
7674
+ try {
7675
+ for (const frame of attributeStorage().getStack()) {
7676
+ if (frame.ownsSpan(span)) frame.markErrorSpanSeen();
7677
+ }
7678
+ } catch (e) {
7817
7679
  }
7818
- return text.slice(0, Math.max(0, limit));
7819
7680
  }
7820
- function capText(value, limit) {
7821
- if (typeof value !== "string") return value;
7822
- const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
7823
- if (value.length <= max) return value;
7824
- return truncateToLimit(value, max);
7681
+ function isAttributeFrame(value) {
7682
+ return typeof value.isLive === "function";
7825
7683
  }
7826
- function boundedClone(obj, charBudget) {
7827
- let budget = charBudget;
7828
- const seen = /* @__PURE__ */ new WeakSet();
7829
- const walk = (node, depth) => {
7830
- if (budget <= 0) return TRUNCATION_MARKER;
7831
- if (typeof node === "string") {
7832
- if (node.length > budget) {
7833
- const taken = node.slice(0, Math.max(0, budget)) + TRUNCATION_MARKER;
7834
- budget = 0;
7835
- return taken;
7684
+ var APP_COMMIT_SHA = "raindrop.app.commit_sha";
7685
+ var APP_COMMIT_DIRTY = "raindrop.app.commit_dirty";
7686
+ var APP_BRANCH = "raindrop.app.branch";
7687
+ var APP_GIT_KEYS = [APP_COMMIT_SHA, APP_COMMIT_DIRTY, APP_BRANCH];
7688
+ var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
7689
+ function appGitExplicitKeys(properties) {
7690
+ const keys = /* @__PURE__ */ new Set();
7691
+ if (!properties) return keys;
7692
+ for (const key of APP_GIT_KEYS) {
7693
+ if (own(properties, key)) keys.add(key);
7694
+ }
7695
+ return keys;
7696
+ }
7697
+ function mergeAppGitSnapshot(base, properties, explicitKeys = /* @__PURE__ */ new Set()) {
7698
+ try {
7699
+ const result = { ...base };
7700
+ if (properties && own(properties, APP_COMMIT_SHA) && properties[APP_COMMIT_SHA] !== result[APP_COMMIT_SHA]) {
7701
+ if (!explicitKeys.has(APP_COMMIT_DIRTY) && !own(properties, APP_COMMIT_DIRTY)) {
7702
+ delete result[APP_COMMIT_DIRTY];
7836
7703
  }
7837
- budget -= Math.max(node.length, 1);
7838
- return node;
7839
- }
7840
- if (node === null || typeof node === "number" || typeof node === "boolean") {
7841
- budget -= 8;
7842
- return node;
7843
- }
7844
- if (typeof node !== "object") {
7845
- budget -= 8;
7846
- return node;
7847
- }
7848
- if (seen.has(node)) return "[CIRCULAR]";
7849
- if (depth >= BOUNDED_CLONE_MAX_DEPTH) {
7850
- budget -= 16;
7851
- return `<max depth>`;
7852
- }
7853
- seen.add(node);
7854
- if (Array.isArray(node)) {
7855
- const out2 = [];
7856
- for (const v of node) {
7857
- if (budget <= 0) {
7858
- out2.push(TRUNCATION_MARKER);
7859
- break;
7860
- }
7861
- out2.push(walk(v, depth + 1));
7704
+ if (!explicitKeys.has(APP_BRANCH) && !own(properties, APP_BRANCH)) {
7705
+ delete result[APP_BRANCH];
7862
7706
  }
7863
- return out2;
7864
7707
  }
7865
- if (typeof node.toJSON === "function") {
7866
- budget -= 16;
7867
- return node;
7868
- }
7869
- const out = {};
7870
- for (const k in node) {
7871
- if (!Object.prototype.hasOwnProperty.call(node, k)) continue;
7872
- if (budget <= 0) {
7873
- out["..."] = TRUNCATION_MARKER;
7874
- break;
7875
- }
7876
- const key = walk(k, depth + 1);
7877
- out[key] = walk(node[k], depth + 1);
7708
+ for (const key of APP_GIT_KEYS) {
7709
+ if (properties && own(properties, key)) result[key] = properties[key];
7878
7710
  }
7879
- return out;
7880
- };
7881
- return walk(obj, 0);
7882
- }
7883
- function stringifyBounded(value, limit) {
7884
- const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
7885
- if (typeof value === "string") {
7886
- return capText(value, max);
7887
- }
7888
- const pruned = boundedClone(value, max + TRUNCATION_MARKER.length + BOUNDED_CLONE_SLACK);
7889
- let json;
7890
- try {
7891
- json = JSON.stringify(pruned);
7711
+ return Object.freeze(result);
7892
7712
  } catch (e) {
7893
- json = void 0;
7894
- }
7895
- if (json === void 0) {
7896
- return capText(String(value), max);
7713
+ return Object.freeze({});
7897
7714
  }
7898
- return json.length <= max ? json : truncateToLimit(json, max);
7899
7715
  }
7900
- function attrString(key, value) {
7901
- if (value === void 0) return void 0;
7902
- return { key, value: { stringValue: value } };
7716
+ function validAppCommitSha(value) {
7717
+ if (typeof value !== "string" || value.length > 128) return void 0;
7718
+ const cleaned = value.trim();
7719
+ return /^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/.test(cleaned) ? cleaned.toLowerCase() : void 0;
7903
7720
  }
7904
- function attrInt(key, value) {
7905
- if (value === void 0) return void 0;
7906
- if (!Number.isFinite(value)) return void 0;
7907
- return { key, value: { intValue: String(Math.trunc(value)) } };
7721
+ function shortString(value) {
7722
+ if (typeof value !== "string" || value.length > 1024) return void 0;
7723
+ const cleaned = value.trim();
7724
+ return cleaned.length > 0 ? cleaned : void 0;
7908
7725
  }
7909
- function attrDouble(key, value) {
7910
- if (value === void 0) return void 0;
7911
- if (!Number.isFinite(value)) return void 0;
7912
- return { key, value: { doubleValue: value } };
7726
+ function flag(value) {
7727
+ if (value === true || value === "true" || value === "1") return true;
7728
+ if (value === false || value === "false" || value === "0") return false;
7729
+ return void 0;
7913
7730
  }
7914
- var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
7915
- function state() {
7916
- const holder = globalThis;
7917
- let current = holder[STATE_KEY];
7918
- if (!current) {
7919
- let storage = null;
7731
+ function environment() {
7732
+ try {
7733
+ return typeof process === "undefined" ? {} : process.env;
7734
+ } catch (e) {
7735
+ return {};
7736
+ }
7737
+ }
7738
+ var DEPLOYMENTS = [
7739
+ ["VERCEL", "VERCEL_GIT_COMMIT_SHA", "VERCEL_GIT_COMMIT_REF"],
7740
+ ["NETLIFY", "COMMIT_REF", "BRANCH"],
7741
+ ["RAILWAY_ENVIRONMENT_NAME", "RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH"],
7742
+ ["RAILWAY_ENVIRONMENT", "RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH"],
7743
+ ["RENDER", "RENDER_GIT_COMMIT", "RENDER_GIT_BRANCH"],
7744
+ ["DYNO", "HEROKU_BUILD_COMMIT", ""],
7745
+ ["DYNO", "HEROKU_SLUG_COMMIT", ""],
7746
+ ["AWS_APP_ID", "AWS_COMMIT_ID", "AWS_BRANCH"],
7747
+ ["WORKERS_CI", "WORKERS_CI_COMMIT_SHA", "WORKERS_CI_BRANCH"]
7748
+ ];
7749
+ var CI_SOURCES = [
7750
+ ["GITHUB_ACTIONS", "GITHUB_SHA", "GITHUB_REF_NAME"],
7751
+ ["GITLAB_CI", "CI_COMMIT_SHA", "CI_COMMIT_REF_NAME"],
7752
+ ["CIRCLECI", "CIRCLE_SHA1", "CIRCLE_BRANCH"]
7753
+ ];
7754
+ function detectedSource(env, table, detectBranch) {
7755
+ for (const row of table) {
7756
+ const [marker, shaKey, branchKey] = row;
7757
+ if (!marker || !shaKey || !shortString(env[marker]) || flag(env[marker]) === false) continue;
7758
+ const commitSha = validAppCommitSha(env[shaKey]);
7759
+ if (commitSha)
7760
+ return {
7761
+ commitSha,
7762
+ ...detectBranch && branchKey ? { branch: shortString(env[branchKey]) } : {}
7763
+ };
7764
+ }
7765
+ return {};
7766
+ }
7767
+ var AppGitContextImpl = class {
7768
+ constructor(options, runtime = {}) {
7769
+ this.defaults = {};
7770
+ this.explicit = {};
7771
+ this.explicitKeys = /* @__PURE__ */ new Set();
7772
+ this.disposed = false;
7773
+ this.operations = /* @__PURE__ */ new Map();
7774
+ this.operationOverflow = false;
7775
+ this.detectBranch = false;
7776
+ this.disabled = false;
7777
+ var _a, _b, _c, _d, _e, _f;
7920
7778
  try {
7921
- const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
7922
- if (Ctor) storage = new Ctor();
7779
+ if (options === false) {
7780
+ this.disabled = true;
7781
+ return;
7782
+ }
7783
+ const opts = options != null ? options : {};
7784
+ const env = (_a = runtime.env) != null ? _a : environment();
7785
+ const detectBranch = (_c = (_b = flag(opts.detectBranch)) != null ? _b : flag(env.RAINDROP_GIT_DETECT_BRANCH)) != null ? _c : false;
7786
+ this.detectBranch = detectBranch;
7787
+ const collectBranch = detectBranch || runtime.captureLegacyReplayBranch === true;
7788
+ const autoDetect = runtime.allowDiscovery === false ? false : (_e = (_d = flag(opts.autoDetect)) != null ? _d : flag(env.RAINDROP_GIT_AUTO_DETECT)) != null ? _e : true;
7789
+ const configuredSha = opts.commitSha;
7790
+ const explicitSha = configuredSha === void 0 ? env.RAINDROP_COMMIT_SHA : configuredSha;
7791
+ if (explicitSha === null) this.explicit[APP_COMMIT_SHA] = null;
7792
+ else if (typeof explicitSha === "string" && validAppCommitSha(explicitSha))
7793
+ this.explicit[APP_COMMIT_SHA] = explicitSha;
7794
+ const configuredDirty = opts.commitDirty;
7795
+ const explicitDirty = configuredDirty === void 0 ? flag(env.RAINDROP_COMMIT_DIRTY) : configuredDirty;
7796
+ if (explicitDirty === null || typeof explicitDirty === "boolean")
7797
+ this.explicit[APP_COMMIT_DIRTY] = explicitDirty;
7798
+ const configuredBranch = opts.branch;
7799
+ const explicitBranch = configuredBranch === void 0 ? env.RAINDROP_BRANCH : configuredBranch;
7800
+ if (explicitBranch === null) this.explicit[APP_BRANCH] = null;
7801
+ else if (typeof explicitBranch === "string" && explicitBranch.length <= 1024)
7802
+ this.explicit[APP_BRANCH] = explicitBranch;
7803
+ this.explicitKeys = appGitExplicitKeys(this.explicit);
7804
+ this.defaults = { ...this.explicit };
7805
+ if (!autoDetect || explicitSha !== void 0) return;
7806
+ const sourceDirectory = (_f = shortString(opts.sourceDirectory)) != null ? _f : shortString(env.RAINDROP_GIT_SOURCE_DIRECTORY);
7807
+ if (runtime.allowLocalGit === false && !sourceDirectory) return;
7808
+ const deploy = sourceDirectory ? {} : detectedSource(env, DEPLOYMENTS, collectBranch);
7809
+ if (deploy.commitSha) {
7810
+ this.accept(deploy);
7811
+ return;
7812
+ }
7813
+ const fallback = sourceDirectory ? {} : detectedSource(env, CI_SOURCES, collectBranch);
7814
+ if (runtime.resolveLocal && (runtime.allowLocalGit !== false || sourceDirectory)) {
7815
+ this.cancel = runtime.resolveLocal(sourceDirectory, collectBranch, (result) => {
7816
+ try {
7817
+ if (!this.disposed) this.accept(result.commitSha ? result : fallback);
7818
+ } catch (e) {
7819
+ }
7820
+ });
7821
+ } else this.accept(fallback);
7923
7822
  } catch (e) {
7924
- storage = null;
7925
7823
  }
7926
- current = {
7927
- storage,
7928
- syncDestination: void 0,
7929
- spanDestinations: /* @__PURE__ */ new WeakMap()
7824
+ }
7825
+ accept(result) {
7826
+ const sha = validAppCommitSha(result.commitSha);
7827
+ if (!sha) return;
7828
+ this.replayBranch = shortString(result.branch);
7829
+ this.defaults = {
7830
+ [APP_COMMIT_SHA]: sha,
7831
+ ...typeof result.commitDirty === "boolean" ? { [APP_COMMIT_DIRTY]: result.commitDirty } : {},
7832
+ ...this.detectBranch && this.replayBranch ? { [APP_BRANCH]: this.replayBranch } : {},
7833
+ ...this.explicit
7930
7834
  };
7931
- holder[STATE_KEY] = current;
7932
7835
  }
7933
- return current;
7934
- }
7935
- function withReplayTraceDestination(destination, callback) {
7936
- const routing = state();
7937
- if (routing.storage) return routing.storage.run(destination, callback);
7938
- const previous = routing.syncDestination;
7939
- routing.syncDestination = destination;
7940
- try {
7941
- return callback();
7942
- } finally {
7943
- routing.syncDestination = previous;
7836
+ snapshot(properties) {
7837
+ var _a;
7838
+ try {
7839
+ const binding = currentRoutingBinding();
7840
+ const base = this.disabled && (binding == null ? void 0 : binding.appGitSource) !== "replay-override" ? {} : (_a = binding == null ? void 0 : binding.appGit) != null ? _a : this.defaults;
7841
+ return this.mergeSnapshot(base, properties);
7842
+ } catch (e) {
7843
+ return Object.freeze({});
7844
+ }
7944
7845
  }
7945
- }
7946
- function currentReplayTraceDestination() {
7947
- var _a, _b;
7948
- const routing = state();
7949
- return (_b = (_a = routing.storage) == null ? void 0 : _a.getStore()) != null ? _b : routing.syncDestination;
7950
- }
7951
- function rememberReplayTraceDestination(span) {
7952
- const destination = currentReplayTraceDestination();
7953
- if (destination) state().spanDestinations.set(span, destination);
7954
- }
7955
- function copyReplayTraceDestination(source, target) {
7956
- const destination = state().spanDestinations.get(source);
7957
- if (destination) state().spanDestinations.set(target, destination);
7958
- }
7959
- function replayTraceDestinationFor(value) {
7960
- if (typeof value !== "object" && typeof value !== "function" || value === null) {
7961
- return void 0;
7846
+ /** Safe defaults when no operation/span start was observed; never infer identity. */
7847
+ snapshotExplicit(properties, eventId) {
7848
+ try {
7849
+ const base = { ...this.explicit };
7850
+ const operation = eventId ? this.operations.get(eventId) : void 0;
7851
+ if (operation) {
7852
+ for (const key of operation.explicitKeys) base[key] = operation.snapshot[key];
7853
+ }
7854
+ return this.mergeSnapshot(base, properties, new Set(Object.keys(base)));
7855
+ } catch (e) {
7856
+ return mergeAppGitSnapshot({}, properties);
7857
+ }
7858
+ }
7859
+ /** Existing replay branch reporting stays separate from opt-in event properties. */
7860
+ snapshotForReplay() {
7861
+ const snapshot = this.snapshot();
7862
+ if (this.replayBranch && !own(snapshot, APP_BRANCH) && snapshot[APP_COMMIT_SHA] === this.defaults[APP_COMMIT_SHA]) {
7863
+ return Object.freeze({ ...snapshot, [APP_BRANCH]: this.replayBranch });
7864
+ }
7865
+ return snapshot;
7866
+ }
7867
+ /** Merge a frozen span snapshot without losing client-authored provenance. */
7868
+ mergeSnapshot(base, properties, explicitKeys = this.explicitKeys) {
7869
+ return mergeAppGitSnapshot(base, properties, /* @__PURE__ */ new Set([...this.explicitKeys, ...explicitKeys]));
7870
+ }
7871
+ /** Shippers belonging to one client share this bounded operation registry. */
7872
+ forOperation(eventId, properties, owner) {
7873
+ if (!eventId) return this.snapshot(properties);
7874
+ let operation = this.operations.get(eventId);
7875
+ if (!operation) {
7876
+ if (this.operations.size >= 1e4) this.operationOverflow = true;
7877
+ if (this.operationOverflow) return this.mergeSnapshot({}, properties);
7878
+ operation = {
7879
+ snapshot: this.snapshot(properties),
7880
+ owners: /* @__PURE__ */ new Set(),
7881
+ unowned: owner === void 0,
7882
+ explicitKeys: appGitExplicitKeys(properties)
7883
+ };
7884
+ this.operations.set(eventId, operation);
7885
+ } else if (properties && APP_GIT_KEYS.some((key) => own(properties, key))) {
7886
+ const explicitKeys = /* @__PURE__ */ new Set([...this.explicitKeys, ...operation.explicitKeys]);
7887
+ operation.snapshot = this.mergeSnapshot(operation.snapshot, properties, explicitKeys);
7888
+ for (const key of appGitExplicitKeys(properties)) operation.explicitKeys.add(key);
7889
+ }
7890
+ if (!owner) operation.unowned = true;
7891
+ if (owner) operation.owners.add(owner);
7892
+ return operation.snapshot;
7893
+ }
7894
+ releaseOperation(eventId, owner) {
7895
+ const operation = this.operations.get(eventId);
7896
+ if (!operation) return;
7897
+ operation.owners.delete(owner);
7898
+ if (operation.owners.size === 0 && !operation.unowned) this.operations.delete(eventId);
7899
+ }
7900
+ finishOperation(eventId) {
7901
+ this.operations.delete(eventId);
7902
+ }
7903
+ dispose() {
7904
+ var _a;
7905
+ this.disposed = true;
7906
+ try {
7907
+ (_a = this.cancel) == null ? void 0 : _a.call(this);
7908
+ } catch (e) {
7909
+ }
7962
7910
  }
7963
- return state().spanDestinations.get(value);
7964
- }
7965
- function recordReplayExportedSpan(destination, spanId, traceId) {
7966
- var _a, _b;
7967
- if (!destination || !spanId || !traceId) return;
7968
- (_a = destination.exportedSpanIds) == null ? void 0 : _a.add(spanId);
7969
- (_b = destination.exportedTraceIds) == null ? void 0 : _b.add(traceId);
7970
- }
7971
- function replayTraceUrl(baseUrl) {
7972
- return `${baseUrl.replace(/\/+$/, "")}/v1/traces`;
7973
- }
7974
- var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
7975
- var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
7976
- var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
7977
- function readEnvVar(name) {
7978
- var _a;
7911
+ };
7912
+ var AppGitContext = AppGitContextImpl;
7913
+ function withAppGitProperties(properties, snapshot) {
7979
7914
  try {
7980
- const env = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env;
7981
- if (env && typeof env[name] === "string" && env[name].length > 0) {
7982
- return env[name];
7983
- }
7915
+ const merged = { ...properties };
7916
+ for (const key of APP_GIT_KEYS)
7917
+ if (!own(merged, key) && own(snapshot, key)) merged[key] = snapshot[key];
7918
+ return merged;
7984
7919
  } catch (e) {
7920
+ return properties != null ? properties : {};
7985
7921
  }
7986
- return void 0;
7987
- }
7988
- function readWorkshopEnv() {
7989
- const raw = readEnvVar(WORKSHOP_ENV_VAR);
7990
- if (raw === void 0) return void 0;
7991
- const trimmed = raw.trim();
7992
- if (trimmed.length === 0) return void 0;
7993
- if (/^https?:\/\//i.test(trimmed)) return { url: trimmed };
7994
- if (/^(1|true|yes|on)$/i.test(trimmed)) return "enable";
7995
- if (/^(0|false|no|off)$/i.test(trimmed)) return "disable";
7996
- return void 0;
7997
- }
7998
- function isLocalDevHost(hostname) {
7999
- if (!hostname) return false;
8000
- if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1") {
8001
- return true;
8002
- }
8003
- if (hostname.endsWith(".localhost")) return true;
8004
- return false;
8005
7922
  }
8006
- function readRuntimeHostname() {
7923
+ function withAppGitAttributes(attributes, snapshot) {
8007
7924
  try {
8008
- const loc = globalThis == null ? void 0 : globalThis.location;
8009
- if (loc && typeof loc.hostname === "string" && loc.hostname.length > 0) {
8010
- return loc.hostname;
7925
+ const present = new Set(attributes.flatMap((attr) => attr ? [attr.key] : []));
7926
+ const added = [];
7927
+ for (const key of APP_GIT_KEYS) {
7928
+ if (present.has(key) || present.has(`ai.telemetry.metadata.${key}`) || present.has(`traceloop.association.properties.${key}`))
7929
+ continue;
7930
+ const value = snapshot[key];
7931
+ if (typeof value === "string") added.push({ key, value: { stringValue: value } });
7932
+ else if (typeof value === "boolean") added.push({ key, value: { boolValue: value } });
8011
7933
  }
7934
+ return added.length ? [...attributes, ...added] : attributes;
8012
7935
  } catch (e) {
7936
+ return attributes;
8013
7937
  }
8014
- return void 0;
8015
7938
  }
8016
- function shouldAutoEnableLocalWorkshop() {
8017
- if (isLocalDevHost(readRuntimeHostname())) return true;
8018
- if (readEnvVar("NODE_ENV") === "development") return true;
8019
- return false;
7939
+ function normalizeFeatureFlags(input) {
7940
+ if (Array.isArray(input)) {
7941
+ return Object.fromEntries(input.map((name) => [name, "true"]));
7942
+ }
7943
+ return Object.fromEntries(
7944
+ Object.entries(input).map(([k, v]) => [k, String(v)])
7945
+ );
8020
7946
  }
8021
- function resolveLocalDebuggerBaseUrl(baseUrl) {
8022
- var _a, _b, _c;
8023
- if (baseUrl === null) return null;
8024
- if (typeof baseUrl === "string" && baseUrl.length > 0) {
8025
- return (_a = formatEndpoint(baseUrl)) != null ? _a : null;
7947
+ function getCrypto() {
7948
+ const c = globalThis.crypto;
7949
+ return c;
7950
+ }
7951
+ function randomBytes(length) {
7952
+ const cryptoObj = getCrypto();
7953
+ const out = new Uint8Array(length);
7954
+ if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
7955
+ cryptoObj.getRandomValues(out);
7956
+ return out;
8026
7957
  }
8027
- const explicitUrlEnv = readEnvVar(LOCAL_DEBUGGER_ENV_VAR);
8028
- if (explicitUrlEnv) return (_b = formatEndpoint(explicitUrlEnv)) != null ? _b : null;
8029
- const workshopEnv = readWorkshopEnv();
8030
- if (workshopEnv === "disable") return null;
8031
- if (workshopEnv === "enable") return DEFAULT_LOCAL_WORKSHOP_URL;
8032
- if (workshopEnv && "url" in workshopEnv) return (_c = formatEndpoint(workshopEnv.url)) != null ? _c : null;
8033
- if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
8034
- return null;
7958
+ for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
7959
+ return out;
8035
7960
  }
8036
- function localDebuggerEnabled(baseUrl) {
8037
- return resolveLocalDebuggerBaseUrl(baseUrl) !== null;
7961
+ function randomUUID() {
7962
+ const cryptoObj = getCrypto();
7963
+ if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
7964
+ return cryptoObj.randomUUID();
7965
+ }
7966
+ const b = randomBytes(16);
7967
+ b[6] = b[6] & 15 | 64;
7968
+ b[8] = b[8] & 63 | 128;
7969
+ const hex = [...b].map((x) => x.toString(16).padStart(2, "0")).join("");
7970
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
8038
7971
  }
8039
- var LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS = 2e3;
8040
- function mirrorTraceExportToLocalDebugger(body, options = {}) {
8041
- var _a;
8042
- const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
8043
- if (!baseUrl) return Promise.resolve();
8044
- return postJson(`${baseUrl}traces`, body, {}, {
8045
- maxAttempts: 1,
8046
- debug: (_a = options.debug) != null ? _a : false,
8047
- sdkName: options.sdkName,
8048
- timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
8049
- }).catch(() => {
8050
- });
7972
+ var HANDOFF_ATTRIBUTE_SUFFIXES = {
7973
+ mode: "raindrop.handoff.mode",
7974
+ childEventId: "raindrop.handoff.childEventId",
7975
+ parentEventId: "raindrop.handoff.parentEventId",
7976
+ parentSpanId: "raindrop.handoff.parentSpanId",
7977
+ name: "raindrop.handoff.name",
7978
+ terminal: "raindrop.handoff.terminal",
7979
+ agentRole: "raindrop.agent.role"
7980
+ };
7981
+ var AI_SDK_METADATA_PREFIX = "ai.telemetry.metadata.";
7982
+ var DETACHED_MODE = "detached";
7983
+ var SUBAGENT_AGENT_ROLE = "subagent";
7984
+ var HANDOFF_TERMINAL_CANCELLED = "cancelled";
7985
+ var TOOL_EVENTS_SUFFIX = "raindrop.toolEvents";
7986
+ var TOOL_EVENTS_ALLOW = "allow";
7987
+ var OPERATION_ID_ATTRIBUTE = "ai.operationId";
7988
+ var SUBAGENT_ABORT_OPERATION_ID = "ai.subagent.failed";
7989
+ var SUBAGENT_ABORT_SPAN_NAME = SUBAGENT_ABORT_OPERATION_ID;
7990
+ var DEFAULT_DISPATCH_SPAN_NAME = "launch_subagent";
7991
+ var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
7992
+ function defined(entries) {
7993
+ const out = {};
7994
+ for (const [key, value] of Object.entries(entries)) {
7995
+ if (typeof value === "string" && value.trim().length > 0) out[key] = value;
7996
+ }
7997
+ return out;
8051
7998
  }
8052
- function mirrorPartialEventToLocalDebugger(event, options = {}) {
8053
- var _a;
8054
- const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
8055
- if (!baseUrl) return Promise.resolve();
8056
- const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
8057
- return postJson(`${baseUrl}events/track_partial`, event, headers, {
8058
- maxAttempts: 1,
8059
- debug: (_a = options.debug) != null ? _a : false,
8060
- sdkName: options.sdkName,
8061
- timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
8062
- }).catch(() => {
7999
+ function detachedDispatchMetadata(dispatch) {
8000
+ return defined({
8001
+ [HANDOFF_ATTRIBUTE_SUFFIXES.mode]: DETACHED_MODE,
8002
+ [HANDOFF_ATTRIBUTE_SUFFIXES.childEventId]: dispatch.childEventId,
8003
+ [HANDOFF_ATTRIBUTE_SUFFIXES.name]: dispatch.name
8063
8004
  });
8064
8005
  }
8065
- function sendLocalDebuggerLiveEvent(event, options = {}) {
8066
- var _a, _b;
8067
- const destination = currentReplayTraceDestination();
8068
- if (destination && destination.kind !== "workshop") return;
8069
- const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
8070
- if (!baseUrl) return;
8071
- void postJson(
8072
- `${baseUrl}live`,
8073
- {
8074
- ...event,
8075
- type: event.type,
8076
- timestamp: (_a = event.timestamp) != null ? _a : Date.now()
8077
- },
8078
- {},
8079
- {
8080
- maxAttempts: 1,
8081
- debug: (_b = options.debug) != null ? _b : false,
8082
- sdkName: options.sdkName
8083
- }
8084
- ).catch(() => {
8006
+ function detachedChildMetadata(link) {
8007
+ return defined({
8008
+ [HANDOFF_ATTRIBUTE_SUFFIXES.parentEventId]: link.parentEventId,
8009
+ [HANDOFF_ATTRIBUTE_SUFFIXES.parentSpanId]: link.parentSpanId,
8010
+ [HANDOFF_ATTRIBUTE_SUFFIXES.name]: link.name,
8011
+ [HANDOFF_ATTRIBUTE_SUFFIXES.agentRole]: SUBAGENT_AGENT_ROLE
8085
8012
  });
8086
8013
  }
8087
- var PROJECT_ID_HEADER = "X-Raindrop-Project-Id";
8088
- var PROJECT_ID_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
8089
- function isValidProjectIdSlug(value) {
8090
- return PROJECT_ID_SLUG_PATTERN.test(value);
8014
+ function cancelledTerminalMetadata() {
8015
+ return { [HANDOFF_ATTRIBUTE_SUFFIXES.terminal]: HANDOFF_TERMINAL_CANCELLED };
8091
8016
  }
8092
- function normalizeProjectId(raw, opts) {
8093
- if (typeof raw !== "string") return void 0;
8094
- const trimmed = raw.trim();
8095
- if (!trimmed) return void 0;
8096
- if (!isValidProjectIdSlug(trimmed) && opts.debug) {
8097
- console.warn(
8098
- `${opts.prefix} projectId "${trimmed}" does not match slug ${PROJECT_ID_SLUG_PATTERN.source}; sending anyway \u2014 backend may reject with HTTP 400`
8099
- );
8100
- }
8101
- return trimmed;
8017
+ function cancelledTerminalAttributes() {
8018
+ const bare = cancelledTerminalMetadata();
8019
+ return { ...bare, ...withMetadataPrefix(bare) };
8102
8020
  }
8103
- function projectIdHeaders(projectId) {
8104
- return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
8021
+ function toolEventsAllowMetadata() {
8022
+ return { [TOOL_EVENTS_SUFFIX]: TOOL_EVENTS_ALLOW };
8105
8023
  }
8106
- var SHUTDOWN_DEADLINE_MS = 1e4;
8107
- var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
8108
- var MODEL_USAGE_ATTRIBUTES = {
8109
- usageSource: "raindrop.usage.source",
8110
- providerName: "gen_ai.provider.name",
8111
- requestModel: "gen_ai.request.model",
8112
- responseModel: "gen_ai.response.model",
8113
- inputTokens: "gen_ai.usage.input_tokens",
8114
- outputTokens: "gen_ai.usage.output_tokens",
8115
- reasoningTokens: "gen_ai.usage.reasoning_tokens",
8116
- cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
8117
- cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
8118
- providerReportedCost: "gen_ai.usage.provider_reported_cost",
8119
- nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
8120
- nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
8121
- totalOutputTokens: "raindrop.usage.output_tokens.total",
8122
- reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
8123
- cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
8124
- cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
8024
+ function withMetadataPrefix(metadata) {
8025
+ const out = {};
8026
+ for (const [key, value] of Object.entries(metadata)) {
8027
+ out[key.startsWith(AI_SDK_METADATA_PREFIX) ? key : `${AI_SDK_METADATA_PREFIX}${key}`] = value;
8028
+ }
8029
+ return out;
8030
+ }
8031
+ var BAGGAGE_KEYS = {
8032
+ eventId: "raindrop-event-id",
8033
+ childEventId: "raindrop-child-event-id",
8034
+ name: "raindrop-handoff-name",
8035
+ convoId: "raindrop-convo-id",
8036
+ userId: "raindrop-user-id",
8037
+ // The dispatch span and its trace, carried as fields as well as in
8038
+ // `traceparent`. Not redundant: OTel's HTTP instrumentations rewrite
8039
+ // `traceparent` from whatever span is current as the request goes out, so by
8040
+ // the time the child reads it the ids name the HTTP client span and its trace
8041
+ // instead of the dispatch. The child would then point its reverse reference at
8042
+ // a span the launcher's UI does not know as a dispatch. A field states which
8043
+ // span it means rather than meaning "whoever wrote this header last".
8044
+ dispatchSpanId: "raindrop-dispatch-span-id",
8045
+ traceId: "raindrop-trace-id"
8125
8046
  };
8126
- var MODEL_PROVIDER_NAMES = [
8127
- ["google.vertex.anthropic", "google-vertex"],
8128
- ["google.vertex_ai", "google-vertex"],
8129
- ["google.vertex", "google-vertex"],
8130
- ["gcp.vertex_ai", "google-vertex"],
8131
- ["gcp.gemini", "google"],
8132
- ["google_vertexai", "google-vertex"],
8133
- ["vertex.anthropic", "google-vertex"],
8134
- ["amazon-bedrock", "amazon-bedrock"],
8135
- ["aws.bedrock", "amazon-bedrock"],
8136
- ["bedrock", "amazon-bedrock"],
8137
- ["anthropic", "anthropic"],
8138
- ["openai", "openai"],
8139
- ["google", "google"],
8140
- ["az.ai.openai", "azure"],
8141
- ["azure", "azure"],
8142
- ["openrouter", "openrouter"]
8143
- ];
8144
- function canonicalModelProvider(provider) {
8145
- var _a;
8146
- const normalizedProvider = provider.trim().toLowerCase();
8147
- if (normalizedProvider === "gateway") return "vercel";
8148
- const match = MODEL_PROVIDER_NAMES.find(
8149
- ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
8047
+ var TRACEPARENT_HEADER = "traceparent";
8048
+ var BAGGAGE_HEADER = "baggage";
8049
+ var W3C_TRACE_ID = /^[0-9a-f]{32}$/;
8050
+ var W3C_SPAN_ID = /^[0-9a-f]{16}$/;
8051
+ function isW3CTraceId(value) {
8052
+ return value !== void 0 && W3C_TRACE_ID.test(value);
8053
+ }
8054
+ function isW3CSpanId(value) {
8055
+ return value !== void 0 && W3C_SPAN_ID.test(value);
8056
+ }
8057
+ var warnedNonConformantIds = false;
8058
+ function warnNonConformantIdsOnce(traceId, spanId) {
8059
+ if (warnedNonConformantIds) return;
8060
+ warnedNonConformantIds = true;
8061
+ console.warn(
8062
+ `[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.`
8150
8063
  );
8151
- return (_a = match == null ? void 0 : match[1]) != null ? _a : normalizedProvider;
8152
8064
  }
8153
- function tokenCount(value) {
8154
- return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
8065
+ var HANDOFF_HEADER = "x-raindrop-handoff";
8066
+ var LANGSMITH_TRACE_HEADER = "langsmith-trace";
8067
+ var LANGSMITH_METADATA_BAGGAGE_KEY = "langsmith-metadata";
8068
+ function encodeBaggage(entries) {
8069
+ return Object.entries(entries).filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join(",");
8155
8070
  }
8156
- function buildRawModelUsageAttributes(usage) {
8157
- const providerReportedCost = typeof usage.providerReportedCost === "number" && Number.isFinite(usage.providerReportedCost) && usage.providerReportedCost >= 0 ? usage.providerReportedCost : void 0;
8158
- return [
8159
- attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, tokenCount(usage.inputTokens)),
8160
- attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, tokenCount(usage.outputTokens)),
8161
- attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, tokenCount(usage.reasoningTokens)),
8162
- attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, tokenCount(usage.cacheReadInputTokens)),
8163
- attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, tokenCount(usage.cacheWriteInputTokens)),
8164
- attrDouble(MODEL_USAGE_ATTRIBUTES.providerReportedCost, providerReportedCost)
8165
- ].filter((attribute) => attribute !== void 0);
8071
+ function decodeBaggage(header) {
8072
+ if (!header) return {};
8073
+ const parsed = {};
8074
+ for (const part of header.split(",")) {
8075
+ const [rawKey, ...rest] = part.split("=");
8076
+ if (!rawKey || rest.length === 0) continue;
8077
+ const key = rawKey.trim();
8078
+ let value;
8079
+ try {
8080
+ value = decodeURIComponent(rest.join("=").trim());
8081
+ } catch (e) {
8082
+ value = rest.join("=").trim();
8083
+ }
8084
+ if (key in parsed && parsed[key] !== value) throw new ConflictingCarrierError(key);
8085
+ parsed[key] = value;
8086
+ }
8087
+ return parsed;
8166
8088
  }
8167
- var STRING_ALIASES = {
8168
- [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
8169
- };
8170
- var NUMBER_ALIASES = {
8171
- [MODEL_USAGE_ATTRIBUTES.inputTokens]: [
8172
- "gen_ai.usage.prompt_tokens",
8173
- "ai.usage.prompt_tokens",
8174
- "ai.usage.promptTokens",
8175
- "ai.usage.input_tokens",
8176
- "ai.usage.inputTokens"
8177
- ],
8178
- [MODEL_USAGE_ATTRIBUTES.outputTokens]: [
8179
- "gen_ai.usage.completion_tokens",
8180
- "ai.usage.completion_tokens",
8181
- "ai.usage.completionTokens",
8182
- "ai.usage.output_tokens",
8183
- "ai.usage.outputTokens"
8184
- ],
8185
- [MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
8186
- "gen_ai.usage.reasoning.output_tokens",
8187
- "ai.usage.reasoningTokens",
8188
- "ai.usage.thoughts_tokens"
8189
- ],
8190
- [MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
8191
- "gen_ai.usage.cache_read_tokens",
8192
- "gen_ai.usage.cache_read.input_tokens",
8193
- "ai.usage.cachedInputTokens",
8194
- "ai.usage.cached_tokens",
8195
- "ai.usage.cache_read_tokens",
8196
- "ai.usage.cache_read_input_tokens",
8197
- "ai.usage.cacheReadInputTokens"
8198
- ],
8199
- [MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
8200
- "gen_ai.usage.cache_creation_input_tokens",
8201
- "gen_ai.usage.cache_creation.input_tokens",
8202
- "ai.usage.cacheWriteInputTokens",
8203
- "ai.usage.cache_creation_input_tokens",
8204
- "ai.usage.cacheCreationInputTokens"
8205
- ]
8089
+ function carrierBaggage(carrier) {
8090
+ return {
8091
+ [BAGGAGE_KEYS.eventId]: carrier.eventId,
8092
+ [BAGGAGE_KEYS.childEventId]: carrier.childEventId,
8093
+ [BAGGAGE_KEYS.name]: carrier.name,
8094
+ [BAGGAGE_KEYS.convoId]: carrier.convoId,
8095
+ [BAGGAGE_KEYS.userId]: carrier.userId,
8096
+ [BAGGAGE_KEYS.dispatchSpanId]: carrier.spanId,
8097
+ [BAGGAGE_KEYS.traceId]: carrier.traceId
8098
+ };
8099
+ }
8100
+ function toHeaders(carrier) {
8101
+ const fields = encodeBaggage(carrierBaggage(carrier));
8102
+ const headers = {
8103
+ [BAGGAGE_HEADER]: fields,
8104
+ [HANDOFF_HEADER]: fields
8105
+ };
8106
+ if (isW3CTraceId(carrier.traceId) && isW3CSpanId(carrier.spanId)) {
8107
+ headers[TRACEPARENT_HEADER] = `00-${carrier.traceId}-${carrier.spanId}-01`;
8108
+ }
8109
+ return headers;
8110
+ }
8111
+ function langsmithStamp() {
8112
+ return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "000000");
8113
+ }
8114
+ function toLangSmithHeaders(carrier) {
8115
+ const stamp = langsmithStamp();
8116
+ const dottedOrder = `${stamp}Z${carrier.traceId}.${stamp}Z${carrier.spanId}`;
8117
+ return {
8118
+ [LANGSMITH_TRACE_HEADER]: dottedOrder,
8119
+ [BAGGAGE_HEADER]: encodeBaggage({
8120
+ [LANGSMITH_METADATA_BAGGAGE_KEY]: JSON.stringify(carrierBaggage(carrier))
8121
+ }),
8122
+ // Carried here too: a LangSmith-shaped carrier is rewritten in transit
8123
+ // exactly like the native one, and an injected `traceparent` is preferred
8124
+ // over the dotted order on read.
8125
+ [HANDOFF_HEADER]: encodeBaggage(carrierBaggage(carrier))
8126
+ };
8127
+ }
8128
+ function isHeaderLookup(headers) {
8129
+ return "get" in headers && typeof headers.get === "function";
8130
+ }
8131
+ var ConflictingCarrierError = class extends Error {
8206
8132
  };
8207
- var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
8208
- "ai.request.providerOptions",
8209
- "ai.response.providerMetadata"
8210
- ];
8211
- var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
8212
- function getAsyncLocalStorageCtor() {
8213
- return globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
8133
+ function readHeader(headers, name) {
8134
+ var _a, _b, _c;
8135
+ if (isHeaderLookup(headers)) return (_a = headers.get(name)) != null ? _a : void 0;
8136
+ const value = (_c = headers[name]) != null ? _c : (_b = Object.entries(headers).find(([key]) => key.toLowerCase() === name)) == null ? void 0 : _b[1];
8137
+ if (!Array.isArray(value)) return value;
8138
+ const distinct = new Set(value);
8139
+ if (distinct.size > 1) throw new ConflictingCarrierError(name);
8140
+ return value[0];
8214
8141
  }
8215
- function getWeakRefCtor() {
8216
- const ctor = globalThis.WeakRef;
8217
- return typeof ctor === "function" ? ctor : void 0;
8142
+ function readTraceHeader(headers, name) {
8143
+ const value = readHeader(headers, name);
8144
+ if (!(value == null ? void 0 : value.includes(","))) return value;
8145
+ const copies = value.split(",").map((copy) => copy.trim());
8146
+ if (new Set(copies).size > 1) throw new ConflictingCarrierError(name);
8147
+ return copies[0];
8148
+ }
8149
+ function fromHeaders(headers) {
8150
+ var _a, _b;
8151
+ let baggage;
8152
+ let handoff;
8153
+ let traceparent;
8154
+ let langsmithTrace;
8155
+ try {
8156
+ baggage = decodeBaggage(readHeader(headers, BAGGAGE_HEADER));
8157
+ handoff = decodeBaggage(readHeader(headers, HANDOFF_HEADER));
8158
+ traceparent = readTraceHeader(headers, TRACEPARENT_HEADER);
8159
+ langsmithTrace = readTraceHeader(headers, LANGSMITH_TRACE_HEADER);
8160
+ } catch (error) {
8161
+ if (error instanceof ConflictingCarrierError) return null;
8162
+ throw error;
8163
+ }
8164
+ const langsmithMetadata = baggage[LANGSMITH_METADATA_BAGGAGE_KEY];
8165
+ const nested = langsmithMetadata ? safeParseStringRecord(langsmithMetadata) : {};
8166
+ for (const [key, value] of Object.entries(nested)) {
8167
+ if (key in baggage && baggage[key] !== value) return null;
8168
+ }
8169
+ const fields = {
8170
+ ...baggage,
8171
+ ...nested,
8172
+ // Last, so it wins: the standard headers may have been rewritten in transit
8173
+ // by a propagator, this one is only ever written by us.
8174
+ ...handoff
8175
+ };
8176
+ let traceId;
8177
+ let spanId;
8178
+ if (traceparent) {
8179
+ const [, parsedTraceId, parsedSpanId] = traceparent.split("-");
8180
+ if (isW3CTraceId(parsedTraceId) && isW3CSpanId(parsedSpanId)) {
8181
+ traceId = parsedTraceId;
8182
+ spanId = parsedSpanId;
8183
+ }
8184
+ }
8185
+ if (!traceId && !spanId && langsmithTrace) {
8186
+ const segments = langsmithTrace.split(".");
8187
+ traceId = (_a = segments[0]) == null ? void 0 : _a.split("Z").pop();
8188
+ spanId = (_b = segments[segments.length - 1]) == null ? void 0 : _b.split("Z").pop();
8189
+ }
8190
+ traceId = fields[BAGGAGE_KEYS.traceId] || traceId;
8191
+ spanId = fields[BAGGAGE_KEYS.dispatchSpanId] || spanId;
8192
+ const eventId = fields[BAGGAGE_KEYS.eventId];
8193
+ if (!traceId || !spanId || !eventId) return null;
8194
+ if (!isW3CTraceId(traceId) || !isW3CSpanId(spanId)) {
8195
+ warnNonConformantIdsOnce(traceId, spanId);
8196
+ }
8197
+ const carrier = { traceId, spanId, eventId };
8198
+ const childEventId = fields[BAGGAGE_KEYS.childEventId];
8199
+ if (childEventId) carrier.childEventId = childEventId;
8200
+ const name = fields[BAGGAGE_KEYS.name];
8201
+ if (name) carrier.name = name;
8202
+ const convoId = fields[BAGGAGE_KEYS.convoId];
8203
+ if (convoId) carrier.convoId = convoId;
8204
+ const userId = fields[BAGGAGE_KEYS.userId];
8205
+ if (userId) carrier.userId = userId;
8206
+ return carrier;
8207
+ }
8208
+ 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.`;
8209
+ var UNRESOLVED_CARRIER_WARNED_KEY = /* @__PURE__ */ Symbol.for("raindrop.handoff.warnedUnresolvedCarrier");
8210
+ function warnedHolder() {
8211
+ return globalThis;
8212
+ }
8213
+ function warnUnresolvedCarrier(headers, carrier) {
8214
+ if (carrier) return;
8215
+ if (!headers) return;
8216
+ const holder = warnedHolder();
8217
+ if (holder[UNRESOLVED_CARRIER_WARNED_KEY]) return;
8218
+ holder[UNRESOLVED_CARRIER_WARNED_KEY] = true;
8219
+ console.warn(UNRESOLVED_CARRIER_WARNING);
8220
+ }
8221
+ function safeParseStringRecord(raw) {
8222
+ try {
8223
+ const parsed = JSON.parse(raw);
8224
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
8225
+ const result = {};
8226
+ for (const [key, value] of Object.entries(parsed)) {
8227
+ if (typeof value === "string") result[key] = value;
8228
+ }
8229
+ return result;
8230
+ } catch (e) {
8231
+ return {};
8232
+ }
8233
+ }
8234
+ function runWithTracingSuppressed(fn) {
8235
+ const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
8236
+ if (typeof hook !== "function") return fn();
8237
+ let started = false;
8238
+ try {
8239
+ return hook(() => {
8240
+ started = true;
8241
+ return fn();
8242
+ });
8243
+ } catch (err) {
8244
+ if (started) throw err;
8245
+ return fn();
8246
+ }
8247
+ }
8248
+ var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
8249
+ var MAX_RETRY_DELAY_MS = 3e4;
8250
+ function wait(ms) {
8251
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
8252
+ }
8253
+ function formatEndpoint(endpoint) {
8254
+ if (!endpoint) return void 0;
8255
+ return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
8256
+ }
8257
+ function redactUrlForLog(url) {
8258
+ try {
8259
+ const parsed = new URL(url);
8260
+ parsed.username = "";
8261
+ parsed.password = "";
8262
+ parsed.search = "";
8263
+ parsed.hash = "";
8264
+ return parsed.toString();
8265
+ } catch (e) {
8266
+ return "<unparseable-url>";
8267
+ }
8268
+ }
8269
+ var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
8270
+ var rateLimitedLogLast = /* @__PURE__ */ new Map();
8271
+ function rateLimitedLog(key, log) {
8272
+ const now = Date.now();
8273
+ const last = rateLimitedLogLast.get(key);
8274
+ if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
8275
+ return false;
8276
+ }
8277
+ rateLimitedLogLast.set(key, now);
8278
+ log();
8279
+ return true;
8280
+ }
8281
+ async function raceWithTimeout(promise, timeoutMs) {
8282
+ let timer;
8283
+ const settledInTime = await Promise.race([
8284
+ promise.then(
8285
+ () => true,
8286
+ () => true
8287
+ ),
8288
+ new Promise((resolve2) => {
8289
+ var _a;
8290
+ timer = setTimeout(() => resolve2(false), Math.max(0, timeoutMs));
8291
+ (_a = timer.unref) == null ? void 0 : _a.call(timer);
8292
+ })
8293
+ ]);
8294
+ if (timer) clearTimeout(timer);
8295
+ return settledInTime;
8296
+ }
8297
+ function parseRetryAfter(headers) {
8298
+ var _a;
8299
+ const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
8300
+ if (!value) return void 0;
8301
+ const asNumber = Number(value);
8302
+ if (value.trim() !== "" && !Number.isNaN(asNumber)) return asNumber * 1e3;
8303
+ const asDate = new Date(value).getTime();
8304
+ if (!Number.isNaN(asDate)) {
8305
+ const delta = asDate - Date.now();
8306
+ return delta > 0 ? delta : 0;
8307
+ }
8308
+ return void 0;
8218
8309
  }
8219
- var MAX_BINDING_STACK = 128;
8220
- var EMPTY_STACK = [];
8221
- var ContextStackStorage = class {
8222
- /** Emitted once when this runtime cannot support an ambient push. */
8223
- constructor(noAmbientBindWarning) {
8224
- this.noAmbientBindWarning = noAmbientBindWarning;
8225
- this._als = null;
8226
- this._enterWithUsable = false;
8227
- this._syncStack = EMPTY_STACK;
8228
- this._adoptionChecked = false;
8229
- this._warnedNoAmbientBind = false;
8310
+ function getRetryDelayMs(attemptNumber, previousError) {
8311
+ if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
8312
+ const v = previousError.retryAfterMs;
8313
+ if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
8230
8314
  }
8231
- maybeAdoptAsyncLocalStorage() {
8232
- if (this._adoptionChecked) return;
8233
- this._adoptionChecked = true;
8234
- const Ctor = getAsyncLocalStorageCtor();
8235
- if (!Ctor) return;
8236
- const als = new Ctor();
8237
- try {
8238
- als.run(EMPTY_STACK, () => {
8239
- });
8240
- } catch (e) {
8241
- return;
8315
+ if (attemptNumber <= 1) return 0;
8316
+ const base = 500;
8317
+ const factor = Math.pow(2, attemptNumber - 2);
8318
+ return Math.min(base * factor, MAX_RETRY_DELAY_MS);
8319
+ }
8320
+ async function withRetry(operation, opName, opts) {
8321
+ const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
8322
+ let lastError = void 0;
8323
+ for (let attemptNumber = 1; attemptNumber <= opts.maxAttempts; attemptNumber++) {
8324
+ if (attemptNumber > 1) {
8325
+ const delay = getRetryDelayMs(attemptNumber, lastError);
8326
+ if (opts.debug) {
8327
+ console.warn(
8328
+ `${prefix} ${opName} retry ${attemptNumber}/${opts.maxAttempts} in ${delay}ms`
8329
+ );
8330
+ }
8331
+ if (delay > 0) await wait(delay);
8332
+ } else if (opts.debug) {
8333
+ console.log(`${prefix} ${opName} attempt ${attemptNumber}/${opts.maxAttempts}`);
8242
8334
  }
8243
- this._als = als;
8244
- if (typeof als.enterWith === "function") {
8245
- try {
8246
- als.enterWith(EMPTY_STACK);
8247
- this._enterWithUsable = true;
8248
- } catch (e) {
8249
- this._enterWithUsable = false;
8335
+ try {
8336
+ return await operation();
8337
+ } catch (err) {
8338
+ lastError = err;
8339
+ if (opts.debug) {
8340
+ const msg = err instanceof Error ? err.message : String(err);
8341
+ console.warn(
8342
+ `${prefix} ${opName} attempt ${attemptNumber} failed: ${msg}${attemptNumber === opts.maxAttempts ? " (no more retries)" : ""}`
8343
+ );
8250
8344
  }
8345
+ if (lastError && typeof lastError === "object" && "retryable" in lastError && !lastError.retryable)
8346
+ break;
8347
+ if (attemptNumber === opts.maxAttempts) break;
8251
8348
  }
8252
8349
  }
8253
- getStack() {
8254
- var _a;
8255
- this.maybeAdoptAsyncLocalStorage();
8256
- if (this._als) return (_a = this._als.getStore()) != null ? _a : this._syncStack;
8257
- return this._syncStack;
8350
+ throw lastError instanceof Error ? lastError : new Error(String(lastError));
8351
+ }
8352
+ async function postJson(url, body, headers, opts) {
8353
+ var _a;
8354
+ const opName = `POST ${redactUrlForLog(url)}`;
8355
+ const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
8356
+ await withRetry(
8357
+ async () => {
8358
+ const resp = await runWithTracingSuppressed(
8359
+ () => fetch(url, {
8360
+ method: "POST",
8361
+ redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
8362
+ headers: {
8363
+ "Content-Type": "application/json",
8364
+ ...headers
8365
+ },
8366
+ body: JSON.stringify(body),
8367
+ signal: AbortSignal.timeout(timeoutMs)
8368
+ })
8369
+ );
8370
+ if (!resp.ok) {
8371
+ const text = await resp.text().catch(() => "");
8372
+ const err = new Error(
8373
+ `HTTP ${resp.status} ${resp.statusText}${text ? `: ${text}` : ""}`
8374
+ );
8375
+ const retryAfterMs = parseRetryAfter(resp.headers);
8376
+ if (typeof retryAfterMs === "number") err.retryAfterMs = retryAfterMs;
8377
+ err.retryable = resp.status === 429 || resp.status >= 500;
8378
+ throw err;
8379
+ }
8380
+ },
8381
+ opName,
8382
+ opts
8383
+ );
8384
+ }
8385
+ var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
8386
+ var TRUNCATION_MARKER = "...[truncated by raindrop]";
8387
+ var BOUNDED_CLONE_MAX_DEPTH = 12;
8388
+ var BOUNDED_CLONE_SLACK = 256;
8389
+ var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
8390
+ function setDefaultMaxTextFieldChars(value) {
8391
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
8392
+ currentDefaultMaxTextFieldChars = Math.floor(value);
8258
8393
  }
8259
- /**
8260
- * Persist an ambient stack mutation. Returns false when an ALS is adopted but
8261
- * `enterWith` is unusable (workerd): callers must NOT fall back to the
8262
- * process-shared sync stack, since concurrent isolates would cross-stamp.
8263
- */
8264
- setAmbientStack(stack) {
8265
- if (this._als) {
8266
- if (!this._enterWithUsable) return false;
8267
- this._als.enterWith(stack);
8268
- return true;
8269
- }
8270
- this._syncStack = stack;
8271
- return true;
8394
+ }
8395
+ function resolveMaxTextFieldChars(value) {
8396
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
8397
+ return Math.floor(value);
8272
8398
  }
8273
- push(binding) {
8274
- this.maybeAdoptAsyncLocalStorage();
8275
- if (this._als && !this._enterWithUsable) {
8276
- this.warnNoAmbientBindOnce();
8277
- return;
8278
- }
8279
- let stack = this.getStack();
8280
- if (stack.length >= MAX_BINDING_STACK) {
8281
- stack = stack.filter((b) => b.isLive());
8282
- if (stack.length >= MAX_BINDING_STACK) {
8283
- stack = stack.slice(stack.length - (MAX_BINDING_STACK - 1));
8399
+ return currentDefaultMaxTextFieldChars;
8400
+ }
8401
+ function truncateToLimit(text, limit) {
8402
+ if (limit > TRUNCATION_MARKER.length) {
8403
+ return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
8404
+ }
8405
+ return text.slice(0, Math.max(0, limit));
8406
+ }
8407
+ function capText(value, limit) {
8408
+ if (typeof value !== "string") return value;
8409
+ const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
8410
+ if (value.length <= max) return value;
8411
+ return truncateToLimit(value, max);
8412
+ }
8413
+ function boundedClone(obj, charBudget) {
8414
+ let budget = charBudget;
8415
+ const seen = /* @__PURE__ */ new WeakSet();
8416
+ const walk = (node, depth) => {
8417
+ if (budget <= 0) return TRUNCATION_MARKER;
8418
+ if (typeof node === "string") {
8419
+ if (node.length > budget) {
8420
+ const taken = node.slice(0, Math.max(0, budget)) + TRUNCATION_MARKER;
8421
+ budget = 0;
8422
+ return taken;
8284
8423
  }
8424
+ budget -= Math.max(node.length, 1);
8425
+ return node;
8285
8426
  }
8286
- this.setAmbientStack([...stack, binding]);
8287
- }
8288
- remove(binding) {
8289
- binding.markDead();
8290
- const stack = this.getStack();
8291
- const pruned = stack.filter((b) => b !== binding && b.isLive());
8292
- if (pruned.length !== stack.length) {
8293
- this.setAmbientStack(pruned);
8427
+ if (node === null || typeof node === "number" || typeof node === "boolean") {
8428
+ budget -= 8;
8429
+ return node;
8294
8430
  }
8295
- }
8296
- runWith(binding, callback) {
8297
- this.maybeAdoptAsyncLocalStorage();
8298
- if (this._als) {
8299
- return this._als.run([...this.getStack(), binding], callback);
8431
+ if (typeof node !== "object") {
8432
+ budget -= 8;
8433
+ return node;
8300
8434
  }
8301
- const previous = this._syncStack;
8302
- this._syncStack = [...previous, binding];
8303
- try {
8304
- return callback();
8305
- } finally {
8306
- this._syncStack = previous;
8435
+ if (seen.has(node)) return "[CIRCULAR]";
8436
+ if (depth >= BOUNDED_CLONE_MAX_DEPTH) {
8437
+ budget -= 16;
8438
+ return `<max depth>`;
8307
8439
  }
8308
- }
8309
- current() {
8310
- const stack = this.getStack();
8311
- if (stack.length === 0) return void 0;
8312
- const top = stack[stack.length - 1];
8313
- if (top.isLive()) return top;
8314
- const pruned = stack.filter((b) => b.isLive());
8315
- this.setAmbientStack(pruned);
8316
- return pruned.length > 0 ? pruned[pruned.length - 1] : void 0;
8317
- }
8318
- warnNoAmbientBindOnce() {
8319
- if (this._warnedNoAmbientBind) return;
8320
- this._warnedNoAmbientBind = true;
8321
- console.warn(this.noAmbientBindWarning);
8322
- }
8323
- };
8324
- 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.";
8325
- 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.";
8326
- var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
8327
- function routingStorage() {
8328
- const holder = globalThis;
8329
- let storage = holder[ROUTING_STORAGE_KEY];
8330
- if (!storage) {
8331
- storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
8332
- holder[ROUTING_STORAGE_KEY] = storage;
8333
- }
8334
- return storage;
8335
- }
8336
- function makeBoundContext(binding, owner) {
8337
- const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
8338
- const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
8339
- let dead = false;
8340
- return {
8341
- projectId: binding.projectId,
8342
- authHint: binding.authHint,
8343
- isLive() {
8344
- if (dead) return false;
8345
- return ref === void 0 || ref.deref() !== void 0;
8346
- },
8347
- markDead() {
8348
- dead = true;
8440
+ seen.add(node);
8441
+ if (Array.isArray(node)) {
8442
+ const out2 = [];
8443
+ for (const v of node) {
8444
+ if (budget <= 0) {
8445
+ out2.push(TRUNCATION_MARKER);
8446
+ break;
8447
+ }
8448
+ out2.push(walk(v, depth + 1));
8449
+ }
8450
+ return out2;
8349
8451
  }
8452
+ if (typeof node.toJSON === "function") {
8453
+ budget -= 16;
8454
+ return node;
8455
+ }
8456
+ const out = {};
8457
+ for (const k in node) {
8458
+ if (!Object.prototype.hasOwnProperty.call(node, k)) continue;
8459
+ if (budget <= 0) {
8460
+ out["..."] = TRUNCATION_MARKER;
8461
+ break;
8462
+ }
8463
+ const key = walk(k, depth + 1);
8464
+ out[key] = walk(node[k], depth + 1);
8465
+ }
8466
+ return out;
8350
8467
  };
8468
+ return walk(obj, 0);
8351
8469
  }
8352
- function bindRoutingContext(binding, owner) {
8353
- const bound = makeBoundContext(binding, owner);
8470
+ function stringifyBounded(value, limit) {
8471
+ const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
8472
+ if (typeof value === "string") {
8473
+ return capText(value, max);
8474
+ }
8475
+ const pruned = boundedClone(value, max + TRUNCATION_MARKER.length + BOUNDED_CLONE_SLACK);
8476
+ let json;
8354
8477
  try {
8355
- routingStorage().push(bound);
8478
+ json = JSON.stringify(pruned);
8356
8479
  } catch (e) {
8480
+ json = void 0;
8357
8481
  }
8358
- return bound;
8482
+ if (json === void 0) {
8483
+ return capText(String(value), max);
8484
+ }
8485
+ return json.length <= max ? json : truncateToLimit(json, max);
8359
8486
  }
8360
- function unbindRoutingContext(bound) {
8361
- if (!bound || !isBoundContext(bound)) return;
8487
+ function attrString(key, value) {
8488
+ if (value === void 0) return void 0;
8489
+ return { key, value: { stringValue: value } };
8490
+ }
8491
+ function attrInt(key, value) {
8492
+ if (value === void 0) return void 0;
8493
+ if (!Number.isFinite(value)) return void 0;
8494
+ return { key, value: { intValue: String(Math.trunc(value)) } };
8495
+ }
8496
+ function attrDouble(key, value) {
8497
+ if (value === void 0) return void 0;
8498
+ if (!Number.isFinite(value)) return void 0;
8499
+ return { key, value: { doubleValue: value } };
8500
+ }
8501
+ var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
8502
+ var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
8503
+ var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
8504
+ function readEnvVar(name) {
8505
+ var _a;
8362
8506
  try {
8363
- routingStorage().remove(bound);
8507
+ const env = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env;
8508
+ if (env && typeof env[name] === "string" && env[name].length > 0) {
8509
+ return env[name];
8510
+ }
8364
8511
  } catch (e) {
8365
8512
  }
8513
+ return void 0;
8366
8514
  }
8367
- function withRoutingContext(binding, callback) {
8368
- const bound = makeBoundContext(binding);
8369
- return routingStorage().runWith(bound, callback);
8515
+ function readWorkshopEnv() {
8516
+ const raw = readEnvVar(WORKSHOP_ENV_VAR);
8517
+ if (raw === void 0) return void 0;
8518
+ const trimmed = raw.trim();
8519
+ if (trimmed.length === 0) return void 0;
8520
+ if (/^https?:\/\//i.test(trimmed)) return { url: trimmed };
8521
+ if (/^(1|true|yes|on)$/i.test(trimmed)) return "enable";
8522
+ if (/^(0|false|no|off)$/i.test(trimmed)) return "disable";
8523
+ return void 0;
8370
8524
  }
8371
- function currentRoutingBinding() {
8372
- const bound = routingStorage().current();
8373
- if (!bound) return void 0;
8374
- return { projectId: bound.projectId, authHint: bound.authHint };
8525
+ function isLocalDevHost(hostname) {
8526
+ if (!hostname) return false;
8527
+ if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1") {
8528
+ return true;
8529
+ }
8530
+ if (hostname.endsWith(".localhost")) return true;
8531
+ return false;
8375
8532
  }
8376
- function isBoundContext(value) {
8377
- return typeof value.isLive === "function";
8533
+ function readRuntimeHostname() {
8534
+ try {
8535
+ const loc = globalThis == null ? void 0 : globalThis.location;
8536
+ if (loc && typeof loc.hostname === "string" && loc.hostname.length > 0) {
8537
+ return loc.hostname;
8538
+ }
8539
+ } catch (e) {
8540
+ }
8541
+ return void 0;
8378
8542
  }
8379
- var ATTRIBUTE_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.spanAttributeStorage");
8380
- function attributeStorage() {
8381
- const holder = globalThis;
8382
- let storage = holder[ATTRIBUTE_STORAGE_KEY];
8383
- if (!storage) {
8384
- storage = new ContextStackStorage(NO_AMBIENT_ATTRIBUTE_BIND_WARNING);
8385
- holder[ATTRIBUTE_STORAGE_KEY] = storage;
8543
+ function shouldAutoEnableLocalWorkshop() {
8544
+ if (isLocalDevHost(readRuntimeHostname())) return true;
8545
+ if (readEnvVar("NODE_ENV") === "development") return true;
8546
+ return false;
8547
+ }
8548
+ function resolveLocalDebuggerBaseUrl(baseUrl) {
8549
+ var _a, _b, _c;
8550
+ if (baseUrl === null) return null;
8551
+ if (typeof baseUrl === "string" && baseUrl.length > 0) {
8552
+ return (_a = formatEndpoint(baseUrl)) != null ? _a : null;
8386
8553
  }
8387
- return storage;
8554
+ const explicitUrlEnv = readEnvVar(LOCAL_DEBUGGER_ENV_VAR);
8555
+ if (explicitUrlEnv) return (_b = formatEndpoint(explicitUrlEnv)) != null ? _b : null;
8556
+ const workshopEnv = readWorkshopEnv();
8557
+ if (workshopEnv === "disable") return null;
8558
+ if (workshopEnv === "enable") return DEFAULT_LOCAL_WORKSHOP_URL;
8559
+ if (workshopEnv && "url" in workshopEnv) return (_c = formatEndpoint(workshopEnv.url)) != null ? _c : null;
8560
+ if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
8561
+ return null;
8388
8562
  }
8389
- function makeAttributeFrame(attributes, owner) {
8390
- const snapshot = { ...attributes };
8391
- const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
8392
- const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
8393
- const owned = /* @__PURE__ */ new WeakSet();
8394
- let dead = false;
8395
- let sawError = false;
8396
- return {
8397
- attributes: snapshot,
8398
- sawErrorSpan: () => sawError,
8399
- ownsSpan: (span) => owned.has(span),
8400
- claimSpan(span) {
8401
- owned.add(span);
8402
- },
8403
- markErrorSpanSeen() {
8404
- sawError = true;
8405
- },
8406
- // A frame whose owner has been collected is dead even though nothing
8407
- // unbound it: a run that never settles must not keep handing its reverse
8408
- // reference to whatever runs next on a reused context.
8409
- isLive() {
8410
- if (dead) return false;
8411
- return ref === void 0 || ref.deref() !== void 0;
8563
+ function localDebuggerEnabled(baseUrl) {
8564
+ return resolveLocalDebuggerBaseUrl(baseUrl) !== null;
8565
+ }
8566
+ var LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS = 2e3;
8567
+ function mirrorTraceExportToLocalDebugger(body, options = {}) {
8568
+ var _a;
8569
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
8570
+ if (!baseUrl) return Promise.resolve();
8571
+ return postJson(`${baseUrl}traces`, body, {}, {
8572
+ maxAttempts: 1,
8573
+ debug: (_a = options.debug) != null ? _a : false,
8574
+ sdkName: options.sdkName,
8575
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
8576
+ }).catch(() => {
8577
+ });
8578
+ }
8579
+ function mirrorPartialEventToLocalDebugger(event, options = {}) {
8580
+ var _a;
8581
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
8582
+ if (!baseUrl) return Promise.resolve();
8583
+ const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
8584
+ return postJson(`${baseUrl}events/track_partial`, event, headers, {
8585
+ maxAttempts: 1,
8586
+ debug: (_a = options.debug) != null ? _a : false,
8587
+ sdkName: options.sdkName,
8588
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
8589
+ }).catch(() => {
8590
+ });
8591
+ }
8592
+ function sendLocalDebuggerLiveEvent(event, options = {}) {
8593
+ var _a, _b;
8594
+ const destination = currentReplayTraceDestination();
8595
+ if (destination && destination.kind !== "workshop") return;
8596
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
8597
+ if (!baseUrl) return;
8598
+ void postJson(
8599
+ `${baseUrl}live`,
8600
+ {
8601
+ ...event,
8602
+ type: event.type,
8603
+ timestamp: (_a = event.timestamp) != null ? _a : Date.now()
8412
8604
  },
8413
- markDead() {
8414
- dead = true;
8605
+ {},
8606
+ {
8607
+ maxAttempts: 1,
8608
+ debug: (_b = options.debug) != null ? _b : false,
8609
+ sdkName: options.sdkName
8415
8610
  }
8416
- };
8611
+ ).catch(() => {
8612
+ });
8613
+ }
8614
+ var PROJECT_ID_HEADER = "X-Raindrop-Project-Id";
8615
+ var PROJECT_ID_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
8616
+ function isValidProjectIdSlug(value) {
8617
+ return PROJECT_ID_SLUG_PATTERN.test(value);
8618
+ }
8619
+ function normalizeProjectId(raw, opts) {
8620
+ if (typeof raw !== "string") return void 0;
8621
+ const trimmed = raw.trim();
8622
+ if (!trimmed) return void 0;
8623
+ if (!isValidProjectIdSlug(trimmed) && opts.debug) {
8624
+ console.warn(
8625
+ `${opts.prefix} projectId "${trimmed}" does not match slug ${PROJECT_ID_SLUG_PATTERN.source}; sending anyway \u2014 backend may reject with HTTP 400`
8626
+ );
8627
+ }
8628
+ return trimmed;
8629
+ }
8630
+ function projectIdHeaders(projectId) {
8631
+ return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
8632
+ }
8633
+ var SHUTDOWN_DEADLINE_MS = 1e4;
8634
+ var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
8635
+ var MODEL_USAGE_ATTRIBUTES = {
8636
+ usageSource: "raindrop.usage.source",
8637
+ providerName: "gen_ai.provider.name",
8638
+ requestModel: "gen_ai.request.model",
8639
+ responseModel: "gen_ai.response.model",
8640
+ inputTokens: "gen_ai.usage.input_tokens",
8641
+ outputTokens: "gen_ai.usage.output_tokens",
8642
+ reasoningTokens: "gen_ai.usage.reasoning_tokens",
8643
+ cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
8644
+ cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
8645
+ providerReportedCost: "gen_ai.usage.provider_reported_cost",
8646
+ nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
8647
+ nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
8648
+ totalOutputTokens: "raindrop.usage.output_tokens.total",
8649
+ reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
8650
+ cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
8651
+ cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
8652
+ };
8653
+ var MODEL_PROVIDER_NAMES = [
8654
+ ["google.vertex.anthropic", "google-vertex"],
8655
+ ["google.vertex_ai", "google-vertex"],
8656
+ ["google.vertex", "google-vertex"],
8657
+ ["gcp.vertex_ai", "google-vertex"],
8658
+ ["gcp.gemini", "google"],
8659
+ ["google_vertexai", "google-vertex"],
8660
+ ["vertex.anthropic", "google-vertex"],
8661
+ ["amazon-bedrock", "amazon-bedrock"],
8662
+ ["aws.bedrock", "amazon-bedrock"],
8663
+ ["bedrock", "amazon-bedrock"],
8664
+ ["anthropic", "anthropic"],
8665
+ ["openai", "openai"],
8666
+ ["google", "google"],
8667
+ ["az.ai.openai", "azure"],
8668
+ ["azure", "azure"],
8669
+ ["openrouter", "openrouter"]
8670
+ ];
8671
+ function canonicalModelProvider(provider) {
8672
+ var _a;
8673
+ const normalizedProvider = provider.trim().toLowerCase();
8674
+ if (normalizedProvider === "gateway") return "vercel";
8675
+ const match = MODEL_PROVIDER_NAMES.find(
8676
+ ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
8677
+ );
8678
+ return (_a = match == null ? void 0 : match[1]) != null ? _a : normalizedProvider;
8417
8679
  }
8418
- function bindSpanAttributes(attributes, owner) {
8419
- if (Object.keys(attributes).length === 0) return void 0;
8420
- const frame = makeAttributeFrame(attributes, owner);
8421
- try {
8422
- attributeStorage().push(frame);
8423
- } catch (e) {
8424
- return void 0;
8425
- }
8426
- return frame;
8680
+ function tokenCount(value) {
8681
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
8427
8682
  }
8428
- function unbindSpanAttributes(frame) {
8429
- if (!frame || !isAttributeFrame(frame)) return;
8430
- try {
8431
- attributeStorage().remove(frame);
8432
- } catch (e) {
8433
- }
8683
+ function buildRawModelUsageAttributes(usage) {
8684
+ const providerReportedCost = typeof usage.providerReportedCost === "number" && Number.isFinite(usage.providerReportedCost) && usage.providerReportedCost >= 0 ? usage.providerReportedCost : void 0;
8685
+ return [
8686
+ attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, tokenCount(usage.inputTokens)),
8687
+ attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, tokenCount(usage.outputTokens)),
8688
+ attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, tokenCount(usage.reasoningTokens)),
8689
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, tokenCount(usage.cacheReadInputTokens)),
8690
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, tokenCount(usage.cacheWriteInputTokens)),
8691
+ attrDouble(MODEL_USAGE_ATTRIBUTES.providerReportedCost, providerReportedCost)
8692
+ ].filter((attribute) => attribute !== void 0);
8434
8693
  }
8435
- function applySpanAttributeFrames(span, setAttribute) {
8436
- try {
8437
- for (const frame of attributeStorage().getStack()) {
8438
- if (!frame.isLive()) continue;
8439
- frame.claimSpan(span);
8440
- for (const [key, value] of Object.entries(frame.attributes)) {
8441
- setAttribute(key, value);
8442
- }
8694
+ var STRING_ALIASES = {
8695
+ [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
8696
+ };
8697
+ var NUMBER_ALIASES = {
8698
+ [MODEL_USAGE_ATTRIBUTES.inputTokens]: [
8699
+ "gen_ai.usage.prompt_tokens",
8700
+ "ai.usage.prompt_tokens",
8701
+ "ai.usage.promptTokens",
8702
+ "ai.usage.input_tokens",
8703
+ "ai.usage.inputTokens"
8704
+ ],
8705
+ [MODEL_USAGE_ATTRIBUTES.outputTokens]: [
8706
+ "gen_ai.usage.completion_tokens",
8707
+ "ai.usage.completion_tokens",
8708
+ "ai.usage.completionTokens",
8709
+ "ai.usage.output_tokens",
8710
+ "ai.usage.outputTokens"
8711
+ ],
8712
+ [MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
8713
+ "gen_ai.usage.reasoning.output_tokens",
8714
+ "ai.usage.reasoningTokens",
8715
+ "ai.usage.thoughts_tokens"
8716
+ ],
8717
+ [MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
8718
+ "gen_ai.usage.cache_read_tokens",
8719
+ "gen_ai.usage.cache_read.input_tokens",
8720
+ "ai.usage.cachedInputTokens",
8721
+ "ai.usage.cached_tokens",
8722
+ "ai.usage.cache_read_tokens",
8723
+ "ai.usage.cache_read_input_tokens",
8724
+ "ai.usage.cacheReadInputTokens"
8725
+ ],
8726
+ [MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
8727
+ "gen_ai.usage.cache_creation_input_tokens",
8728
+ "gen_ai.usage.cache_creation.input_tokens",
8729
+ "ai.usage.cacheWriteInputTokens",
8730
+ "ai.usage.cache_creation_input_tokens",
8731
+ "ai.usage.cacheCreationInputTokens"
8732
+ ]
8733
+ };
8734
+ var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
8735
+ "ai.request.providerOptions",
8736
+ "ai.response.providerMetadata"
8737
+ ];
8738
+ var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
8739
+
8740
+ // ../core/dist/index.node.js
8741
+ var import_async_hooks = require("async_hooks");
8742
+ var import_child_process = require("child_process");
8743
+ var import_path = require("path");
8744
+ var resolveLocal = (sourceDirectory, detectBranch, completed) => {
8745
+ const cwd = sourceDirectory ? (0, import_path.resolve)(sourceDirectory) : process.cwd();
8746
+ let cancelled = false;
8747
+ let active;
8748
+ let deadlineTimer;
8749
+ const deadline = Date.now() + 1e3;
8750
+ const run = (args, done) => {
8751
+ if (cancelled) return;
8752
+ const remaining = deadline - Date.now();
8753
+ if (remaining <= 0) {
8754
+ done(void 0);
8755
+ return;
8443
8756
  }
8444
- } catch (e) {
8445
- }
8446
- }
8447
- function claimSpanForFrames(span) {
8448
- try {
8449
- for (const frame of attributeStorage().getStack()) {
8450
- if (frame.isLive()) frame.claimSpan(span);
8757
+ try {
8758
+ active = (0, import_child_process.execFile)(
8759
+ "git",
8760
+ args,
8761
+ {
8762
+ cwd,
8763
+ encoding: "utf8",
8764
+ maxBuffer: 4096,
8765
+ windowsHide: true,
8766
+ env: {
8767
+ ...process.env,
8768
+ // The selected application cwd, not the invoking shell's Git plumbing, owns identity.
8769
+ GIT_DIR: void 0,
8770
+ GIT_WORK_TREE: void 0,
8771
+ GIT_COMMON_DIR: void 0,
8772
+ GIT_NAMESPACE: void 0,
8773
+ GIT_INDEX_FILE: void 0,
8774
+ GIT_OBJECT_DIRECTORY: void 0,
8775
+ GIT_ALTERNATE_OBJECT_DIRECTORIES: void 0,
8776
+ GIT_CONFIG_PARAMETERS: void 0,
8777
+ GIT_CONFIG_COUNT: "0",
8778
+ GIT_TERMINAL_PROMPT: "0",
8779
+ GIT_OPTIONAL_LOCKS: "0"
8780
+ }
8781
+ },
8782
+ (error, stdout) => {
8783
+ clearTimeout(deadlineTimer);
8784
+ deadlineTimer = void 0;
8785
+ active = void 0;
8786
+ if (!cancelled) {
8787
+ try {
8788
+ done(error ? void 0 : stdout.trim());
8789
+ } catch (e) {
8790
+ }
8791
+ }
8792
+ }
8793
+ );
8794
+ active.unref();
8795
+ if (active.stdout && "unref" in active.stdout && typeof active.stdout.unref === "function")
8796
+ active.stdout.unref();
8797
+ if (active.stderr && "unref" in active.stderr && typeof active.stderr.unref === "function")
8798
+ active.stderr.unref();
8799
+ deadlineTimer = setTimeout(() => {
8800
+ try {
8801
+ active == null ? void 0 : active.kill("SIGKILL");
8802
+ } catch (e) {
8803
+ }
8804
+ }, remaining);
8805
+ deadlineTimer.unref();
8806
+ } catch (e) {
8807
+ try {
8808
+ done(void 0);
8809
+ } catch (e2) {
8810
+ }
8451
8811
  }
8452
- } catch (e) {
8453
- }
8454
- }
8455
- function currentSpanAttributes() {
8456
- const merged = {};
8457
- try {
8458
- for (const frame of attributeStorage().getStack()) {
8459
- if (!frame.isLive()) continue;
8460
- Object.assign(merged, frame.attributes);
8812
+ };
8813
+ const scheduled = setTimeout(() => {
8814
+ try {
8815
+ run(["rev-parse", "HEAD"], (rawSha) => {
8816
+ const commitSha = validAppCommitSha(rawSha);
8817
+ if (!commitSha) {
8818
+ completed({});
8819
+ return;
8820
+ }
8821
+ run(["-c", "core.fsmonitor=false", "status", "--porcelain"], (status) => {
8822
+ const finish = (branch) => {
8823
+ run(["rev-parse", "HEAD"], (finalSha) => {
8824
+ const coherent = validAppCommitSha(finalSha) === commitSha;
8825
+ completed({
8826
+ commitSha,
8827
+ ...coherent && status !== void 0 ? { commitDirty: status !== "" } : {},
8828
+ ...coherent && branch && branch !== "HEAD" ? { branch } : {}
8829
+ });
8830
+ });
8831
+ };
8832
+ if (detectBranch) run(["symbolic-ref", "--quiet", "--short", "HEAD"], finish);
8833
+ else finish();
8834
+ });
8835
+ });
8836
+ } catch (e) {
8837
+ completed({});
8461
8838
  }
8462
- } catch (e) {
8463
- }
8464
- return merged;
8465
- }
8466
- function markErrorSpanSeen(span) {
8467
- try {
8468
- for (const frame of attributeStorage().getStack()) {
8469
- if (frame.ownsSpan(span)) frame.markErrorSpanSeen();
8839
+ }, 0);
8840
+ scheduled.unref();
8841
+ return () => {
8842
+ cancelled = true;
8843
+ clearTimeout(scheduled);
8844
+ clearTimeout(deadlineTimer);
8845
+ try {
8846
+ active == null ? void 0 : active.kill("SIGKILL");
8847
+ } catch (e) {
8470
8848
  }
8471
- } catch (e) {
8472
- }
8473
- }
8474
- function isAttributeFrame(value) {
8475
- return typeof value.isLive === "function";
8849
+ };
8850
+ };
8851
+ function createAppGitContext(options, runtime = {}) {
8852
+ return new AppGitContext(options, { ...runtime, resolveLocal });
8476
8853
  }
8477
-
8478
- // ../core/dist/index.node.js
8479
- var import_async_hooks = require("async_hooks");
8480
8854
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
8481
8855
  var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
8482
8856
  "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
@@ -8504,7 +8878,7 @@ function installTracingSuppressionHook() {
8504
8878
  }
8505
8879
  installTracingSuppressionHook();
8506
8880
 
8507
- // ../schemas/dist/chunk-VQNKQ4ZC.js
8881
+ // ../schemas/dist/chunk-HA2X6666.js
8508
8882
  var __defProp2 = Object.defineProperty;
8509
8883
  var __export2 = (target, all) => {
8510
8884
  for (var name in all)
@@ -12544,6 +12918,14 @@ var coerce = {
12544
12918
  date: ((arg) => ZodDate.create({ ...arg, coerce: true }))
12545
12919
  };
12546
12920
  var NEVER = INVALID;
12921
+ var AppGitOptionsSchema = external_exports.object({
12922
+ commitSha: external_exports.string().nullable().optional(),
12923
+ commitDirty: external_exports.boolean().nullable().optional(),
12924
+ branch: external_exports.string().nullable().optional(),
12925
+ detectBranch: external_exports.boolean().optional(),
12926
+ sourceDirectory: external_exports.string().optional(),
12927
+ autoDetect: external_exports.boolean().optional()
12928
+ });
12547
12929
  var BaseAttachmentSchema = external_exports.object({
12548
12930
  attachment_id: external_exports.string().optional(),
12549
12931
  name: external_exports.string().default("").optional(),
@@ -12613,7 +12995,7 @@ var SignalEventSchema = external_exports.object({
12613
12995
  // package.json
12614
12996
  var package_default = {
12615
12997
  name: "raindrop-ai",
12616
- version: "0.7.0",
12998
+ version: "0.7.1",
12617
12999
  main: "dist/index.js",
12618
13000
  module: "dist/index.mjs",
12619
13001
  types: "dist/index.d.ts",
@@ -13041,11 +13423,38 @@ function stampContextAttributes(span) {
13041
13423
  }
13042
13424
  }
13043
13425
  var RaindropContextSpanProcessor = class {
13426
+ constructor(appGitSnapshot) {
13427
+ this.appGitSnapshot = appGitSnapshot;
13428
+ this.appGitAdded = /* @__PURE__ */ new WeakMap();
13429
+ }
13044
13430
  onStart(span) {
13431
+ var _a, _b, _c, _d;
13045
13432
  try {
13046
13433
  stampContextAttributes(span);
13047
13434
  rememberReplayTraceDestination(span);
13048
13435
  const bound = currentRoutingBinding();
13436
+ const keys = ["raindrop.app.commit_sha", "raindrop.app.commit_dirty", "raindrop.app.branch"];
13437
+ const attrs = (_a = span.attributes) != null ? _a : {};
13438
+ const properties = {};
13439
+ for (const key of keys) {
13440
+ const existing = [key, `ai.telemetry.metadata.${key}`, `traceloop.association.properties.${key}`].find((name) => Object.prototype.hasOwnProperty.call(attrs, name));
13441
+ if (existing) properties[key] = attrs[existing];
13442
+ }
13443
+ const source = { ...(_d = (_c = (_b = this.appGitSnapshot) == null ? void 0 : _b.call(this, properties)) != null ? _c : bound == null ? void 0 : bound.appGit) != null ? _d : {} };
13444
+ if (Object.prototype.hasOwnProperty.call(properties, "raindrop.app.commit_sha") && properties["raindrop.app.commit_sha"] !== source["raindrop.app.commit_sha"]) {
13445
+ delete source["raindrop.app.commit_dirty"];
13446
+ delete source["raindrop.app.branch"];
13447
+ }
13448
+ const added = /* @__PURE__ */ new Map();
13449
+ for (const key of keys) {
13450
+ if ([key, `ai.telemetry.metadata.${key}`, `traceloop.association.properties.${key}`].some((name) => Object.prototype.hasOwnProperty.call(attrs, name))) continue;
13451
+ const value = source == null ? void 0 : source[key];
13452
+ if (typeof value === "string" || typeof value === "boolean") {
13453
+ span.setAttribute(key, value);
13454
+ added.set(key, value);
13455
+ }
13456
+ }
13457
+ this.appGitAdded.set(span, added);
13049
13458
  if (!bound) return;
13050
13459
  stampSpan(span, bound.projectId, bound.authHint);
13051
13460
  } catch (e) {
@@ -13054,6 +13463,16 @@ var RaindropContextSpanProcessor = class {
13054
13463
  onEnd(span) {
13055
13464
  var _a;
13056
13465
  try {
13466
+ if (span == null ? void 0 : span.attributes) {
13467
+ const attrs = span.attributes;
13468
+ const added = this.appGitAdded.get(span);
13469
+ const shaKey = "raindrop.app.commit_sha";
13470
+ const hasAlias = (key) => [`ai.telemetry.metadata.${key}`, `traceloop.association.properties.${key}`].some((name) => Object.prototype.hasOwnProperty.call(attrs, name));
13471
+ const shaChanged = (added == null ? void 0 : added.has(shaKey)) && (attrs[shaKey] !== added.get(shaKey) || hasAlias(shaKey));
13472
+ for (const [key, value] of added != null ? added : []) {
13473
+ if (attrs[key] === value && (hasAlias(key) || shaChanged && key !== shaKey)) delete attrs[key];
13474
+ }
13475
+ }
13057
13476
  if (((_a = span == null ? void 0 : span.status) == null ? void 0 : _a.code) !== SPAN_STATUS_ERROR) return;
13058
13477
  markErrorSpanSeen(span);
13059
13478
  } catch (e) {
@@ -13181,7 +13600,7 @@ var import_weakref = require("weakref");
13181
13600
 
13182
13601
  // src/tracing/direct/http.ts
13183
13602
  function wait2(ms) {
13184
- return new Promise((resolve) => setTimeout(resolve, ms));
13603
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
13185
13604
  }
13186
13605
  function formatEndpoint2(endpoint) {
13187
13606
  if (!endpoint) return void 0;
@@ -13426,6 +13845,7 @@ var DirectTraceShipper = class {
13426
13845
  */
13427
13846
  this.hasShutdown = false;
13428
13847
  var _a, _b, _c, _d, _e, _f;
13848
+ this.appGitSnapshot = opts.appGitSnapshot;
13429
13849
  this.writeKey = opts.writeKey;
13430
13850
  this.baseUrl = (_a = formatEndpoint2(opts.endpoint)) != null ? _a : "https://api.raindrop.ai/v1/";
13431
13851
  this.enabled = (_b = opts.enabled) != null ? _b : true;
@@ -13440,6 +13860,14 @@ var DirectTraceShipper = class {
13440
13860
  this.localDebuggerUrl = resolved;
13441
13861
  }
13442
13862
  }
13863
+ applicationGitSnapshot(properties) {
13864
+ var _a, _b, _c, _d;
13865
+ try {
13866
+ return (_d = (_c = (_a = this.appGitSnapshot) == null ? void 0 : _a.call(this, properties)) != null ? _c : (_b = currentRoutingBinding()) == null ? void 0 : _b.appGit) != null ? _d : {};
13867
+ } catch (e) {
13868
+ return {};
13869
+ }
13870
+ }
13443
13871
  /**
13444
13872
  * Build and enqueue an OTLP span for a trackTool call.
13445
13873
  *
@@ -13514,7 +13942,10 @@ var DirectTraceShipper = class {
13514
13942
  name: params.name,
13515
13943
  startTimeUnixNano,
13516
13944
  endTimeUnixNano,
13517
- attributes,
13945
+ attributes: withAppGitAttributes(
13946
+ attributes,
13947
+ params.appGitSnapshot !== void 0 ? params.appGitSnapshot : this.applicationGitSnapshot(params.properties)
13948
+ ),
13518
13949
  status
13519
13950
  });
13520
13951
  const replayDestination = currentReplayTraceDestination();
@@ -15135,7 +15566,7 @@ var LiveInteraction = class {
15135
15566
  span.end();
15136
15567
  }
15137
15568
  async finish(resultEvent) {
15138
- var _a, _b, _c, _d, _e, _f, _g;
15569
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
15139
15570
  unbindRoutingContext(this.boundContext);
15140
15571
  this.boundContext = void 0;
15141
15572
  unbindSpanAttributes(this.toolEventsFrame);
@@ -15145,10 +15576,19 @@ var LiveInteraction = class {
15145
15576
  }
15146
15577
  const eventId = resultEvent.eventId || this.context.eventId;
15147
15578
  if (!this.usageEmitted) {
15148
- (_c = this.analytics) == null ? void 0 : _c._recordModelUsage(
15579
+ const appGitSnapshot = (_b = (_a = this.analytics) == null ? void 0 : _a.getAppGitSnapshot(resultEvent.properties, eventId)) != null ? _b : {};
15580
+ const usageProperties = {};
15581
+ for (const key of [APP_COMMIT_SHA, APP_COMMIT_DIRTY, APP_BRANCH]) {
15582
+ if (resultEvent.properties && Object.prototype.hasOwnProperty.call(resultEvent.properties, key)) {
15583
+ usageProperties[key] = resultEvent.properties[key];
15584
+ }
15585
+ }
15586
+ (_e = this.analytics) == null ? void 0 : _e._recordModelUsage(
15149
15587
  eventId,
15150
- (_a = resultEvent.model) != null ? _a : this.context.model,
15151
- (_b = resultEvent.usage) != null ? _b : this.context.usage
15588
+ (_c = resultEvent.model) != null ? _c : this.context.model,
15589
+ (_d = resultEvent.usage) != null ? _d : this.context.usage,
15590
+ withAppGitProperties(usageProperties, appGitSnapshot),
15591
+ appGitSnapshot
15152
15592
  );
15153
15593
  this.usageEmitted = true;
15154
15594
  }
@@ -15159,20 +15599,20 @@ var LiveInteraction = class {
15159
15599
  } catch (e) {
15160
15600
  }
15161
15601
  })();
15162
- if ((_e = (_d = this.analytics) == null ? void 0 : _d._shouldBlockFinishFlush()) != null ? _e : true) {
15602
+ if ((_g = (_f = this.analytics) == null ? void 0 : _f._shouldBlockFinishFlush()) != null ? _g : true) {
15163
15603
  await spanFlush;
15164
15604
  } else {
15165
15605
  void spanFlush.catch(() => {
15166
15606
  });
15167
15607
  }
15168
15608
  try {
15169
- await ((_f = this.analytics) == null ? void 0 : _f._drainInFlightPartials(eventId));
15609
+ await ((_h = this.analytics) == null ? void 0 : _h._drainInFlightPartials(eventId));
15170
15610
  } catch (e) {
15171
15611
  }
15172
15612
  if (this.traceId) {
15173
15613
  this.setProperty("trace_id", this.traceId);
15174
15614
  }
15175
- await ((_g = this.analytics) == null ? void 0 : _g._trackAiPartial({
15615
+ await ((_i = this.analytics) == null ? void 0 : _i._trackAiPartial({
15176
15616
  ...resultEvent,
15177
15617
  eventId,
15178
15618
  isPending: false
@@ -15979,6 +16419,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
15979
16419
  }
15980
16420
  }
15981
16421
  const directShipper = bypassOtelForTools ? new DirectTraceShipper({
16422
+ appGitSnapshot: (properties) => analytics.getAppGitSnapshot(properties),
15982
16423
  writeKey,
15983
16424
  endpoint: directEndpoint != null ? directEndpoint : apiUrl,
15984
16425
  debug: isDebug,
@@ -16038,7 +16479,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
16038
16479
  tracingEnabled: tracingEnabled !== false,
16039
16480
  disableBatch: disableBatching,
16040
16481
  processor: new CompositeSpanProcessor(
16041
- [new RaindropContextSpanProcessor(), localDebuggerProcessor],
16482
+ [new RaindropContextSpanProcessor((properties) => analytics.getAppGitSnapshot(properties)), localDebuggerProcessor],
16042
16483
  []
16043
16484
  ),
16044
16485
  instrumentations: createConfiguredInstrumentations(otherOptions)
@@ -16062,7 +16503,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
16062
16503
  tracingEnabled: tracingEnabled !== false,
16063
16504
  disableBatch: disableBatching,
16064
16505
  processor: new CompositeSpanProcessor(
16065
- [new RaindropContextSpanProcessor(), replayOnlyProcessor],
16506
+ [new RaindropContextSpanProcessor((properties) => analytics.getAppGitSnapshot(properties)), replayOnlyProcessor],
16066
16507
  []
16067
16508
  ),
16068
16509
  instrumentations: createConfiguredInstrumentations(otherOptions)
@@ -16103,7 +16544,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
16103
16544
  );
16104
16545
  }
16105
16546
  if (guardedExporter) {
16106
- const contextProcessor = new RaindropContextSpanProcessor();
16547
+ const contextProcessor = new RaindropContextSpanProcessor((properties) => analytics.getAppGitSnapshot(properties));
16107
16548
  const callerProcessor = otherOptions.processor;
16108
16549
  const processor = new CompositeSpanProcessor(
16109
16550
  callerProcessor ? [contextProcessor, callerProcessor] : [contextProcessor],
@@ -16166,9 +16607,9 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
16166
16607
  traceId,
16167
16608
  analytics,
16168
16609
  directShipper,
16169
- { projectId, authHint }
16610
+ { projectId, authHint, appGit: analytics.getAppGitSnapshot(traceContext.properties, traceContext.eventId) }
16170
16611
  );
16171
- const bound = bindRoutingContext({ projectId, authHint }, interaction);
16612
+ const bound = bindRoutingContext({ projectId, authHint, appGit: analytics.getAppGitSnapshot(traceContext.properties, traceContext.eventId) }, interaction);
16172
16613
  interaction.setBoundContext(bound);
16173
16614
  try {
16174
16615
  analytics._trackAiPartial({
@@ -16245,7 +16686,8 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
16245
16686
  directShipper.sendSpan({
16246
16687
  name: "ai.model.usage",
16247
16688
  spanKind: "llm",
16248
- properties: { event_id: params.eventId },
16689
+ properties: { ...params.appGitProperties, event_id: params.eventId },
16690
+ appGitSnapshot: params.appGitSnapshot,
16249
16691
  startTimeMs: timeMs,
16250
16692
  endTimeMs: timeMs,
16251
16693
  durationMs: 0,
@@ -16253,9 +16695,23 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
16253
16695
  });
16254
16696
  return;
16255
16697
  }
16256
- const span = import_api6.trace.getTracer("traceloop.tracer").startSpan("ai.model.usage");
16257
- span.setAttribute("traceloop.span.kind", "llm");
16258
- span.setAttribute("traceloop.association.properties.event_id", params.eventId);
16698
+ const initialAttributes = {
16699
+ "traceloop.span.kind": "llm"
16700
+ };
16701
+ for (const [key, value] of Object.entries({
16702
+ ...params.appGitProperties,
16703
+ event_id: params.eventId
16704
+ })) {
16705
+ if (value !== void 0) {
16706
+ initialAttributes[`traceloop.association.properties.${key}`] = stringifyBounded(value);
16707
+ }
16708
+ }
16709
+ for (const [key, value] of Object.entries(params.appGitSnapshot)) {
16710
+ if (typeof value === "string" || typeof value === "boolean") {
16711
+ initialAttributes[key] = value;
16712
+ }
16713
+ }
16714
+ const span = import_api6.trace.getTracer("traceloop.tracer").startSpan("ai.model.usage", { attributes: initialAttributes });
16259
16715
  stampSpan(span, projectId, authHint);
16260
16716
  for (const attribute of attributes) {
16261
16717
  const value = attribute.value;
@@ -16376,7 +16832,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
16376
16832
  allowedInstrumentationLibraries: processorOptions == null ? void 0 : processorOptions.allowedInstrumentationLibraries,
16377
16833
  localDebuggerUrlOpt: localDebuggerUrl
16378
16834
  });
16379
- const contextProcessor = new RaindropContextSpanProcessor();
16835
+ const contextProcessor = new RaindropContextSpanProcessor((properties) => analytics.getAppGitSnapshot(properties));
16380
16836
  return withV2SpanCompat(
16381
16837
  new CompositeSpanProcessor(
16382
16838
  [contextProcessor, productionProcessor],
@@ -16613,32 +17069,6 @@ function replayIngestEndpoint(value, trustedUrl) {
16613
17069
  return url.href;
16614
17070
  }
16615
17071
 
16616
- // src/replay/git.ts
16617
- var import_node_child_process = require("child_process");
16618
- function readGitCommit() {
16619
- const sha = git(["rev-parse", "HEAD"]);
16620
- if (sha === null) return null;
16621
- const branch = git(["rev-parse", "--abbrev-ref", "HEAD"]);
16622
- const status = git(["status", "--porcelain"]);
16623
- if (status === null) return null;
16624
- return {
16625
- sha,
16626
- branch: branch === null || branch === "HEAD" ? null : branch,
16627
- dirty: status !== ""
16628
- };
16629
- }
16630
- function git(args) {
16631
- try {
16632
- return (0, import_node_child_process.execFileSync)("git", [...args], {
16633
- encoding: "utf8",
16634
- stdio: ["ignore", "pipe", "ignore"],
16635
- timeout: 2e3
16636
- }).trim();
16637
- } catch (e) {
16638
- return null;
16639
- }
16640
- }
16641
-
16642
17072
  // src/replay/index.ts
16643
17073
  var DEFAULT_QUERY_URL = "https://query.raindrop.ai";
16644
17074
  var DEFAULT_TRACE_WAIT_MS = 3e4;
@@ -17030,6 +17460,47 @@ var WireEvalCaseTracePairReadSchema = import_zod5.z.discriminatedUnion("state",
17030
17460
  reason: import_zod5.z.string()
17031
17461
  })
17032
17462
  ]);
17463
+ var replayGitSnapshots = /* @__PURE__ */ new WeakMap();
17464
+ var replayPropertySnapshots = /* @__PURE__ */ new WeakMap();
17465
+ var replayOverrideSnapshots = /* @__PURE__ */ new WeakSet();
17466
+ function withReplayGit(snapshot, run) {
17467
+ var _a;
17468
+ return withRoutingContext({
17469
+ ...currentRoutingBinding(),
17470
+ appGit: (_a = replayPropertySnapshots.get(snapshot)) != null ? _a : snapshot,
17471
+ appGitSource: replayOverrideSnapshots.has(snapshot) ? "replay-override" : void 0
17472
+ }, run);
17473
+ }
17474
+ function captureReplayGit(client, options) {
17475
+ var _a, _b;
17476
+ try {
17477
+ if (options === void 0 && client.getAppGitSnapshot) {
17478
+ const properties = Object.freeze({ ...client.getAppGitSnapshot() });
17479
+ const snapshot2 = Object.freeze({ ...(_b = (_a = client.getReplayAppGitSnapshot) == null ? void 0 : _a.call(client)) != null ? _b : properties });
17480
+ replayPropertySnapshots.set(snapshot2, properties);
17481
+ return snapshot2;
17482
+ }
17483
+ const resolver = createAppGitContext(options, { allowLocalGit: false });
17484
+ const snapshot = resolver.snapshot();
17485
+ resolver.dispose();
17486
+ if (options && typeof options === "object") replayOverrideSnapshots.add(snapshot);
17487
+ return snapshot;
17488
+ } catch (e) {
17489
+ return {};
17490
+ }
17491
+ }
17492
+ function replayGitFields(snapshot) {
17493
+ const rawSha = snapshot[APP_COMMIT_SHA];
17494
+ const sha = typeof rawSha === "string" && validAppCommitSha(rawSha) ? rawSha.trim() : void 0;
17495
+ const dirty = snapshot[APP_COMMIT_DIRTY];
17496
+ const rawBranch = snapshot[APP_BRANCH];
17497
+ const branch = typeof rawBranch === "string" && rawBranch.length <= 1024 ? rawBranch.trim() : "";
17498
+ return {
17499
+ commit_sha: sha != null ? sha : null,
17500
+ commit_dirty: sha && typeof dirty === "boolean" ? dirty : null,
17501
+ commit_branch: branch.length > 0 && branch.length <= 400 ? branch : null
17502
+ };
17503
+ }
17033
17504
  function toReading(wire) {
17034
17505
  return {
17035
17506
  replayId: wire.replay.id,
@@ -17047,11 +17518,11 @@ function toReading(wire) {
17047
17518
  };
17048
17519
  }
17049
17520
  async function createReplay(client, options) {
17050
- var _a, _b, _c, _d;
17521
+ var _a;
17522
+ const gitSnapshot = captureReplayGit(client, options.appGit);
17051
17523
  const queryUrl = (_a = options.queryUrl) != null ? _a : DEFAULT_QUERY_URL;
17052
17524
  const manifest = typeof options.dataset === "string" ? await readEvalDataset(client, options.dataset, { queryUrl }) : validateEvalDatasetManifest(options.dataset);
17053
17525
  const rowIds = selectEvalDatasetRows(manifest, options.rowIds);
17054
- const commit = readGitCommit();
17055
17526
  const created = await queryApi({
17056
17527
  client,
17057
17528
  queryUrl,
@@ -17064,9 +17535,7 @@ async function createReplay(client, options) {
17064
17535
  dataset_version_id: manifest.version.id,
17065
17536
  dataset_fingerprint: manifest.version.fingerprint,
17066
17537
  request_key: randomUUID(),
17067
- commit_sha: (_b = commit == null ? void 0 : commit.sha) != null ? _b : null,
17068
- commit_branch: (_c = commit == null ? void 0 : commit.branch) != null ? _c : null,
17069
- commit_dirty: (_d = commit == null ? void 0 : commit.dirty) != null ? _d : null,
17538
+ ...replayGitFields(gitSnapshot),
17070
17539
  row_ids: rowIds
17071
17540
  }
17072
17541
  });
@@ -17078,6 +17547,7 @@ async function createReplay(client, options) {
17078
17547
  }
17079
17548
  };
17080
17549
  assertSelectedRows(handle, rowIds);
17550
+ replayGitSnapshots.set(handle, gitSnapshot);
17081
17551
  return handle;
17082
17552
  }
17083
17553
  function selectEvalDatasetRows(manifest, requestedRowIds) {
@@ -17126,15 +17596,19 @@ function toHandle(wire) {
17126
17596
  }
17127
17597
  async function claimReplay(client, replayId, options) {
17128
17598
  var _a;
17129
- return toHandle(
17599
+ const gitSnapshot = captureReplayGit(client, options == null ? void 0 : options.appGit);
17600
+ const handle = toHandle(
17130
17601
  await queryApi({
17131
17602
  client,
17132
17603
  queryUrl: (_a = options == null ? void 0 : options.queryUrl) != null ? _a : DEFAULT_QUERY_URL,
17133
17604
  path: `/v1/replays/${encodeURIComponent(replayId)}/claim`,
17134
17605
  method: "POST",
17135
- schema: WirePlanSchema
17606
+ schema: WirePlanSchema,
17607
+ body: replayGitFields(gitSnapshot)
17136
17608
  })
17137
17609
  );
17610
+ replayGitSnapshots.set(handle, gitSnapshot);
17611
+ return handle;
17138
17612
  }
17139
17613
  async function readReplay(client, replayId, options) {
17140
17614
  var _a;
@@ -17210,7 +17684,7 @@ async function runReplay(client, handle, options) {
17210
17684
  return (await runReplayRows(client, handle, options)).reading;
17211
17685
  }
17212
17686
  async function runReplayRows(client, handle, options) {
17213
- var _a, _b, _c, _d, _e, _f, _g;
17687
+ var _a, _b, _c, _d, _e, _f, _g, _h;
17214
17688
  if (!Number.isSafeInteger((_a = options.concurrency) != null ? _a : DEFAULT_CONCURRENCY) || ((_b = options.concurrency) != null ? _b : DEFAULT_CONCURRENCY) < 1) {
17215
17689
  throw new Error("Raindrop replay concurrency must be a positive safe integer");
17216
17690
  }
@@ -17231,7 +17705,9 @@ async function runReplayRows(client, handle, options) {
17231
17705
  body,
17232
17706
  maxRetries: MAX_REPLAY_REPORT_RETRIES
17233
17707
  });
17234
- await report({ type: "started" });
17708
+ const gitSnapshot = (_e = replayGitSnapshots.get(handle)) != null ? _e : captureReplayGit(client);
17709
+ replayGitSnapshots.set(handle, gitSnapshot);
17710
+ await report({ type: "started", ...replayGitFields(gitSnapshot) });
17235
17711
  const rows = [...handle.rows];
17236
17712
  const results = /* @__PURE__ */ new Map();
17237
17713
  const captures = /* @__PURE__ */ new Map();
@@ -17255,7 +17731,7 @@ async function runReplayRows(client, handle, options) {
17255
17731
  exportedSpanIds: spanIds,
17256
17732
  exportedTraceIds: traceIds
17257
17733
  };
17258
- const result = await import_api7.context.with(
17734
+ const result = await withReplayGit(gitSnapshot, () => import_api7.context.with(
17259
17735
  import_api7.trace.deleteSpan(import_api7.context.active()),
17260
17736
  () => withReplayTraceDestination(
17261
17737
  replayDestination,
@@ -17282,7 +17758,7 @@ async function runReplayRows(client, handle, options) {
17282
17758
  )
17283
17759
  )
17284
17760
  )
17285
- );
17761
+ ));
17286
17762
  results.set(row.id, { value: result });
17287
17763
  } catch (cause) {
17288
17764
  const error = describeError(cause);
@@ -17313,7 +17789,7 @@ async function runReplayRows(client, handle, options) {
17313
17789
  };
17314
17790
  await Promise.all(
17315
17791
  Array.from(
17316
- { length: Math.min((_e = options.concurrency) != null ? _e : DEFAULT_CONCURRENCY, rows.length) },
17792
+ { length: Math.min((_f = options.concurrency) != null ? _f : DEFAULT_CONCURRENCY, rows.length) },
17317
17793
  () => worker()
17318
17794
  )
17319
17795
  );
@@ -17339,7 +17815,7 @@ async function runReplayRows(client, handle, options) {
17339
17815
  client,
17340
17816
  queryUrl,
17341
17817
  replayId: handle.replayId,
17342
- deadlineAt: Date.now() + ((_f = options.traceWaitMs) != null ? _f : DEFAULT_TRACE_WAIT_MS)
17818
+ deadlineAt: Date.now() + ((_g = options.traceWaitMs) != null ? _g : DEFAULT_TRACE_WAIT_MS)
17343
17819
  });
17344
17820
  for (const row of settled.rows) {
17345
17821
  if (row.status !== "pending") continue;
@@ -17348,7 +17824,7 @@ async function runReplayRows(client, handle, options) {
17348
17824
  row_id: row.rowId,
17349
17825
  attempt: row.attempt,
17350
17826
  error: `no trace arrived for this row within ${Math.round(
17351
- ((_g = options.traceWaitMs) != null ? _g : DEFAULT_TRACE_WAIT_MS) / 1e3
17827
+ ((_h = options.traceWaitMs) != null ? _h : DEFAULT_TRACE_WAIT_MS) / 1e3
17352
17828
  )}s`
17353
17829
  });
17354
17830
  }
@@ -17422,7 +17898,7 @@ async function readEvalCaseTracePair(input, evidence, attempt) {
17422
17898
  });
17423
17899
  }
17424
17900
  async function createLocalEvalReplaySession(client, options) {
17425
- var _a;
17901
+ var _a, _b, _c;
17426
17902
  if (options.evaluators.length === 0) {
17427
17903
  throw new Error("Raindrop local eval replay requires at least one evaluator");
17428
17904
  }
@@ -17443,7 +17919,8 @@ async function createLocalEvalReplaySession(client, options) {
17443
17919
  ...handle,
17444
17920
  ingestUrl: replayIngestEndpoint(requireReplayIngestUrl(handle), options.replayIngestUrl)
17445
17921
  };
17446
- await reportReplay(client, queryUrl, handle.replayId, { type: "started" });
17922
+ replayGitSnapshots.set(validatedHandle, (_b = replayGitSnapshots.get(handle)) != null ? _b : {});
17923
+ await reportReplay(client, queryUrl, handle.replayId, { type: "started", ...replayGitFields((_c = replayGitSnapshots.get(handle)) != null ? _c : {}) });
17447
17924
  const parents = await Promise.all(
17448
17925
  options.evaluators.map(async (evaluator) => {
17449
17926
  const registered = await registerLocalEvaluator(client, evaluator, queryUrl);
@@ -17488,7 +17965,7 @@ var LocalEvalReplaySessionImpl = class {
17488
17965
  );
17489
17966
  }
17490
17967
  async run(input) {
17491
- var _a, _b, _c, _d;
17968
+ var _a, _b, _c, _d, _e;
17492
17969
  validateWait("traceWaitMs", (_a = input.traceWaitMs) != null ? _a : DEFAULT_TRACE_WAIT_MS, true);
17493
17970
  if (this.state !== "open") {
17494
17971
  throw new Error(`Raindrop replay ${this.replayId} is already finishing`);
@@ -17514,30 +17991,33 @@ var LocalEvalReplaySessionImpl = class {
17514
17991
  exportedTraceIds: traceIds
17515
17992
  };
17516
17993
  try {
17517
- result = await import_api7.context.with(
17518
- import_api7.trace.deleteSpan(import_api7.context.active()),
17519
- () => withReplayTraceDestination(
17520
- replayDestination,
17521
- () => withEvalScope(
17522
- { correlationId },
17523
- () => withTask(
17524
- { name: "raindrop.replay.row", inputParameters: [row], traceContent: true },
17525
- () => {
17526
- var _a2;
17527
- const parent = (_a2 = import_api7.trace.getSpan(import_api7.context.active())) == null ? void 0 : _a2.spanContext();
17528
- if (!parent) return input.run(row);
17529
- return withReplayTraceDestination(
17530
- {
17531
- ...replayDestination,
17532
- parentSpanContext: {
17533
- traceIdB64: Buffer.from(parent.traceId, "hex").toString("base64"),
17534
- spanIdB64: Buffer.from(parent.spanId, "hex").toString("base64"),
17535
- eventId: parent.traceId
17536
- }
17537
- },
17538
- () => input.run(row)
17539
- );
17540
- }
17994
+ result = await withReplayGit(
17995
+ (_c = replayGitSnapshots.get(this.handle)) != null ? _c : {},
17996
+ () => import_api7.context.with(
17997
+ import_api7.trace.deleteSpan(import_api7.context.active()),
17998
+ () => withReplayTraceDestination(
17999
+ replayDestination,
18000
+ () => withEvalScope(
18001
+ { correlationId },
18002
+ () => withTask(
18003
+ { name: "raindrop.replay.row", inputParameters: [row], traceContent: true },
18004
+ () => {
18005
+ var _a2;
18006
+ const parent = (_a2 = import_api7.trace.getSpan(import_api7.context.active())) == null ? void 0 : _a2.spanContext();
18007
+ if (!parent) return input.run(row);
18008
+ return withReplayTraceDestination(
18009
+ {
18010
+ ...replayDestination,
18011
+ parentSpanContext: {
18012
+ traceIdB64: Buffer.from(parent.traceId, "hex").toString("base64"),
18013
+ spanIdB64: Buffer.from(parent.spanId, "hex").toString("base64"),
18014
+ eventId: parent.traceId
18015
+ }
18016
+ },
18017
+ () => input.run(row)
18018
+ );
18019
+ }
18020
+ )
17541
18021
  )
17542
18022
  )
17543
18023
  )
@@ -17561,7 +18041,7 @@ var LocalEvalReplaySessionImpl = class {
17561
18041
  attempt: input.attempt,
17562
18042
  traceIds,
17563
18043
  spanIds,
17564
- waitMs: (_c = input.traceWaitMs) != null ? _c : DEFAULT_TRACE_WAIT_MS
18044
+ waitMs: (_d = input.traceWaitMs) != null ? _d : DEFAULT_TRACE_WAIT_MS
17565
18045
  });
17566
18046
  } catch (retentionCause) {
17567
18047
  throw failedTraceRetentionError(cause, retentionCause, row.id);
@@ -17586,7 +18066,7 @@ var LocalEvalReplaySessionImpl = class {
17586
18066
  replayId: this.replayId,
17587
18067
  datasetVersion: this.handle.datasetVersion,
17588
18068
  evidence: [evidence],
17589
- waitMs: (_d = input.traceWaitMs) != null ? _d : DEFAULT_TRACE_WAIT_MS,
18069
+ waitMs: (_e = input.traceWaitMs) != null ? _e : DEFAULT_TRACE_WAIT_MS,
17590
18070
  attempt: input.attempt
17591
18071
  });
17592
18072
  const captured = pairs.get(row.id);
@@ -18215,7 +18695,7 @@ function failedTraceRetentionError(taskCause, retentionCause, rowId) {
18215
18695
  );
18216
18696
  }
18217
18697
  function sleep(ms) {
18218
- return new Promise((resolve) => setTimeout(resolve, ms));
18698
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
18219
18699
  }
18220
18700
  function isRetryableStatus(status) {
18221
18701
  return status === 408 || status === 429 || status >= 500;
@@ -18445,7 +18925,7 @@ function isRetryableStatus2(status) {
18445
18925
  return status === 408 || status === 429 || status >= 500;
18446
18926
  }
18447
18927
  function sleep2(ms) {
18448
- return new Promise((resolve) => setTimeout(resolve, ms));
18928
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
18449
18929
  }
18450
18930
 
18451
18931
  // src/evals/publish.ts
@@ -19003,7 +19483,7 @@ async function readWorkshopTrace(input) {
19003
19483
  throw new Error(`Workshop trace ${input.correlationId} remained ${state2}`);
19004
19484
  }
19005
19485
  await new Promise(
19006
- (resolve) => setTimeout(resolve, Math.min(50, Math.max(0, deadlineAt - Date.now())))
19486
+ (resolve2) => setTimeout(resolve2, Math.min(50, Math.max(0, deadlineAt - Date.now())))
19007
19487
  );
19008
19488
  }
19009
19489
  }
@@ -19309,7 +19789,7 @@ var RaindropRowEvalRun = class {
19309
19789
  var _a;
19310
19790
  const limit = (_a = this.definition.concurrency) != null ? _a : 4;
19311
19791
  if (this.runningRows >= limit) {
19312
- await new Promise((resolve) => this.rowWaiters.push(resolve));
19792
+ await new Promise((resolve2) => this.rowWaiters.push(resolve2));
19313
19793
  } else {
19314
19794
  this.runningRows += 1;
19315
19795
  }
@@ -19593,7 +20073,7 @@ var WorkshopEvalRun = class {
19593
20073
  var _a;
19594
20074
  const limit = (_a = this.definition.concurrency) != null ? _a : 4;
19595
20075
  if (this.runningRows >= limit) {
19596
- await new Promise((resolve) => this.rowWaiters.push(resolve));
20076
+ await new Promise((resolve2) => this.rowWaiters.push(resolve2));
19597
20077
  } else {
19598
20078
  this.runningRows += 1;
19599
20079
  }
@@ -19989,6 +20469,15 @@ function isPortableEvaluator(value) {
19989
20469
  }
19990
20470
 
19991
20471
  // src/index.ts
20472
+ function rawAppGitProperties(properties) {
20473
+ const result = {};
20474
+ for (const key of [APP_COMMIT_SHA, APP_COMMIT_DIRTY, APP_BRANCH]) {
20475
+ if (properties && Object.prototype.hasOwnProperty.call(properties, key)) {
20476
+ result[key] = properties[key];
20477
+ }
20478
+ }
20479
+ return result;
20480
+ }
19992
20481
  var MAX_INGEST_SIZE_BYTES = 1 * 1024 * 1024;
19993
20482
  var SELF_DIAGNOSTICS_TOOL_NAME_DEFAULT = "__raindrop_report";
19994
20483
  var SELF_DIAGNOSTICS_SOURCE_DEFAULT = "agent_reporting_tool";
@@ -20096,7 +20585,7 @@ function resolveEventIdFromMetadata(metadata) {
20096
20585
  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"]);
20097
20586
  }
20098
20587
  async function wait3(ms) {
20099
- return new Promise((resolve) => setTimeout(resolve, ms));
20588
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
20100
20589
  }
20101
20590
  function parseRetryAfter3(headers) {
20102
20591
  var _a;
@@ -20171,18 +20660,23 @@ var Raindrop = class {
20171
20660
  */
20172
20661
  this.hasShutdown = false;
20173
20662
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
20663
+ const writeKey = (_a = config.writeKey) != null ? _a : "";
20664
+ const disabled = (_b = config.disabled) != null ? _b : false;
20665
+ const localDebuggerUrlOpt = config.localWorkshopUrl === false ? null : config.localWorkshopUrl;
20666
+ const apiUrl = (_c = this.formatEndpoint(config.endpoint)) != null ? _c : `https://api.raindrop.ai/v1/`;
20667
+ const url = new URL(apiUrl);
20174
20668
  setDefaultMaxTextFieldChars(config.maxTextFieldChars);
20175
20669
  this.maxTextFieldChars = resolveMaxTextFieldChars(config.maxTextFieldChars);
20176
- this.writeKey = (_a = config.writeKey) != null ? _a : "";
20670
+ this.writeKey = writeKey;
20177
20671
  this.apiKey = config.apiKey;
20178
20672
  this.authHint = authHintForKey(this.writeKey);
20179
- this.localDebuggerUrlOpt = config.localWorkshopUrl === false ? null : config.localWorkshopUrl;
20673
+ this.localDebuggerUrlOpt = localDebuggerUrlOpt;
20180
20674
  this.awaitSpanFlushOpt = config.awaitSpanFlush;
20181
- this.apiUrl = (_b = this.formatEndpoint(config.endpoint)) != null ? _b : `https://api.raindrop.ai/v1/`;
20182
- this.bufferSize = (_c = config.bufferSize) != null ? _c : 50;
20183
- this.bufferTimeout = (_d = config.bufferTimeout) != null ? _d : 1e3;
20675
+ this.apiUrl = apiUrl;
20676
+ this.bufferSize = (_d = config.bufferSize) != null ? _d : 50;
20677
+ this.bufferTimeout = (_e = config.bufferTimeout) != null ? _e : 1e3;
20184
20678
  this.buffer = new Array();
20185
- this.disabled = (_e = config.disabled) != null ? _e : false;
20679
+ this.disabled = disabled;
20186
20680
  this.wizardSession = (_f = config.wizardSession) != null ? _f : null;
20187
20681
  this.debugLogs = (_g = config.debugLogs) != null ? _g : false;
20188
20682
  this.projectId = normalizeProjectId(config.projectId, {
@@ -20200,34 +20694,43 @@ var Raindrop = class {
20200
20694
  );
20201
20695
  }
20202
20696
  const useExternalOtel = (_j = (_i = config.useExternalOtel) != null ? _i : config.disableTracing) != null ? _j : false;
20203
- const url = new URL(this.apiUrl);
20204
20697
  const disableBatching = resolveDisableBatching(config.disableBatching, {
20205
20698
  isProduction: process.env.NODE_ENV === "production",
20206
20699
  isLocalDebugger: localDebuggerEnabled(this.localDebuggerUrlOpt)
20207
20700
  });
20208
- this._tracing = tracing(
20209
- this,
20210
- url.origin,
20211
- this.writeKey,
20212
- {
20213
- disableBatch: disableBatching,
20214
- disableBatching,
20215
- logLevel: this.debugLogs ? "debug" : "error",
20216
- // Pass through disabled state - tracer-core handles no-op exporter logic
20217
- tracingEnabled: !this.disabled,
20218
- useExternalOtel,
20219
- instrumentModules: config.instrumentModules,
20220
- bypassOtelForTools: config.bypassOtelForTools,
20221
- // Pass full API URL (with /v1/ path) for DirectTraceShipper.
20222
- // url.origin strips the path, which traceloop handles internally,
20223
- // but the direct shipper needs the full path to post to /v1/traces.
20224
- directEndpoint: this.apiUrl,
20225
- localDebuggerUrl: this.localDebuggerUrlOpt,
20226
- cloudEnabled: !this.localOnly,
20227
- projectId: this.projectId
20228
- },
20229
- this.debugLogs
20230
- );
20701
+ const hasDestination = !this.disabled && (this.writeKey.trim().length > 0 || localDebuggerEnabled(this.localDebuggerUrlOpt));
20702
+ this.appGitContext = createAppGitContext(this.disabled ? false : config.appGit, {
20703
+ captureLegacyReplayBranch: true,
20704
+ allowDiscovery: hasDestination
20705
+ });
20706
+ try {
20707
+ this._tracing = tracing(
20708
+ this,
20709
+ url.origin,
20710
+ this.writeKey,
20711
+ {
20712
+ disableBatch: disableBatching,
20713
+ disableBatching,
20714
+ logLevel: this.debugLogs ? "debug" : "error",
20715
+ // Pass through disabled state - tracer-core handles no-op exporter logic
20716
+ tracingEnabled: !this.disabled,
20717
+ useExternalOtel,
20718
+ instrumentModules: config.instrumentModules,
20719
+ bypassOtelForTools: config.bypassOtelForTools,
20720
+ // Pass full API URL (with /v1/ path) for DirectTraceShipper.
20721
+ // url.origin strips the path, which traceloop handles internally,
20722
+ // but the direct shipper needs the full path to post to /v1/traces.
20723
+ directEndpoint: this.apiUrl,
20724
+ localDebuggerUrl: this.localDebuggerUrlOpt,
20725
+ cloudEnabled: !this.localOnly,
20726
+ projectId: this.projectId
20727
+ },
20728
+ this.debugLogs
20729
+ );
20730
+ } catch (error) {
20731
+ this.appGitContext.dispose();
20732
+ throw error;
20733
+ }
20231
20734
  }
20232
20735
  get localOnly() {
20233
20736
  return !this.writeKey;
@@ -20268,7 +20771,29 @@ var Raindrop = class {
20268
20771
  * @returns The interaction object.
20269
20772
  */
20270
20773
  begin(traceContext) {
20271
- return this._tracing.begin(traceContext);
20774
+ var _a;
20775
+ const eventId = (_a = traceContext.eventId) != null ? _a : crypto.randomUUID();
20776
+ return this._tracing.begin({
20777
+ ...traceContext,
20778
+ eventId,
20779
+ properties: this.propertiesWithAppGit(traceContext.properties, eventId)
20780
+ });
20781
+ }
20782
+ /** @internal Snapshot only; this never waits for optional Git discovery. */
20783
+ getAppGitSnapshot(properties, eventId) {
20784
+ return this.appGitContext.forOperation(eventId, properties);
20785
+ }
20786
+ /** @internal Existing replay branch reporting; ordinary properties remain opt-in. */
20787
+ getReplayAppGitSnapshot() {
20788
+ return this.appGitContext.snapshotForReplay();
20789
+ }
20790
+ propertiesWithAppGit(properties, eventId) {
20791
+ const result = { ...properties };
20792
+ for (const [key, value] of Object.entries(this.getAppGitSnapshot(properties, eventId))) {
20793
+ if (Object.prototype.hasOwnProperty.call(result, key)) continue;
20794
+ if (value === null || typeof value === "string" || typeof value === "boolean") result[key] = value;
20795
+ }
20796
+ return result;
20272
20797
  }
20273
20798
  /**
20274
20799
  * Returns a tracer object that can be used to create spans and tools that aren't tied to any interaction.
@@ -20295,7 +20820,7 @@ var Raindrop = class {
20295
20820
  * ```
20296
20821
  */
20297
20822
  asCurrent(fn) {
20298
- return withRoutingContext({ projectId: this.projectId, authHint: this.authHint }, fn);
20823
+ return withRoutingContext({ projectId: this.projectId, authHint: this.authHint, appGit: this.appGitContext.snapshot() }, fn);
20299
20824
  }
20300
20825
  createSpanProcessor(processorOptions) {
20301
20826
  return this._tracing.createSpanProcessor(processorOptions);
@@ -20501,7 +21026,6 @@ var Raindrop = class {
20501
21026
  const destination = currentReplayTraceDestination();
20502
21027
  const cloudReplay = destination !== void 0 && destination.kind !== "workshop";
20503
21028
  const addEvent = (e) => {
20504
- var _a;
20505
21029
  const {
20506
21030
  eventId,
20507
21031
  userId,
@@ -20547,28 +21071,38 @@ var Raindrop = class {
20547
21071
  return;
20548
21072
  }
20549
21073
  const toSave = this.redactPii ? redactPII(parsed.data) : parsed.data;
20550
- toSave.properties = {
20551
- ...(_a = toSave.properties) != null ? _a : {},
20552
- ...this.wizardSession ? { "raindrop.wizardSession": this.wizardSession } : {},
20553
- // Last, so a caller-supplied property cannot shadow the replay row the
20554
- // event belongs to.
20555
- ...evalScopeAttributes(),
20556
- $context: this.context
20557
- };
20558
- const size = this.getSize(toSave);
20559
- if (size > MAX_INGEST_SIZE_BYTES) {
20560
- console.warn(
20561
- `[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`
20562
- );
20563
- }
20564
- if (!cloudReplay) {
20565
- this.saveToBuffer({
20566
- type: "ai",
20567
- ...toSave
20568
- });
21074
+ const appGitSnapshot = this.getAppGitSnapshot(toSave.properties, customEventId);
21075
+ const eventProperties = withAppGitProperties(toSave.properties, appGitSnapshot);
21076
+ const usageProperties = withAppGitProperties(
21077
+ rawAppGitProperties(toSave.properties),
21078
+ appGitSnapshot
21079
+ );
21080
+ try {
21081
+ toSave.properties = {
21082
+ ...eventProperties,
21083
+ ...this.wizardSession ? { "raindrop.wizardSession": this.wizardSession } : {},
21084
+ // Last, so a caller-supplied property cannot shadow the replay row the
21085
+ // event belongs to.
21086
+ ...evalScopeAttributes(),
21087
+ $context: this.context
21088
+ };
21089
+ const size = this.getSize(toSave);
21090
+ if (size > MAX_INGEST_SIZE_BYTES) {
21091
+ console.warn(
21092
+ `[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`
21093
+ );
21094
+ }
21095
+ if (!cloudReplay) {
21096
+ this.saveToBuffer({
21097
+ type: "ai",
21098
+ ...toSave
21099
+ });
21100
+ }
21101
+ this._recordModelUsage(customEventId, model, usage, usageProperties, appGitSnapshot);
21102
+ return customEventId;
21103
+ } finally {
21104
+ this.appGitContext.finishOperation(customEventId);
20569
21105
  }
20570
- this._recordModelUsage(customEventId, model, usage);
20571
- return customEventId;
20572
21106
  };
20573
21107
  if (Array.isArray(event)) {
20574
21108
  return event.map(addEvent);
@@ -20883,6 +21417,7 @@ var Raindrop = class {
20883
21417
  return Promise.resolve();
20884
21418
  }
20885
21419
  const { eventId, isPending, ...rest } = event;
21420
+ rest.properties = this.propertiesWithAppGit(rest.properties, eventId);
20886
21421
  if (!eventId) {
20887
21422
  console.warn("[raindrop] trackAiPartial requires an eventId.");
20888
21423
  return Promise.resolve();
@@ -20894,6 +21429,7 @@ var Raindrop = class {
20894
21429
  if (replayOnly) {
20895
21430
  if (isPending === false) {
20896
21431
  this.partialEventBuffer.delete(eventId);
21432
+ this.appGitContext.finishOperation(eventId);
20897
21433
  } else if (!captured) {
20898
21434
  this.partialEventBuffer.set(eventId, {
20899
21435
  replayOnly: true
@@ -20998,6 +21534,7 @@ var Raindrop = class {
20998
21534
  });
20999
21535
  } else {
21000
21536
  this.partialEventBuffer.delete(eventId);
21537
+ this.appGitContext.finishOperation(eventId);
21001
21538
  }
21002
21539
  if (!accumulatedEvent) {
21003
21540
  if (this.debugLogs) {
@@ -21168,7 +21705,7 @@ var Raindrop = class {
21168
21705
  * Emit one model span carrying raw provider usage facts.
21169
21706
  * @internal
21170
21707
  */
21171
- _recordModelUsage(eventId, model, usage) {
21708
+ _recordModelUsage(eventId, model, usage, properties = {}, appGitSnapshot = this.getAppGitSnapshot(properties)) {
21172
21709
  if (usage === void 0) return;
21173
21710
  const parsed = AiTrackUsageSchema.safeParse(usage);
21174
21711
  if (!parsed.success) {
@@ -21184,7 +21721,9 @@ var Raindrop = class {
21184
21721
  this._tracing.recordModelUsage({
21185
21722
  eventId,
21186
21723
  model,
21187
- usage: parsed.data
21724
+ usage: parsed.data,
21725
+ appGitProperties: rawAppGitProperties(properties),
21726
+ appGitSnapshot
21188
21727
  });
21189
21728
  }
21190
21729
  /**
@@ -21216,6 +21755,7 @@ var Raindrop = class {
21216
21755
  return this.awaitSpanFlushOpt !== false;
21217
21756
  }
21218
21757
  async close() {
21758
+ this.appGitContext.dispose();
21219
21759
  this.hasShutdown = true;
21220
21760
  this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
21221
21761
  try {
@@ -21280,6 +21820,9 @@ var Raindrop = class {
21280
21820
  }
21281
21821
  };
21282
21822
  var index_default = Raindrop;
21823
+ var EVAL_CORRELATION_ID_ATTRIBUTE2 = EVAL_CORRELATION_ID_ATTRIBUTE;
21824
+ var currentEvalScope2 = currentEvalScope;
21825
+ var withEvalScope2 = withEvalScope;
21283
21826
  // Annotate the CommonJS export names for ESM import in node:
21284
21827
  0 && (module.exports = {
21285
21828
  BooleanThresholdSchema,