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.
@@ -79,1135 +79,1509 @@ function evalScopeAttributes() {
79
79
  return { [EVAL_CORRELATION_ID_ATTRIBUTE]: scope.correlationId };
80
80
  }
81
81
 
82
- // ../core/dist/chunk-T53LQAH7.js
83
- function normalizeFeatureFlags(input) {
84
- if (Array.isArray(input)) {
85
- return Object.fromEntries(input.map((name) => [name, "true"]));
86
- }
87
- return Object.fromEntries(
88
- Object.entries(input).map(([k, v]) => [k, String(v)])
89
- );
90
- }
91
- function getCrypto() {
92
- const c = globalThis.crypto;
93
- return c;
94
- }
95
- function randomBytes(length) {
96
- const cryptoObj = getCrypto();
97
- const out = new Uint8Array(length);
98
- if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
99
- cryptoObj.getRandomValues(out);
100
- return out;
101
- }
102
- for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
103
- return out;
104
- }
105
- function randomUUID() {
106
- const cryptoObj = getCrypto();
107
- if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
108
- return cryptoObj.randomUUID();
82
+ // ../core/dist/chunk-WJOR6M7H.js
83
+ var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
84
+ function state() {
85
+ const holder = globalThis;
86
+ let current = holder[STATE_KEY];
87
+ if (!current) {
88
+ let storage = null;
89
+ try {
90
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
91
+ if (Ctor) storage = new Ctor();
92
+ } catch (e) {
93
+ storage = null;
94
+ }
95
+ current = {
96
+ storage,
97
+ syncDestination: void 0,
98
+ spanDestinations: /* @__PURE__ */ new WeakMap()
99
+ };
100
+ holder[STATE_KEY] = current;
109
101
  }
110
- const b = randomBytes(16);
111
- b[6] = b[6] & 15 | 64;
112
- b[8] = b[8] & 63 | 128;
113
- const hex = [...b].map((x) => x.toString(16).padStart(2, "0")).join("");
114
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
102
+ return current;
115
103
  }
116
- var HANDOFF_ATTRIBUTE_SUFFIXES = {
117
- mode: "raindrop.handoff.mode",
118
- childEventId: "raindrop.handoff.childEventId",
119
- parentEventId: "raindrop.handoff.parentEventId",
120
- parentSpanId: "raindrop.handoff.parentSpanId",
121
- name: "raindrop.handoff.name",
122
- terminal: "raindrop.handoff.terminal",
123
- agentRole: "raindrop.agent.role"
124
- };
125
- var AI_SDK_METADATA_PREFIX = "ai.telemetry.metadata.";
126
- var DETACHED_MODE = "detached";
127
- var SUBAGENT_AGENT_ROLE = "subagent";
128
- var HANDOFF_TERMINAL_CANCELLED = "cancelled";
129
- var TOOL_EVENTS_SUFFIX = "raindrop.toolEvents";
130
- var TOOL_EVENTS_ALLOW = "allow";
131
- var OPERATION_ID_ATTRIBUTE = "ai.operationId";
132
- var SUBAGENT_ABORT_OPERATION_ID = "ai.subagent.failed";
133
- var SUBAGENT_ABORT_SPAN_NAME = SUBAGENT_ABORT_OPERATION_ID;
134
- var DEFAULT_DISPATCH_SPAN_NAME = "launch_subagent";
135
- var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
136
- function defined(entries) {
137
- const out = {};
138
- for (const [key, value] of Object.entries(entries)) {
139
- if (typeof value === "string" && value.trim().length > 0) out[key] = value;
104
+ function withReplayTraceDestination(destination, callback) {
105
+ const routing = state();
106
+ if (routing.storage) return routing.storage.run(destination, callback);
107
+ const previous = routing.syncDestination;
108
+ routing.syncDestination = destination;
109
+ try {
110
+ return callback();
111
+ } finally {
112
+ routing.syncDestination = previous;
140
113
  }
141
- return out;
142
- }
143
- function detachedDispatchMetadata(dispatch) {
144
- return defined({
145
- [HANDOFF_ATTRIBUTE_SUFFIXES.mode]: DETACHED_MODE,
146
- [HANDOFF_ATTRIBUTE_SUFFIXES.childEventId]: dispatch.childEventId,
147
- [HANDOFF_ATTRIBUTE_SUFFIXES.name]: dispatch.name
148
- });
149
- }
150
- function detachedChildMetadata(link) {
151
- return defined({
152
- [HANDOFF_ATTRIBUTE_SUFFIXES.parentEventId]: link.parentEventId,
153
- [HANDOFF_ATTRIBUTE_SUFFIXES.parentSpanId]: link.parentSpanId,
154
- [HANDOFF_ATTRIBUTE_SUFFIXES.name]: link.name,
155
- [HANDOFF_ATTRIBUTE_SUFFIXES.agentRole]: SUBAGENT_AGENT_ROLE
156
- });
157
114
  }
158
- function cancelledTerminalMetadata() {
159
- return { [HANDOFF_ATTRIBUTE_SUFFIXES.terminal]: HANDOFF_TERMINAL_CANCELLED };
115
+ function currentReplayTraceDestination() {
116
+ var _a, _b;
117
+ const routing = state();
118
+ return (_b = (_a = routing.storage) == null ? void 0 : _a.getStore()) != null ? _b : routing.syncDestination;
160
119
  }
161
- function cancelledTerminalAttributes() {
162
- const bare = cancelledTerminalMetadata();
163
- return { ...bare, ...withMetadataPrefix(bare) };
120
+ function rememberReplayTraceDestination(span) {
121
+ const destination = currentReplayTraceDestination();
122
+ if (destination) state().spanDestinations.set(span, destination);
164
123
  }
165
- function toolEventsAllowMetadata() {
166
- return { [TOOL_EVENTS_SUFFIX]: TOOL_EVENTS_ALLOW };
124
+ function copyReplayTraceDestination(source, target) {
125
+ const destination = state().spanDestinations.get(source);
126
+ if (destination) state().spanDestinations.set(target, destination);
167
127
  }
168
- function withMetadataPrefix(metadata) {
169
- const out = {};
170
- for (const [key, value] of Object.entries(metadata)) {
171
- out[key.startsWith(AI_SDK_METADATA_PREFIX) ? key : `${AI_SDK_METADATA_PREFIX}${key}`] = value;
128
+ function replayTraceDestinationFor(value) {
129
+ if (typeof value !== "object" && typeof value !== "function" || value === null) {
130
+ return void 0;
172
131
  }
173
- return out;
132
+ return state().spanDestinations.get(value);
174
133
  }
175
- var BAGGAGE_KEYS = {
176
- eventId: "raindrop-event-id",
177
- childEventId: "raindrop-child-event-id",
178
- name: "raindrop-handoff-name",
179
- convoId: "raindrop-convo-id",
180
- userId: "raindrop-user-id",
181
- // The dispatch span and its trace, carried as fields as well as in
182
- // `traceparent`. Not redundant: OTel's HTTP instrumentations rewrite
183
- // `traceparent` from whatever span is current as the request goes out, so by
184
- // the time the child reads it the ids name the HTTP client span and its trace
185
- // instead of the dispatch. The child would then point its reverse reference at
186
- // a span the launcher's UI does not know as a dispatch. A field states which
187
- // span it means rather than meaning "whoever wrote this header last".
188
- dispatchSpanId: "raindrop-dispatch-span-id",
189
- traceId: "raindrop-trace-id"
190
- };
191
- var TRACEPARENT_HEADER = "traceparent";
192
- var BAGGAGE_HEADER = "baggage";
193
- var W3C_TRACE_ID = /^[0-9a-f]{32}$/;
194
- var W3C_SPAN_ID = /^[0-9a-f]{16}$/;
195
- function isW3CTraceId(value) {
196
- return value !== void 0 && W3C_TRACE_ID.test(value);
134
+ function recordReplayExportedSpan(destination, spanId, traceId) {
135
+ var _a, _b;
136
+ if (!destination || !spanId || !traceId) return;
137
+ (_a = destination.exportedSpanIds) == null ? void 0 : _a.add(spanId);
138
+ (_b = destination.exportedTraceIds) == null ? void 0 : _b.add(traceId);
197
139
  }
198
- function isW3CSpanId(value) {
199
- return value !== void 0 && W3C_SPAN_ID.test(value);
140
+ function replayTraceUrl(baseUrl) {
141
+ return `${baseUrl.replace(/\/+$/, "")}/v1/traces`;
200
142
  }
201
- var warnedNonConformantIds = false;
202
- function warnNonConformantIdsOnce(traceId, spanId) {
203
- if (warnedNonConformantIds) return;
204
- warnedNonConformantIds = true;
205
- console.warn(
206
- `[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.`
207
- );
143
+ function getAsyncLocalStorageCtor() {
144
+ return globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
208
145
  }
209
- var HANDOFF_HEADER = "x-raindrop-handoff";
210
- var LANGSMITH_TRACE_HEADER = "langsmith-trace";
211
- var LANGSMITH_METADATA_BAGGAGE_KEY = "langsmith-metadata";
212
- function encodeBaggage(entries) {
213
- return Object.entries(entries).filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join(",");
146
+ function getWeakRefCtor() {
147
+ const ctor = globalThis.WeakRef;
148
+ return typeof ctor === "function" ? ctor : void 0;
214
149
  }
215
- function decodeBaggage(header) {
216
- if (!header) return {};
217
- const parsed = {};
218
- for (const part of header.split(",")) {
219
- const [rawKey, ...rest] = part.split("=");
220
- if (!rawKey || rest.length === 0) continue;
221
- const key = rawKey.trim();
222
- let value;
150
+ var MAX_BINDING_STACK = 128;
151
+ var EMPTY_STACK = [];
152
+ var ContextStackStorage = class {
153
+ /** Emitted once when this runtime cannot support an ambient push. */
154
+ constructor(noAmbientBindWarning) {
155
+ this.noAmbientBindWarning = noAmbientBindWarning;
156
+ this._als = null;
157
+ this._enterWithUsable = false;
158
+ this._syncStack = EMPTY_STACK;
159
+ this._adoptionChecked = false;
160
+ this._warnedNoAmbientBind = false;
161
+ }
162
+ maybeAdoptAsyncLocalStorage() {
163
+ if (this._adoptionChecked) return;
164
+ this._adoptionChecked = true;
165
+ const Ctor = getAsyncLocalStorageCtor();
166
+ if (!Ctor) return;
167
+ const als = new Ctor();
223
168
  try {
224
- value = decodeURIComponent(rest.join("=").trim());
169
+ als.run(EMPTY_STACK, () => {
170
+ });
225
171
  } catch (e) {
226
- value = rest.join("=").trim();
172
+ return;
173
+ }
174
+ this._als = als;
175
+ if (typeof als.enterWith === "function") {
176
+ try {
177
+ als.enterWith(EMPTY_STACK);
178
+ this._enterWithUsable = true;
179
+ } catch (e) {
180
+ this._enterWithUsable = false;
181
+ }
227
182
  }
228
- if (key in parsed && parsed[key] !== value) throw new ConflictingCarrierError(key);
229
- parsed[key] = value;
230
183
  }
231
- return parsed;
232
- }
233
- function carrierBaggage(carrier) {
234
- return {
235
- [BAGGAGE_KEYS.eventId]: carrier.eventId,
236
- [BAGGAGE_KEYS.childEventId]: carrier.childEventId,
237
- [BAGGAGE_KEYS.name]: carrier.name,
238
- [BAGGAGE_KEYS.convoId]: carrier.convoId,
239
- [BAGGAGE_KEYS.userId]: carrier.userId,
240
- [BAGGAGE_KEYS.dispatchSpanId]: carrier.spanId,
241
- [BAGGAGE_KEYS.traceId]: carrier.traceId
242
- };
243
- }
244
- function toHeaders(carrier) {
245
- const fields = encodeBaggage(carrierBaggage(carrier));
246
- const headers = {
247
- [BAGGAGE_HEADER]: fields,
248
- [HANDOFF_HEADER]: fields
249
- };
250
- if (isW3CTraceId(carrier.traceId) && isW3CSpanId(carrier.spanId)) {
251
- headers[TRACEPARENT_HEADER] = `00-${carrier.traceId}-${carrier.spanId}-01`;
184
+ getStack() {
185
+ var _a;
186
+ this.maybeAdoptAsyncLocalStorage();
187
+ if (this._als) return (_a = this._als.getStore()) != null ? _a : this._syncStack;
188
+ return this._syncStack;
252
189
  }
253
- return headers;
254
- }
255
- function langsmithStamp() {
256
- return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "000000");
257
- }
258
- function toLangSmithHeaders(carrier) {
259
- const stamp = langsmithStamp();
260
- const dottedOrder = `${stamp}Z${carrier.traceId}.${stamp}Z${carrier.spanId}`;
261
- return {
262
- [LANGSMITH_TRACE_HEADER]: dottedOrder,
263
- [BAGGAGE_HEADER]: encodeBaggage({
264
- [LANGSMITH_METADATA_BAGGAGE_KEY]: JSON.stringify(carrierBaggage(carrier))
265
- }),
266
- // Carried here too: a LangSmith-shaped carrier is rewritten in transit
267
- // exactly like the native one, and an injected `traceparent` is preferred
268
- // over the dotted order on read.
269
- [HANDOFF_HEADER]: encodeBaggage(carrierBaggage(carrier))
270
- };
271
- }
272
- function isHeaderLookup(headers) {
273
- return "get" in headers && typeof headers.get === "function";
274
- }
275
- var ConflictingCarrierError = class extends Error {
276
- };
277
- function readHeader(headers, name) {
278
- var _a, _b, _c;
279
- if (isHeaderLookup(headers)) return (_a = headers.get(name)) != null ? _a : void 0;
280
- const value = (_c = headers[name]) != null ? _c : (_b = Object.entries(headers).find(([key]) => key.toLowerCase() === name)) == null ? void 0 : _b[1];
281
- if (!Array.isArray(value)) return value;
282
- const distinct = new Set(value);
283
- if (distinct.size > 1) throw new ConflictingCarrierError(name);
284
- return value[0];
285
- }
286
- function readTraceHeader(headers, name) {
287
- const value = readHeader(headers, name);
288
- if (!(value == null ? void 0 : value.includes(","))) return value;
289
- const copies = value.split(",").map((copy) => copy.trim());
290
- if (new Set(copies).size > 1) throw new ConflictingCarrierError(name);
291
- return copies[0];
292
- }
293
- function fromHeaders(headers) {
294
- var _a, _b;
295
- let baggage;
296
- let handoff;
297
- let traceparent;
298
- let langsmithTrace;
299
- try {
300
- baggage = decodeBaggage(readHeader(headers, BAGGAGE_HEADER));
301
- handoff = decodeBaggage(readHeader(headers, HANDOFF_HEADER));
302
- traceparent = readTraceHeader(headers, TRACEPARENT_HEADER);
303
- langsmithTrace = readTraceHeader(headers, LANGSMITH_TRACE_HEADER);
304
- } catch (error) {
305
- if (error instanceof ConflictingCarrierError) return null;
306
- throw error;
190
+ /**
191
+ * Persist an ambient stack mutation. Returns false when an ALS is adopted but
192
+ * `enterWith` is unusable (workerd): callers must NOT fall back to the
193
+ * process-shared sync stack, since concurrent isolates would cross-stamp.
194
+ */
195
+ setAmbientStack(stack) {
196
+ if (this._als) {
197
+ if (!this._enterWithUsable) return false;
198
+ this._als.enterWith(stack);
199
+ return true;
200
+ }
201
+ this._syncStack = stack;
202
+ return true;
307
203
  }
308
- const langsmithMetadata = baggage[LANGSMITH_METADATA_BAGGAGE_KEY];
309
- const nested = langsmithMetadata ? safeParseStringRecord(langsmithMetadata) : {};
310
- for (const [key, value] of Object.entries(nested)) {
311
- if (key in baggage && baggage[key] !== value) return null;
204
+ push(binding) {
205
+ this.maybeAdoptAsyncLocalStorage();
206
+ if (this._als && !this._enterWithUsable) {
207
+ this.warnNoAmbientBindOnce();
208
+ return;
209
+ }
210
+ let stack = this.getStack();
211
+ if (stack.length >= MAX_BINDING_STACK) {
212
+ stack = stack.filter((b) => b.isLive());
213
+ if (stack.length >= MAX_BINDING_STACK) {
214
+ stack = stack.slice(stack.length - (MAX_BINDING_STACK - 1));
215
+ }
216
+ }
217
+ this.setAmbientStack([...stack, binding]);
312
218
  }
313
- const fields = {
314
- ...baggage,
315
- ...nested,
316
- // Last, so it wins: the standard headers may have been rewritten in transit
317
- // by a propagator, this one is only ever written by us.
318
- ...handoff
319
- };
320
- let traceId;
321
- let spanId;
322
- if (traceparent) {
323
- const [, parsedTraceId, parsedSpanId] = traceparent.split("-");
324
- if (isW3CTraceId(parsedTraceId) && isW3CSpanId(parsedSpanId)) {
325
- traceId = parsedTraceId;
326
- spanId = parsedSpanId;
219
+ remove(binding) {
220
+ binding.markDead();
221
+ const stack = this.getStack();
222
+ const pruned = stack.filter((b) => b !== binding && b.isLive());
223
+ if (pruned.length !== stack.length) {
224
+ this.setAmbientStack(pruned);
327
225
  }
328
226
  }
329
- if (!traceId && !spanId && langsmithTrace) {
330
- const segments = langsmithTrace.split(".");
331
- traceId = (_a = segments[0]) == null ? void 0 : _a.split("Z").pop();
332
- spanId = (_b = segments[segments.length - 1]) == null ? void 0 : _b.split("Z").pop();
227
+ runWith(binding, callback) {
228
+ this.maybeAdoptAsyncLocalStorage();
229
+ if (this._als) {
230
+ return this._als.run([...this.getStack(), binding], callback);
231
+ }
232
+ const previous = this._syncStack;
233
+ this._syncStack = [...previous, binding];
234
+ try {
235
+ return callback();
236
+ } finally {
237
+ this._syncStack = previous;
238
+ }
333
239
  }
334
- traceId = fields[BAGGAGE_KEYS.traceId] || traceId;
335
- spanId = fields[BAGGAGE_KEYS.dispatchSpanId] || spanId;
336
- const eventId = fields[BAGGAGE_KEYS.eventId];
337
- if (!traceId || !spanId || !eventId) return null;
338
- if (!isW3CTraceId(traceId) || !isW3CSpanId(spanId)) {
339
- warnNonConformantIdsOnce(traceId, spanId);
240
+ current() {
241
+ const stack = this.getStack();
242
+ if (stack.length === 0) return void 0;
243
+ const top = stack[stack.length - 1];
244
+ if (top.isLive()) return top;
245
+ const pruned = stack.filter((b) => b.isLive());
246
+ this.setAmbientStack(pruned);
247
+ return pruned.length > 0 ? pruned[pruned.length - 1] : void 0;
340
248
  }
341
- const carrier = { traceId, spanId, eventId };
342
- const childEventId = fields[BAGGAGE_KEYS.childEventId];
343
- if (childEventId) carrier.childEventId = childEventId;
344
- const name = fields[BAGGAGE_KEYS.name];
345
- if (name) carrier.name = name;
346
- const convoId = fields[BAGGAGE_KEYS.convoId];
347
- if (convoId) carrier.convoId = convoId;
348
- const userId = fields[BAGGAGE_KEYS.userId];
349
- if (userId) carrier.userId = userId;
350
- return carrier;
351
- }
352
- 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.`;
353
- var UNRESOLVED_CARRIER_WARNED_KEY = /* @__PURE__ */ Symbol.for("raindrop.handoff.warnedUnresolvedCarrier");
354
- function warnedHolder() {
355
- return globalThis;
249
+ warnNoAmbientBindOnce() {
250
+ if (this._warnedNoAmbientBind) return;
251
+ this._warnedNoAmbientBind = true;
252
+ console.warn(this.noAmbientBindWarning);
253
+ }
254
+ };
255
+ 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.";
256
+ 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.";
257
+ var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
258
+ function routingStorage() {
259
+ const holder = globalThis;
260
+ let storage = holder[ROUTING_STORAGE_KEY];
261
+ if (!storage) {
262
+ storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
263
+ holder[ROUTING_STORAGE_KEY] = storage;
264
+ }
265
+ return storage;
356
266
  }
357
- function warnUnresolvedCarrier(headers, carrier) {
358
- if (carrier) return;
359
- if (!headers) return;
360
- const holder = warnedHolder();
361
- if (holder[UNRESOLVED_CARRIER_WARNED_KEY]) return;
362
- holder[UNRESOLVED_CARRIER_WARNED_KEY] = true;
363
- console.warn(UNRESOLVED_CARRIER_WARNING);
267
+ function makeBoundContext(binding, owner) {
268
+ const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
269
+ const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
270
+ let dead = false;
271
+ return {
272
+ projectId: binding.projectId,
273
+ authHint: binding.authHint,
274
+ ...binding.appGit ? { appGit: binding.appGit } : {},
275
+ ...binding.appGitSource ? { appGitSource: binding.appGitSource } : {},
276
+ isLive() {
277
+ if (dead) return false;
278
+ return ref === void 0 || ref.deref() !== void 0;
279
+ },
280
+ markDead() {
281
+ dead = true;
282
+ }
283
+ };
364
284
  }
365
- function safeParseStringRecord(raw) {
285
+ function bindRoutingContext(binding, owner) {
286
+ const bound = makeBoundContext(binding, owner);
366
287
  try {
367
- const parsed = JSON.parse(raw);
368
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
369
- const result = {};
370
- for (const [key, value] of Object.entries(parsed)) {
371
- if (typeof value === "string") result[key] = value;
372
- }
373
- return result;
288
+ routingStorage().push(bound);
374
289
  } catch (e) {
375
- return {};
376
290
  }
291
+ return bound;
377
292
  }
378
- function runWithTracingSuppressed(fn) {
379
- const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
380
- if (typeof hook !== "function") return fn();
381
- let started = false;
293
+ function unbindRoutingContext(bound) {
294
+ if (!bound || !isBoundContext(bound)) return;
382
295
  try {
383
- return hook(() => {
384
- started = true;
385
- return fn();
386
- });
387
- } catch (err) {
388
- if (started) throw err;
389
- return fn();
296
+ routingStorage().remove(bound);
297
+ } catch (e) {
390
298
  }
391
299
  }
392
- var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
393
- var MAX_RETRY_DELAY_MS = 3e4;
394
- function wait(ms) {
395
- return new Promise((resolve) => setTimeout(resolve, ms));
300
+ function withRoutingContext(binding, callback) {
301
+ const bound = makeBoundContext(binding);
302
+ return routingStorage().runWith(bound, callback);
396
303
  }
397
- function formatEndpoint(endpoint) {
398
- if (!endpoint) return void 0;
399
- return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
304
+ function currentRoutingBinding() {
305
+ const bound = routingStorage().current();
306
+ if (!bound) return void 0;
307
+ return {
308
+ projectId: bound.projectId,
309
+ authHint: bound.authHint,
310
+ ...bound.appGit ? { appGit: bound.appGit } : {},
311
+ ...bound.appGitSource ? { appGitSource: bound.appGitSource } : {}
312
+ };
400
313
  }
401
- function redactUrlForLog(url) {
402
- try {
403
- const parsed = new URL(url);
404
- parsed.username = "";
405
- parsed.password = "";
406
- parsed.search = "";
407
- parsed.hash = "";
408
- return parsed.toString();
409
- } catch (e) {
410
- return "<unparseable-url>";
411
- }
314
+ function isBoundContext(value) {
315
+ return typeof value.isLive === "function";
412
316
  }
413
- var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
414
- var rateLimitedLogLast = /* @__PURE__ */ new Map();
415
- function rateLimitedLog(key, log) {
416
- const now = Date.now();
417
- const last = rateLimitedLogLast.get(key);
418
- if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
419
- return false;
317
+ var ATTRIBUTE_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.spanAttributeStorage");
318
+ function attributeStorage() {
319
+ const holder = globalThis;
320
+ let storage = holder[ATTRIBUTE_STORAGE_KEY];
321
+ if (!storage) {
322
+ storage = new ContextStackStorage(NO_AMBIENT_ATTRIBUTE_BIND_WARNING);
323
+ holder[ATTRIBUTE_STORAGE_KEY] = storage;
420
324
  }
421
- rateLimitedLogLast.set(key, now);
422
- log();
423
- return true;
325
+ return storage;
424
326
  }
425
- async function raceWithTimeout(promise, timeoutMs) {
426
- let timer;
427
- const settledInTime = await Promise.race([
428
- promise.then(
429
- () => true,
430
- () => true
431
- ),
432
- new Promise((resolve) => {
433
- var _a;
434
- timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
435
- (_a = timer.unref) == null ? void 0 : _a.call(timer);
436
- })
437
- ]);
438
- if (timer) clearTimeout(timer);
439
- return settledInTime;
327
+ function makeAttributeFrame(attributes, owner) {
328
+ const snapshot = { ...attributes };
329
+ const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
330
+ const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
331
+ const owned = /* @__PURE__ */ new WeakSet();
332
+ let dead = false;
333
+ let sawError = false;
334
+ return {
335
+ attributes: snapshot,
336
+ sawErrorSpan: () => sawError,
337
+ ownsSpan: (span) => owned.has(span),
338
+ claimSpan(span) {
339
+ owned.add(span);
340
+ },
341
+ markErrorSpanSeen() {
342
+ sawError = true;
343
+ },
344
+ // A frame whose owner has been collected is dead even though nothing
345
+ // unbound it: a run that never settles must not keep handing its reverse
346
+ // reference to whatever runs next on a reused context.
347
+ isLive() {
348
+ if (dead) return false;
349
+ return ref === void 0 || ref.deref() !== void 0;
350
+ },
351
+ markDead() {
352
+ dead = true;
353
+ }
354
+ };
440
355
  }
441
- function parseRetryAfter(headers) {
442
- var _a;
443
- const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
444
- if (!value) return void 0;
445
- const asNumber = Number(value);
446
- if (value.trim() !== "" && !Number.isNaN(asNumber)) return asNumber * 1e3;
447
- const asDate = new Date(value).getTime();
448
- if (!Number.isNaN(asDate)) {
449
- const delta = asDate - Date.now();
450
- return delta > 0 ? delta : 0;
356
+ function bindSpanAttributes(attributes, owner) {
357
+ if (Object.keys(attributes).length === 0) return void 0;
358
+ const frame = makeAttributeFrame(attributes, owner);
359
+ try {
360
+ attributeStorage().push(frame);
361
+ } catch (e) {
362
+ return void 0;
451
363
  }
452
- return void 0;
364
+ return frame;
453
365
  }
454
- function getRetryDelayMs(attemptNumber, previousError) {
455
- if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
456
- const v = previousError.retryAfterMs;
457
- if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
366
+ function unbindSpanAttributes(frame) {
367
+ if (!frame || !isAttributeFrame(frame)) return;
368
+ try {
369
+ attributeStorage().remove(frame);
370
+ } catch (e) {
458
371
  }
459
- if (attemptNumber <= 1) return 0;
460
- const base = 500;
461
- const factor = Math.pow(2, attemptNumber - 2);
462
- return Math.min(base * factor, MAX_RETRY_DELAY_MS);
463
372
  }
464
- async function withRetry(operation, opName, opts) {
465
- const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
466
- let lastError = void 0;
467
- for (let attemptNumber = 1; attemptNumber <= opts.maxAttempts; attemptNumber++) {
468
- if (attemptNumber > 1) {
469
- const delay = getRetryDelayMs(attemptNumber, lastError);
470
- if (opts.debug) {
471
- console.warn(
472
- `${prefix} ${opName} retry ${attemptNumber}/${opts.maxAttempts} in ${delay}ms`
473
- );
474
- }
475
- if (delay > 0) await wait(delay);
476
- } else if (opts.debug) {
477
- console.log(`${prefix} ${opName} attempt ${attemptNumber}/${opts.maxAttempts}`);
478
- }
479
- try {
480
- return await operation();
481
- } catch (err) {
482
- lastError = err;
483
- if (opts.debug) {
484
- const msg = err instanceof Error ? err.message : String(err);
485
- console.warn(
486
- `${prefix} ${opName} attempt ${attemptNumber} failed: ${msg}${attemptNumber === opts.maxAttempts ? " (no more retries)" : ""}`
487
- );
373
+ function applySpanAttributeFrames(span, setAttribute) {
374
+ try {
375
+ for (const frame of attributeStorage().getStack()) {
376
+ if (!frame.isLive()) continue;
377
+ frame.claimSpan(span);
378
+ for (const [key, value] of Object.entries(frame.attributes)) {
379
+ setAttribute(key, value);
488
380
  }
489
- if (lastError && typeof lastError === "object" && "retryable" in lastError && !lastError.retryable)
490
- break;
491
- if (attemptNumber === opts.maxAttempts) break;
492
381
  }
382
+ } catch (e) {
493
383
  }
494
- throw lastError instanceof Error ? lastError : new Error(String(lastError));
495
- }
496
- async function postJson(url, body, headers, opts) {
497
- var _a;
498
- const opName = `POST ${redactUrlForLog(url)}`;
499
- const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
500
- await withRetry(
501
- async () => {
502
- const resp = await runWithTracingSuppressed(
503
- () => fetch(url, {
504
- method: "POST",
505
- redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
506
- headers: {
507
- "Content-Type": "application/json",
508
- ...headers
509
- },
510
- body: JSON.stringify(body),
511
- signal: AbortSignal.timeout(timeoutMs)
512
- })
513
- );
514
- if (!resp.ok) {
515
- const text = await resp.text().catch(() => "");
516
- const err = new Error(
517
- `HTTP ${resp.status} ${resp.statusText}${text ? `: ${text}` : ""}`
518
- );
519
- const retryAfterMs = parseRetryAfter(resp.headers);
520
- if (typeof retryAfterMs === "number") err.retryAfterMs = retryAfterMs;
521
- err.retryable = resp.status === 429 || resp.status >= 500;
522
- throw err;
523
- }
524
- },
525
- opName,
526
- opts
527
- );
528
384
  }
529
- var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
530
- var TRUNCATION_MARKER = "...[truncated by raindrop]";
531
- var BOUNDED_CLONE_MAX_DEPTH = 12;
532
- var BOUNDED_CLONE_SLACK = 256;
533
- var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
534
- function setDefaultMaxTextFieldChars(value) {
535
- if (typeof value === "number" && Number.isFinite(value) && value > 0) {
536
- currentDefaultMaxTextFieldChars = Math.floor(value);
385
+ function claimSpanForFrames(span) {
386
+ try {
387
+ for (const frame of attributeStorage().getStack()) {
388
+ if (frame.isLive()) frame.claimSpan(span);
389
+ }
390
+ } catch (e) {
537
391
  }
538
392
  }
539
- function resolveMaxTextFieldChars(value) {
540
- if (typeof value === "number" && Number.isFinite(value) && value > 0) {
541
- return Math.floor(value);
393
+ function currentSpanAttributes() {
394
+ const merged = {};
395
+ try {
396
+ for (const frame of attributeStorage().getStack()) {
397
+ if (!frame.isLive()) continue;
398
+ Object.assign(merged, frame.attributes);
399
+ }
400
+ } catch (e) {
542
401
  }
543
- return currentDefaultMaxTextFieldChars;
402
+ return merged;
544
403
  }
545
- function truncateToLimit(text, limit) {
546
- if (limit > TRUNCATION_MARKER.length) {
547
- return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
404
+ function markErrorSpanSeen(span) {
405
+ try {
406
+ for (const frame of attributeStorage().getStack()) {
407
+ if (frame.ownsSpan(span)) frame.markErrorSpanSeen();
408
+ }
409
+ } catch (e) {
548
410
  }
549
- return text.slice(0, Math.max(0, limit));
550
411
  }
551
- function capText(value, limit) {
552
- if (typeof value !== "string") return value;
553
- const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
554
- if (value.length <= max) return value;
555
- return truncateToLimit(value, max);
412
+ function isAttributeFrame(value) {
413
+ return typeof value.isLive === "function";
556
414
  }
557
- function boundedClone(obj, charBudget) {
558
- let budget = charBudget;
559
- const seen = /* @__PURE__ */ new WeakSet();
560
- const walk = (node, depth) => {
561
- if (budget <= 0) return TRUNCATION_MARKER;
562
- if (typeof node === "string") {
563
- if (node.length > budget) {
564
- const taken = node.slice(0, Math.max(0, budget)) + TRUNCATION_MARKER;
565
- budget = 0;
566
- return taken;
415
+ var APP_COMMIT_SHA = "raindrop.app.commit_sha";
416
+ var APP_COMMIT_DIRTY = "raindrop.app.commit_dirty";
417
+ var APP_BRANCH = "raindrop.app.branch";
418
+ var APP_GIT_KEYS = [APP_COMMIT_SHA, APP_COMMIT_DIRTY, APP_BRANCH];
419
+ var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
420
+ function appGitExplicitKeys(properties) {
421
+ const keys = /* @__PURE__ */ new Set();
422
+ if (!properties) return keys;
423
+ for (const key of APP_GIT_KEYS) {
424
+ if (own(properties, key)) keys.add(key);
425
+ }
426
+ return keys;
427
+ }
428
+ function mergeAppGitSnapshot(base, properties, explicitKeys = /* @__PURE__ */ new Set()) {
429
+ try {
430
+ const result = { ...base };
431
+ if (properties && own(properties, APP_COMMIT_SHA) && properties[APP_COMMIT_SHA] !== result[APP_COMMIT_SHA]) {
432
+ if (!explicitKeys.has(APP_COMMIT_DIRTY) && !own(properties, APP_COMMIT_DIRTY)) {
433
+ delete result[APP_COMMIT_DIRTY];
567
434
  }
568
- budget -= Math.max(node.length, 1);
569
- return node;
570
- }
571
- if (node === null || typeof node === "number" || typeof node === "boolean") {
572
- budget -= 8;
573
- return node;
574
- }
575
- if (typeof node !== "object") {
576
- budget -= 8;
577
- return node;
578
- }
579
- if (seen.has(node)) return "[CIRCULAR]";
580
- if (depth >= BOUNDED_CLONE_MAX_DEPTH) {
581
- budget -= 16;
582
- return `<max depth>`;
583
- }
584
- seen.add(node);
585
- if (Array.isArray(node)) {
586
- const out2 = [];
587
- for (const v of node) {
588
- if (budget <= 0) {
589
- out2.push(TRUNCATION_MARKER);
590
- break;
591
- }
592
- out2.push(walk(v, depth + 1));
435
+ if (!explicitKeys.has(APP_BRANCH) && !own(properties, APP_BRANCH)) {
436
+ delete result[APP_BRANCH];
593
437
  }
594
- return out2;
595
438
  }
596
- if (typeof node.toJSON === "function") {
597
- budget -= 16;
598
- return node;
599
- }
600
- const out = {};
601
- for (const k in node) {
602
- if (!Object.prototype.hasOwnProperty.call(node, k)) continue;
603
- if (budget <= 0) {
604
- out["..."] = TRUNCATION_MARKER;
605
- break;
606
- }
607
- const key = walk(k, depth + 1);
608
- out[key] = walk(node[k], depth + 1);
439
+ for (const key of APP_GIT_KEYS) {
440
+ if (properties && own(properties, key)) result[key] = properties[key];
609
441
  }
610
- return out;
611
- };
612
- return walk(obj, 0);
613
- }
614
- function stringifyBounded(value, limit) {
615
- const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
616
- if (typeof value === "string") {
617
- return capText(value, max);
618
- }
619
- const pruned = boundedClone(value, max + TRUNCATION_MARKER.length + BOUNDED_CLONE_SLACK);
620
- let json;
621
- try {
622
- json = JSON.stringify(pruned);
442
+ return Object.freeze(result);
623
443
  } catch (e) {
624
- json = void 0;
625
- }
626
- if (json === void 0) {
627
- return capText(String(value), max);
444
+ return Object.freeze({});
628
445
  }
629
- return json.length <= max ? json : truncateToLimit(json, max);
630
446
  }
631
- function attrString(key, value) {
632
- if (value === void 0) return void 0;
633
- return { key, value: { stringValue: value } };
447
+ function validAppCommitSha(value) {
448
+ if (typeof value !== "string" || value.length > 128) return void 0;
449
+ const cleaned = value.trim();
450
+ return /^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/.test(cleaned) ? cleaned.toLowerCase() : void 0;
634
451
  }
635
- function attrInt(key, value) {
636
- if (value === void 0) return void 0;
637
- if (!Number.isFinite(value)) return void 0;
638
- return { key, value: { intValue: String(Math.trunc(value)) } };
452
+ function shortString(value) {
453
+ if (typeof value !== "string" || value.length > 1024) return void 0;
454
+ const cleaned = value.trim();
455
+ return cleaned.length > 0 ? cleaned : void 0;
639
456
  }
640
- function attrDouble(key, value) {
641
- if (value === void 0) return void 0;
642
- if (!Number.isFinite(value)) return void 0;
643
- return { key, value: { doubleValue: value } };
457
+ function flag(value) {
458
+ if (value === true || value === "true" || value === "1") return true;
459
+ if (value === false || value === "false" || value === "0") return false;
460
+ return void 0;
644
461
  }
645
- var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
646
- function state() {
647
- const holder = globalThis;
648
- let current = holder[STATE_KEY];
649
- if (!current) {
650
- let storage = null;
462
+ function environment() {
463
+ try {
464
+ return typeof process === "undefined" ? {} : process.env;
465
+ } catch (e) {
466
+ return {};
467
+ }
468
+ }
469
+ var DEPLOYMENTS = [
470
+ ["VERCEL", "VERCEL_GIT_COMMIT_SHA", "VERCEL_GIT_COMMIT_REF"],
471
+ ["NETLIFY", "COMMIT_REF", "BRANCH"],
472
+ ["RAILWAY_ENVIRONMENT_NAME", "RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH"],
473
+ ["RAILWAY_ENVIRONMENT", "RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH"],
474
+ ["RENDER", "RENDER_GIT_COMMIT", "RENDER_GIT_BRANCH"],
475
+ ["DYNO", "HEROKU_BUILD_COMMIT", ""],
476
+ ["DYNO", "HEROKU_SLUG_COMMIT", ""],
477
+ ["AWS_APP_ID", "AWS_COMMIT_ID", "AWS_BRANCH"],
478
+ ["WORKERS_CI", "WORKERS_CI_COMMIT_SHA", "WORKERS_CI_BRANCH"]
479
+ ];
480
+ var CI_SOURCES = [
481
+ ["GITHUB_ACTIONS", "GITHUB_SHA", "GITHUB_REF_NAME"],
482
+ ["GITLAB_CI", "CI_COMMIT_SHA", "CI_COMMIT_REF_NAME"],
483
+ ["CIRCLECI", "CIRCLE_SHA1", "CIRCLE_BRANCH"]
484
+ ];
485
+ function detectedSource(env, table, detectBranch) {
486
+ for (const row of table) {
487
+ const [marker, shaKey, branchKey] = row;
488
+ if (!marker || !shaKey || !shortString(env[marker]) || flag(env[marker]) === false) continue;
489
+ const commitSha = validAppCommitSha(env[shaKey]);
490
+ if (commitSha)
491
+ return {
492
+ commitSha,
493
+ ...detectBranch && branchKey ? { branch: shortString(env[branchKey]) } : {}
494
+ };
495
+ }
496
+ return {};
497
+ }
498
+ var AppGitContextImpl = class {
499
+ constructor(options, runtime = {}) {
500
+ this.defaults = {};
501
+ this.explicit = {};
502
+ this.explicitKeys = /* @__PURE__ */ new Set();
503
+ this.disposed = false;
504
+ this.operations = /* @__PURE__ */ new Map();
505
+ this.operationOverflow = false;
506
+ this.detectBranch = false;
507
+ this.disabled = false;
508
+ var _a, _b, _c, _d, _e, _f;
651
509
  try {
652
- const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
653
- if (Ctor) storage = new Ctor();
510
+ if (options === false) {
511
+ this.disabled = true;
512
+ return;
513
+ }
514
+ const opts = options != null ? options : {};
515
+ const env = (_a = runtime.env) != null ? _a : environment();
516
+ const detectBranch = (_c = (_b = flag(opts.detectBranch)) != null ? _b : flag(env.RAINDROP_GIT_DETECT_BRANCH)) != null ? _c : false;
517
+ this.detectBranch = detectBranch;
518
+ const collectBranch = detectBranch || runtime.captureLegacyReplayBranch === true;
519
+ const autoDetect = runtime.allowDiscovery === false ? false : (_e = (_d = flag(opts.autoDetect)) != null ? _d : flag(env.RAINDROP_GIT_AUTO_DETECT)) != null ? _e : true;
520
+ const configuredSha = opts.commitSha;
521
+ const explicitSha = configuredSha === void 0 ? env.RAINDROP_COMMIT_SHA : configuredSha;
522
+ if (explicitSha === null) this.explicit[APP_COMMIT_SHA] = null;
523
+ else if (typeof explicitSha === "string" && validAppCommitSha(explicitSha))
524
+ this.explicit[APP_COMMIT_SHA] = explicitSha;
525
+ const configuredDirty = opts.commitDirty;
526
+ const explicitDirty = configuredDirty === void 0 ? flag(env.RAINDROP_COMMIT_DIRTY) : configuredDirty;
527
+ if (explicitDirty === null || typeof explicitDirty === "boolean")
528
+ this.explicit[APP_COMMIT_DIRTY] = explicitDirty;
529
+ const configuredBranch = opts.branch;
530
+ const explicitBranch = configuredBranch === void 0 ? env.RAINDROP_BRANCH : configuredBranch;
531
+ if (explicitBranch === null) this.explicit[APP_BRANCH] = null;
532
+ else if (typeof explicitBranch === "string" && explicitBranch.length <= 1024)
533
+ this.explicit[APP_BRANCH] = explicitBranch;
534
+ this.explicitKeys = appGitExplicitKeys(this.explicit);
535
+ this.defaults = { ...this.explicit };
536
+ if (!autoDetect || explicitSha !== void 0) return;
537
+ const sourceDirectory = (_f = shortString(opts.sourceDirectory)) != null ? _f : shortString(env.RAINDROP_GIT_SOURCE_DIRECTORY);
538
+ if (runtime.allowLocalGit === false && !sourceDirectory) return;
539
+ const deploy = sourceDirectory ? {} : detectedSource(env, DEPLOYMENTS, collectBranch);
540
+ if (deploy.commitSha) {
541
+ this.accept(deploy);
542
+ return;
543
+ }
544
+ const fallback = sourceDirectory ? {} : detectedSource(env, CI_SOURCES, collectBranch);
545
+ if (runtime.resolveLocal && (runtime.allowLocalGit !== false || sourceDirectory)) {
546
+ this.cancel = runtime.resolveLocal(sourceDirectory, collectBranch, (result) => {
547
+ try {
548
+ if (!this.disposed) this.accept(result.commitSha ? result : fallback);
549
+ } catch (e) {
550
+ }
551
+ });
552
+ } else this.accept(fallback);
654
553
  } catch (e) {
655
- storage = null;
656
554
  }
657
- current = {
658
- storage,
659
- syncDestination: void 0,
660
- spanDestinations: /* @__PURE__ */ new WeakMap()
555
+ }
556
+ accept(result) {
557
+ const sha = validAppCommitSha(result.commitSha);
558
+ if (!sha) return;
559
+ this.replayBranch = shortString(result.branch);
560
+ this.defaults = {
561
+ [APP_COMMIT_SHA]: sha,
562
+ ...typeof result.commitDirty === "boolean" ? { [APP_COMMIT_DIRTY]: result.commitDirty } : {},
563
+ ...this.detectBranch && this.replayBranch ? { [APP_BRANCH]: this.replayBranch } : {},
564
+ ...this.explicit
661
565
  };
662
- holder[STATE_KEY] = current;
663
566
  }
664
- return current;
665
- }
666
- function withReplayTraceDestination(destination, callback) {
667
- const routing = state();
668
- if (routing.storage) return routing.storage.run(destination, callback);
669
- const previous = routing.syncDestination;
670
- routing.syncDestination = destination;
671
- try {
672
- return callback();
673
- } finally {
674
- routing.syncDestination = previous;
567
+ snapshot(properties) {
568
+ var _a;
569
+ try {
570
+ const binding = currentRoutingBinding();
571
+ const base = this.disabled && (binding == null ? void 0 : binding.appGitSource) !== "replay-override" ? {} : (_a = binding == null ? void 0 : binding.appGit) != null ? _a : this.defaults;
572
+ return this.mergeSnapshot(base, properties);
573
+ } catch (e) {
574
+ return Object.freeze({});
575
+ }
675
576
  }
676
- }
677
- function currentReplayTraceDestination() {
678
- var _a, _b;
679
- const routing = state();
680
- return (_b = (_a = routing.storage) == null ? void 0 : _a.getStore()) != null ? _b : routing.syncDestination;
681
- }
682
- function rememberReplayTraceDestination(span) {
683
- const destination = currentReplayTraceDestination();
684
- if (destination) state().spanDestinations.set(span, destination);
685
- }
686
- function copyReplayTraceDestination(source, target) {
687
- const destination = state().spanDestinations.get(source);
688
- if (destination) state().spanDestinations.set(target, destination);
689
- }
690
- function replayTraceDestinationFor(value) {
691
- if (typeof value !== "object" && typeof value !== "function" || value === null) {
692
- return void 0;
577
+ /** Safe defaults when no operation/span start was observed; never infer identity. */
578
+ snapshotExplicit(properties, eventId) {
579
+ try {
580
+ const base = { ...this.explicit };
581
+ const operation = eventId ? this.operations.get(eventId) : void 0;
582
+ if (operation) {
583
+ for (const key of operation.explicitKeys) base[key] = operation.snapshot[key];
584
+ }
585
+ return this.mergeSnapshot(base, properties, new Set(Object.keys(base)));
586
+ } catch (e) {
587
+ return mergeAppGitSnapshot({}, properties);
588
+ }
589
+ }
590
+ /** Existing replay branch reporting stays separate from opt-in event properties. */
591
+ snapshotForReplay() {
592
+ const snapshot = this.snapshot();
593
+ if (this.replayBranch && !own(snapshot, APP_BRANCH) && snapshot[APP_COMMIT_SHA] === this.defaults[APP_COMMIT_SHA]) {
594
+ return Object.freeze({ ...snapshot, [APP_BRANCH]: this.replayBranch });
595
+ }
596
+ return snapshot;
597
+ }
598
+ /** Merge a frozen span snapshot without losing client-authored provenance. */
599
+ mergeSnapshot(base, properties, explicitKeys = this.explicitKeys) {
600
+ return mergeAppGitSnapshot(base, properties, /* @__PURE__ */ new Set([...this.explicitKeys, ...explicitKeys]));
601
+ }
602
+ /** Shippers belonging to one client share this bounded operation registry. */
603
+ forOperation(eventId, properties, owner) {
604
+ if (!eventId) return this.snapshot(properties);
605
+ let operation = this.operations.get(eventId);
606
+ if (!operation) {
607
+ if (this.operations.size >= 1e4) this.operationOverflow = true;
608
+ if (this.operationOverflow) return this.mergeSnapshot({}, properties);
609
+ operation = {
610
+ snapshot: this.snapshot(properties),
611
+ owners: /* @__PURE__ */ new Set(),
612
+ unowned: owner === void 0,
613
+ explicitKeys: appGitExplicitKeys(properties)
614
+ };
615
+ this.operations.set(eventId, operation);
616
+ } else if (properties && APP_GIT_KEYS.some((key) => own(properties, key))) {
617
+ const explicitKeys = /* @__PURE__ */ new Set([...this.explicitKeys, ...operation.explicitKeys]);
618
+ operation.snapshot = this.mergeSnapshot(operation.snapshot, properties, explicitKeys);
619
+ for (const key of appGitExplicitKeys(properties)) operation.explicitKeys.add(key);
620
+ }
621
+ if (!owner) operation.unowned = true;
622
+ if (owner) operation.owners.add(owner);
623
+ return operation.snapshot;
624
+ }
625
+ releaseOperation(eventId, owner) {
626
+ const operation = this.operations.get(eventId);
627
+ if (!operation) return;
628
+ operation.owners.delete(owner);
629
+ if (operation.owners.size === 0 && !operation.unowned) this.operations.delete(eventId);
630
+ }
631
+ finishOperation(eventId) {
632
+ this.operations.delete(eventId);
633
+ }
634
+ dispose() {
635
+ var _a;
636
+ this.disposed = true;
637
+ try {
638
+ (_a = this.cancel) == null ? void 0 : _a.call(this);
639
+ } catch (e) {
640
+ }
693
641
  }
694
- return state().spanDestinations.get(value);
695
- }
696
- function recordReplayExportedSpan(destination, spanId, traceId) {
697
- var _a, _b;
698
- if (!destination || !spanId || !traceId) return;
699
- (_a = destination.exportedSpanIds) == null ? void 0 : _a.add(spanId);
700
- (_b = destination.exportedTraceIds) == null ? void 0 : _b.add(traceId);
701
- }
702
- function replayTraceUrl(baseUrl) {
703
- return `${baseUrl.replace(/\/+$/, "")}/v1/traces`;
704
- }
705
- var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
706
- var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
707
- var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
708
- function readEnvVar(name) {
709
- var _a;
642
+ };
643
+ var AppGitContext = AppGitContextImpl;
644
+ function withAppGitProperties(properties, snapshot) {
710
645
  try {
711
- const env = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env;
712
- if (env && typeof env[name] === "string" && env[name].length > 0) {
713
- return env[name];
714
- }
646
+ const merged = { ...properties };
647
+ for (const key of APP_GIT_KEYS)
648
+ if (!own(merged, key) && own(snapshot, key)) merged[key] = snapshot[key];
649
+ return merged;
715
650
  } catch (e) {
651
+ return properties != null ? properties : {};
716
652
  }
717
- return void 0;
718
- }
719
- function readWorkshopEnv() {
720
- const raw = readEnvVar(WORKSHOP_ENV_VAR);
721
- if (raw === void 0) return void 0;
722
- const trimmed = raw.trim();
723
- if (trimmed.length === 0) return void 0;
724
- if (/^https?:\/\//i.test(trimmed)) return { url: trimmed };
725
- if (/^(1|true|yes|on)$/i.test(trimmed)) return "enable";
726
- if (/^(0|false|no|off)$/i.test(trimmed)) return "disable";
727
- return void 0;
728
- }
729
- function isLocalDevHost(hostname) {
730
- if (!hostname) return false;
731
- if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1") {
732
- return true;
733
- }
734
- if (hostname.endsWith(".localhost")) return true;
735
- return false;
736
653
  }
737
- function readRuntimeHostname() {
654
+ function withAppGitAttributes(attributes, snapshot) {
738
655
  try {
739
- const loc = globalThis == null ? void 0 : globalThis.location;
740
- if (loc && typeof loc.hostname === "string" && loc.hostname.length > 0) {
741
- return loc.hostname;
656
+ const present = new Set(attributes.flatMap((attr) => attr ? [attr.key] : []));
657
+ const added = [];
658
+ for (const key of APP_GIT_KEYS) {
659
+ if (present.has(key) || present.has(`ai.telemetry.metadata.${key}`) || present.has(`traceloop.association.properties.${key}`))
660
+ continue;
661
+ const value = snapshot[key];
662
+ if (typeof value === "string") added.push({ key, value: { stringValue: value } });
663
+ else if (typeof value === "boolean") added.push({ key, value: { boolValue: value } });
742
664
  }
665
+ return added.length ? [...attributes, ...added] : attributes;
743
666
  } catch (e) {
667
+ return attributes;
744
668
  }
745
- return void 0;
746
669
  }
747
- function shouldAutoEnableLocalWorkshop() {
748
- if (isLocalDevHost(readRuntimeHostname())) return true;
749
- if (readEnvVar("NODE_ENV") === "development") return true;
750
- return false;
670
+ function normalizeFeatureFlags(input) {
671
+ if (Array.isArray(input)) {
672
+ return Object.fromEntries(input.map((name) => [name, "true"]));
673
+ }
674
+ return Object.fromEntries(
675
+ Object.entries(input).map(([k, v]) => [k, String(v)])
676
+ );
751
677
  }
752
- function resolveLocalDebuggerBaseUrl(baseUrl) {
753
- var _a, _b, _c;
754
- if (baseUrl === null) return null;
755
- if (typeof baseUrl === "string" && baseUrl.length > 0) {
756
- return (_a = formatEndpoint(baseUrl)) != null ? _a : null;
678
+ function getCrypto() {
679
+ const c = globalThis.crypto;
680
+ return c;
681
+ }
682
+ function randomBytes(length) {
683
+ const cryptoObj = getCrypto();
684
+ const out = new Uint8Array(length);
685
+ if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
686
+ cryptoObj.getRandomValues(out);
687
+ return out;
757
688
  }
758
- const explicitUrlEnv = readEnvVar(LOCAL_DEBUGGER_ENV_VAR);
759
- if (explicitUrlEnv) return (_b = formatEndpoint(explicitUrlEnv)) != null ? _b : null;
760
- const workshopEnv = readWorkshopEnv();
761
- if (workshopEnv === "disable") return null;
762
- if (workshopEnv === "enable") return DEFAULT_LOCAL_WORKSHOP_URL;
763
- if (workshopEnv && "url" in workshopEnv) return (_c = formatEndpoint(workshopEnv.url)) != null ? _c : null;
764
- if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
765
- return null;
689
+ for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
690
+ return out;
766
691
  }
767
- function localDebuggerEnabled(baseUrl) {
768
- return resolveLocalDebuggerBaseUrl(baseUrl) !== null;
692
+ function randomUUID() {
693
+ const cryptoObj = getCrypto();
694
+ if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
695
+ return cryptoObj.randomUUID();
696
+ }
697
+ const b = randomBytes(16);
698
+ b[6] = b[6] & 15 | 64;
699
+ b[8] = b[8] & 63 | 128;
700
+ const hex = [...b].map((x) => x.toString(16).padStart(2, "0")).join("");
701
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
769
702
  }
770
- var LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS = 2e3;
771
- function mirrorTraceExportToLocalDebugger(body, options = {}) {
772
- var _a;
773
- const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
774
- if (!baseUrl) return Promise.resolve();
775
- return postJson(`${baseUrl}traces`, body, {}, {
776
- maxAttempts: 1,
777
- debug: (_a = options.debug) != null ? _a : false,
778
- sdkName: options.sdkName,
779
- timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
780
- }).catch(() => {
781
- });
703
+ var HANDOFF_ATTRIBUTE_SUFFIXES = {
704
+ mode: "raindrop.handoff.mode",
705
+ childEventId: "raindrop.handoff.childEventId",
706
+ parentEventId: "raindrop.handoff.parentEventId",
707
+ parentSpanId: "raindrop.handoff.parentSpanId",
708
+ name: "raindrop.handoff.name",
709
+ terminal: "raindrop.handoff.terminal",
710
+ agentRole: "raindrop.agent.role"
711
+ };
712
+ var AI_SDK_METADATA_PREFIX = "ai.telemetry.metadata.";
713
+ var DETACHED_MODE = "detached";
714
+ var SUBAGENT_AGENT_ROLE = "subagent";
715
+ var HANDOFF_TERMINAL_CANCELLED = "cancelled";
716
+ var TOOL_EVENTS_SUFFIX = "raindrop.toolEvents";
717
+ var TOOL_EVENTS_ALLOW = "allow";
718
+ var OPERATION_ID_ATTRIBUTE = "ai.operationId";
719
+ var SUBAGENT_ABORT_OPERATION_ID = "ai.subagent.failed";
720
+ var SUBAGENT_ABORT_SPAN_NAME = SUBAGENT_ABORT_OPERATION_ID;
721
+ var DEFAULT_DISPATCH_SPAN_NAME = "launch_subagent";
722
+ var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
723
+ function defined(entries) {
724
+ const out = {};
725
+ for (const [key, value] of Object.entries(entries)) {
726
+ if (typeof value === "string" && value.trim().length > 0) out[key] = value;
727
+ }
728
+ return out;
782
729
  }
783
- function mirrorPartialEventToLocalDebugger(event, options = {}) {
784
- var _a;
785
- const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
786
- if (!baseUrl) return Promise.resolve();
787
- const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
788
- return postJson(`${baseUrl}events/track_partial`, event, headers, {
789
- maxAttempts: 1,
790
- debug: (_a = options.debug) != null ? _a : false,
791
- sdkName: options.sdkName,
792
- timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
793
- }).catch(() => {
730
+ function detachedDispatchMetadata(dispatch) {
731
+ return defined({
732
+ [HANDOFF_ATTRIBUTE_SUFFIXES.mode]: DETACHED_MODE,
733
+ [HANDOFF_ATTRIBUTE_SUFFIXES.childEventId]: dispatch.childEventId,
734
+ [HANDOFF_ATTRIBUTE_SUFFIXES.name]: dispatch.name
794
735
  });
795
736
  }
796
- function sendLocalDebuggerLiveEvent(event, options = {}) {
797
- var _a, _b;
798
- const destination = currentReplayTraceDestination();
799
- if (destination && destination.kind !== "workshop") return;
800
- const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
801
- if (!baseUrl) return;
802
- void postJson(
803
- `${baseUrl}live`,
804
- {
805
- ...event,
806
- type: event.type,
807
- timestamp: (_a = event.timestamp) != null ? _a : Date.now()
808
- },
809
- {},
810
- {
811
- maxAttempts: 1,
812
- debug: (_b = options.debug) != null ? _b : false,
813
- sdkName: options.sdkName
814
- }
815
- ).catch(() => {
737
+ function detachedChildMetadata(link) {
738
+ return defined({
739
+ [HANDOFF_ATTRIBUTE_SUFFIXES.parentEventId]: link.parentEventId,
740
+ [HANDOFF_ATTRIBUTE_SUFFIXES.parentSpanId]: link.parentSpanId,
741
+ [HANDOFF_ATTRIBUTE_SUFFIXES.name]: link.name,
742
+ [HANDOFF_ATTRIBUTE_SUFFIXES.agentRole]: SUBAGENT_AGENT_ROLE
816
743
  });
817
744
  }
818
- var PROJECT_ID_HEADER = "X-Raindrop-Project-Id";
819
- var PROJECT_ID_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
820
- function isValidProjectIdSlug(value) {
821
- return PROJECT_ID_SLUG_PATTERN.test(value);
822
- }
823
- function normalizeProjectId(raw, opts) {
824
- if (typeof raw !== "string") return void 0;
825
- const trimmed = raw.trim();
826
- if (!trimmed) return void 0;
827
- if (!isValidProjectIdSlug(trimmed) && opts.debug) {
828
- console.warn(
829
- `${opts.prefix} projectId "${trimmed}" does not match slug ${PROJECT_ID_SLUG_PATTERN.source}; sending anyway \u2014 backend may reject with HTTP 400`
830
- );
831
- }
832
- return trimmed;
833
- }
834
- function projectIdHeaders(projectId) {
835
- return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
745
+ function cancelledTerminalMetadata() {
746
+ return { [HANDOFF_ATTRIBUTE_SUFFIXES.terminal]: HANDOFF_TERMINAL_CANCELLED };
836
747
  }
837
- var SHUTDOWN_DEADLINE_MS = 1e4;
838
- var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
839
- var MODEL_USAGE_ATTRIBUTES = {
840
- usageSource: "raindrop.usage.source",
841
- providerName: "gen_ai.provider.name",
842
- requestModel: "gen_ai.request.model",
843
- responseModel: "gen_ai.response.model",
844
- inputTokens: "gen_ai.usage.input_tokens",
845
- outputTokens: "gen_ai.usage.output_tokens",
846
- reasoningTokens: "gen_ai.usage.reasoning_tokens",
847
- cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
848
- cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
849
- providerReportedCost: "gen_ai.usage.provider_reported_cost",
850
- nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
851
- nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
852
- totalOutputTokens: "raindrop.usage.output_tokens.total",
853
- reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
854
- cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
855
- cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
856
- };
857
- var MODEL_PROVIDER_NAMES = [
858
- ["google.vertex.anthropic", "google-vertex"],
859
- ["google.vertex_ai", "google-vertex"],
860
- ["google.vertex", "google-vertex"],
861
- ["gcp.vertex_ai", "google-vertex"],
862
- ["gcp.gemini", "google"],
863
- ["google_vertexai", "google-vertex"],
864
- ["vertex.anthropic", "google-vertex"],
865
- ["amazon-bedrock", "amazon-bedrock"],
866
- ["aws.bedrock", "amazon-bedrock"],
867
- ["bedrock", "amazon-bedrock"],
868
- ["anthropic", "anthropic"],
869
- ["openai", "openai"],
870
- ["google", "google"],
871
- ["az.ai.openai", "azure"],
872
- ["azure", "azure"],
873
- ["openrouter", "openrouter"]
874
- ];
875
- function canonicalModelProvider(provider) {
876
- var _a;
877
- const normalizedProvider = provider.trim().toLowerCase();
878
- if (normalizedProvider === "gateway") return "vercel";
879
- const match = MODEL_PROVIDER_NAMES.find(
880
- ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
881
- );
882
- return (_a = match == null ? void 0 : match[1]) != null ? _a : normalizedProvider;
748
+ function cancelledTerminalAttributes() {
749
+ const bare = cancelledTerminalMetadata();
750
+ return { ...bare, ...withMetadataPrefix(bare) };
883
751
  }
884
- function tokenCount(value) {
885
- return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
752
+ function toolEventsAllowMetadata() {
753
+ return { [TOOL_EVENTS_SUFFIX]: TOOL_EVENTS_ALLOW };
886
754
  }
887
- function buildRawModelUsageAttributes(usage) {
888
- const providerReportedCost = typeof usage.providerReportedCost === "number" && Number.isFinite(usage.providerReportedCost) && usage.providerReportedCost >= 0 ? usage.providerReportedCost : void 0;
889
- return [
890
- attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, tokenCount(usage.inputTokens)),
891
- attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, tokenCount(usage.outputTokens)),
892
- attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, tokenCount(usage.reasoningTokens)),
893
- attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, tokenCount(usage.cacheReadInputTokens)),
894
- attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, tokenCount(usage.cacheWriteInputTokens)),
895
- attrDouble(MODEL_USAGE_ATTRIBUTES.providerReportedCost, providerReportedCost)
896
- ].filter((attribute) => attribute !== void 0);
755
+ function withMetadataPrefix(metadata) {
756
+ const out = {};
757
+ for (const [key, value] of Object.entries(metadata)) {
758
+ out[key.startsWith(AI_SDK_METADATA_PREFIX) ? key : `${AI_SDK_METADATA_PREFIX}${key}`] = value;
759
+ }
760
+ return out;
897
761
  }
898
- var STRING_ALIASES = {
899
- [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
900
- };
901
- var NUMBER_ALIASES = {
902
- [MODEL_USAGE_ATTRIBUTES.inputTokens]: [
903
- "gen_ai.usage.prompt_tokens",
904
- "ai.usage.prompt_tokens",
905
- "ai.usage.promptTokens",
906
- "ai.usage.input_tokens",
907
- "ai.usage.inputTokens"
908
- ],
909
- [MODEL_USAGE_ATTRIBUTES.outputTokens]: [
910
- "gen_ai.usage.completion_tokens",
911
- "ai.usage.completion_tokens",
912
- "ai.usage.completionTokens",
913
- "ai.usage.output_tokens",
914
- "ai.usage.outputTokens"
915
- ],
916
- [MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
917
- "gen_ai.usage.reasoning.output_tokens",
918
- "ai.usage.reasoningTokens",
919
- "ai.usage.thoughts_tokens"
920
- ],
921
- [MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
922
- "gen_ai.usage.cache_read_tokens",
923
- "gen_ai.usage.cache_read.input_tokens",
924
- "ai.usage.cachedInputTokens",
925
- "ai.usage.cached_tokens",
926
- "ai.usage.cache_read_tokens",
927
- "ai.usage.cache_read_input_tokens",
928
- "ai.usage.cacheReadInputTokens"
929
- ],
930
- [MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
931
- "gen_ai.usage.cache_creation_input_tokens",
932
- "gen_ai.usage.cache_creation.input_tokens",
933
- "ai.usage.cacheWriteInputTokens",
934
- "ai.usage.cache_creation_input_tokens",
935
- "ai.usage.cacheCreationInputTokens"
936
- ]
762
+ var BAGGAGE_KEYS = {
763
+ eventId: "raindrop-event-id",
764
+ childEventId: "raindrop-child-event-id",
765
+ name: "raindrop-handoff-name",
766
+ convoId: "raindrop-convo-id",
767
+ userId: "raindrop-user-id",
768
+ // The dispatch span and its trace, carried as fields as well as in
769
+ // `traceparent`. Not redundant: OTel's HTTP instrumentations rewrite
770
+ // `traceparent` from whatever span is current as the request goes out, so by
771
+ // the time the child reads it the ids name the HTTP client span and its trace
772
+ // instead of the dispatch. The child would then point its reverse reference at
773
+ // a span the launcher's UI does not know as a dispatch. A field states which
774
+ // span it means rather than meaning "whoever wrote this header last".
775
+ dispatchSpanId: "raindrop-dispatch-span-id",
776
+ traceId: "raindrop-trace-id"
937
777
  };
938
- var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
939
- "ai.request.providerOptions",
940
- "ai.response.providerMetadata"
941
- ];
942
- var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
943
- function getAsyncLocalStorageCtor() {
944
- return globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
778
+ var TRACEPARENT_HEADER = "traceparent";
779
+ var BAGGAGE_HEADER = "baggage";
780
+ var W3C_TRACE_ID = /^[0-9a-f]{32}$/;
781
+ var W3C_SPAN_ID = /^[0-9a-f]{16}$/;
782
+ function isW3CTraceId(value) {
783
+ return value !== void 0 && W3C_TRACE_ID.test(value);
945
784
  }
946
- function getWeakRefCtor() {
947
- const ctor = globalThis.WeakRef;
948
- return typeof ctor === "function" ? ctor : void 0;
785
+ function isW3CSpanId(value) {
786
+ return value !== void 0 && W3C_SPAN_ID.test(value);
949
787
  }
950
- var MAX_BINDING_STACK = 128;
951
- var EMPTY_STACK = [];
952
- var ContextStackStorage = class {
953
- /** Emitted once when this runtime cannot support an ambient push. */
954
- constructor(noAmbientBindWarning) {
955
- this.noAmbientBindWarning = noAmbientBindWarning;
956
- this._als = null;
957
- this._enterWithUsable = false;
958
- this._syncStack = EMPTY_STACK;
959
- this._adoptionChecked = false;
960
- this._warnedNoAmbientBind = false;
961
- }
962
- maybeAdoptAsyncLocalStorage() {
963
- if (this._adoptionChecked) return;
964
- this._adoptionChecked = true;
965
- const Ctor = getAsyncLocalStorageCtor();
966
- if (!Ctor) return;
967
- const als = new Ctor();
788
+ var warnedNonConformantIds = false;
789
+ function warnNonConformantIdsOnce(traceId, spanId) {
790
+ if (warnedNonConformantIds) return;
791
+ warnedNonConformantIds = true;
792
+ console.warn(
793
+ `[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.`
794
+ );
795
+ }
796
+ var HANDOFF_HEADER = "x-raindrop-handoff";
797
+ var LANGSMITH_TRACE_HEADER = "langsmith-trace";
798
+ var LANGSMITH_METADATA_BAGGAGE_KEY = "langsmith-metadata";
799
+ function encodeBaggage(entries) {
800
+ return Object.entries(entries).filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join(",");
801
+ }
802
+ function decodeBaggage(header) {
803
+ if (!header) return {};
804
+ const parsed = {};
805
+ for (const part of header.split(",")) {
806
+ const [rawKey, ...rest] = part.split("=");
807
+ if (!rawKey || rest.length === 0) continue;
808
+ const key = rawKey.trim();
809
+ let value;
968
810
  try {
969
- als.run(EMPTY_STACK, () => {
970
- });
811
+ value = decodeURIComponent(rest.join("=").trim());
971
812
  } catch (e) {
972
- return;
973
- }
974
- this._als = als;
975
- if (typeof als.enterWith === "function") {
976
- try {
977
- als.enterWith(EMPTY_STACK);
978
- this._enterWithUsable = true;
979
- } catch (e) {
980
- this._enterWithUsable = false;
981
- }
813
+ value = rest.join("=").trim();
982
814
  }
815
+ if (key in parsed && parsed[key] !== value) throw new ConflictingCarrierError(key);
816
+ parsed[key] = value;
983
817
  }
984
- getStack() {
985
- var _a;
986
- this.maybeAdoptAsyncLocalStorage();
987
- if (this._als) return (_a = this._als.getStore()) != null ? _a : this._syncStack;
988
- return this._syncStack;
818
+ return parsed;
819
+ }
820
+ function carrierBaggage(carrier) {
821
+ return {
822
+ [BAGGAGE_KEYS.eventId]: carrier.eventId,
823
+ [BAGGAGE_KEYS.childEventId]: carrier.childEventId,
824
+ [BAGGAGE_KEYS.name]: carrier.name,
825
+ [BAGGAGE_KEYS.convoId]: carrier.convoId,
826
+ [BAGGAGE_KEYS.userId]: carrier.userId,
827
+ [BAGGAGE_KEYS.dispatchSpanId]: carrier.spanId,
828
+ [BAGGAGE_KEYS.traceId]: carrier.traceId
829
+ };
830
+ }
831
+ function toHeaders(carrier) {
832
+ const fields = encodeBaggage(carrierBaggage(carrier));
833
+ const headers = {
834
+ [BAGGAGE_HEADER]: fields,
835
+ [HANDOFF_HEADER]: fields
836
+ };
837
+ if (isW3CTraceId(carrier.traceId) && isW3CSpanId(carrier.spanId)) {
838
+ headers[TRACEPARENT_HEADER] = `00-${carrier.traceId}-${carrier.spanId}-01`;
989
839
  }
990
- /**
991
- * Persist an ambient stack mutation. Returns false when an ALS is adopted but
992
- * `enterWith` is unusable (workerd): callers must NOT fall back to the
993
- * process-shared sync stack, since concurrent isolates would cross-stamp.
994
- */
995
- setAmbientStack(stack) {
996
- if (this._als) {
997
- if (!this._enterWithUsable) return false;
998
- this._als.enterWith(stack);
999
- return true;
840
+ return headers;
841
+ }
842
+ function langsmithStamp() {
843
+ return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "000000");
844
+ }
845
+ function toLangSmithHeaders(carrier) {
846
+ const stamp = langsmithStamp();
847
+ const dottedOrder = `${stamp}Z${carrier.traceId}.${stamp}Z${carrier.spanId}`;
848
+ return {
849
+ [LANGSMITH_TRACE_HEADER]: dottedOrder,
850
+ [BAGGAGE_HEADER]: encodeBaggage({
851
+ [LANGSMITH_METADATA_BAGGAGE_KEY]: JSON.stringify(carrierBaggage(carrier))
852
+ }),
853
+ // Carried here too: a LangSmith-shaped carrier is rewritten in transit
854
+ // exactly like the native one, and an injected `traceparent` is preferred
855
+ // over the dotted order on read.
856
+ [HANDOFF_HEADER]: encodeBaggage(carrierBaggage(carrier))
857
+ };
858
+ }
859
+ function isHeaderLookup(headers) {
860
+ return "get" in headers && typeof headers.get === "function";
861
+ }
862
+ var ConflictingCarrierError = class extends Error {
863
+ };
864
+ function readHeader(headers, name) {
865
+ var _a, _b, _c;
866
+ if (isHeaderLookup(headers)) return (_a = headers.get(name)) != null ? _a : void 0;
867
+ const value = (_c = headers[name]) != null ? _c : (_b = Object.entries(headers).find(([key]) => key.toLowerCase() === name)) == null ? void 0 : _b[1];
868
+ if (!Array.isArray(value)) return value;
869
+ const distinct = new Set(value);
870
+ if (distinct.size > 1) throw new ConflictingCarrierError(name);
871
+ return value[0];
872
+ }
873
+ function readTraceHeader(headers, name) {
874
+ const value = readHeader(headers, name);
875
+ if (!(value == null ? void 0 : value.includes(","))) return value;
876
+ const copies = value.split(",").map((copy) => copy.trim());
877
+ if (new Set(copies).size > 1) throw new ConflictingCarrierError(name);
878
+ return copies[0];
879
+ }
880
+ function fromHeaders(headers) {
881
+ var _a, _b;
882
+ let baggage;
883
+ let handoff;
884
+ let traceparent;
885
+ let langsmithTrace;
886
+ try {
887
+ baggage = decodeBaggage(readHeader(headers, BAGGAGE_HEADER));
888
+ handoff = decodeBaggage(readHeader(headers, HANDOFF_HEADER));
889
+ traceparent = readTraceHeader(headers, TRACEPARENT_HEADER);
890
+ langsmithTrace = readTraceHeader(headers, LANGSMITH_TRACE_HEADER);
891
+ } catch (error) {
892
+ if (error instanceof ConflictingCarrierError) return null;
893
+ throw error;
894
+ }
895
+ const langsmithMetadata = baggage[LANGSMITH_METADATA_BAGGAGE_KEY];
896
+ const nested = langsmithMetadata ? safeParseStringRecord(langsmithMetadata) : {};
897
+ for (const [key, value] of Object.entries(nested)) {
898
+ if (key in baggage && baggage[key] !== value) return null;
899
+ }
900
+ const fields = {
901
+ ...baggage,
902
+ ...nested,
903
+ // Last, so it wins: the standard headers may have been rewritten in transit
904
+ // by a propagator, this one is only ever written by us.
905
+ ...handoff
906
+ };
907
+ let traceId;
908
+ let spanId;
909
+ if (traceparent) {
910
+ const [, parsedTraceId, parsedSpanId] = traceparent.split("-");
911
+ if (isW3CTraceId(parsedTraceId) && isW3CSpanId(parsedSpanId)) {
912
+ traceId = parsedTraceId;
913
+ spanId = parsedSpanId;
914
+ }
915
+ }
916
+ if (!traceId && !spanId && langsmithTrace) {
917
+ const segments = langsmithTrace.split(".");
918
+ traceId = (_a = segments[0]) == null ? void 0 : _a.split("Z").pop();
919
+ spanId = (_b = segments[segments.length - 1]) == null ? void 0 : _b.split("Z").pop();
920
+ }
921
+ traceId = fields[BAGGAGE_KEYS.traceId] || traceId;
922
+ spanId = fields[BAGGAGE_KEYS.dispatchSpanId] || spanId;
923
+ const eventId = fields[BAGGAGE_KEYS.eventId];
924
+ if (!traceId || !spanId || !eventId) return null;
925
+ if (!isW3CTraceId(traceId) || !isW3CSpanId(spanId)) {
926
+ warnNonConformantIdsOnce(traceId, spanId);
927
+ }
928
+ const carrier = { traceId, spanId, eventId };
929
+ const childEventId = fields[BAGGAGE_KEYS.childEventId];
930
+ if (childEventId) carrier.childEventId = childEventId;
931
+ const name = fields[BAGGAGE_KEYS.name];
932
+ if (name) carrier.name = name;
933
+ const convoId = fields[BAGGAGE_KEYS.convoId];
934
+ if (convoId) carrier.convoId = convoId;
935
+ const userId = fields[BAGGAGE_KEYS.userId];
936
+ if (userId) carrier.userId = userId;
937
+ return carrier;
938
+ }
939
+ 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.`;
940
+ var UNRESOLVED_CARRIER_WARNED_KEY = /* @__PURE__ */ Symbol.for("raindrop.handoff.warnedUnresolvedCarrier");
941
+ function warnedHolder() {
942
+ return globalThis;
943
+ }
944
+ function warnUnresolvedCarrier(headers, carrier) {
945
+ if (carrier) return;
946
+ if (!headers) return;
947
+ const holder = warnedHolder();
948
+ if (holder[UNRESOLVED_CARRIER_WARNED_KEY]) return;
949
+ holder[UNRESOLVED_CARRIER_WARNED_KEY] = true;
950
+ console.warn(UNRESOLVED_CARRIER_WARNING);
951
+ }
952
+ function safeParseStringRecord(raw) {
953
+ try {
954
+ const parsed = JSON.parse(raw);
955
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
956
+ const result = {};
957
+ for (const [key, value] of Object.entries(parsed)) {
958
+ if (typeof value === "string") result[key] = value;
959
+ }
960
+ return result;
961
+ } catch (e) {
962
+ return {};
963
+ }
964
+ }
965
+ function runWithTracingSuppressed(fn) {
966
+ const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
967
+ if (typeof hook !== "function") return fn();
968
+ let started = false;
969
+ try {
970
+ return hook(() => {
971
+ started = true;
972
+ return fn();
973
+ });
974
+ } catch (err) {
975
+ if (started) throw err;
976
+ return fn();
977
+ }
978
+ }
979
+ var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
980
+ var MAX_RETRY_DELAY_MS = 3e4;
981
+ function wait(ms) {
982
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
983
+ }
984
+ function formatEndpoint(endpoint) {
985
+ if (!endpoint) return void 0;
986
+ return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
987
+ }
988
+ function redactUrlForLog(url) {
989
+ try {
990
+ const parsed = new URL(url);
991
+ parsed.username = "";
992
+ parsed.password = "";
993
+ parsed.search = "";
994
+ parsed.hash = "";
995
+ return parsed.toString();
996
+ } catch (e) {
997
+ return "<unparseable-url>";
998
+ }
999
+ }
1000
+ var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
1001
+ var rateLimitedLogLast = /* @__PURE__ */ new Map();
1002
+ function rateLimitedLog(key, log) {
1003
+ const now = Date.now();
1004
+ const last = rateLimitedLogLast.get(key);
1005
+ if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
1006
+ return false;
1007
+ }
1008
+ rateLimitedLogLast.set(key, now);
1009
+ log();
1010
+ return true;
1011
+ }
1012
+ async function raceWithTimeout(promise, timeoutMs) {
1013
+ let timer;
1014
+ const settledInTime = await Promise.race([
1015
+ promise.then(
1016
+ () => true,
1017
+ () => true
1018
+ ),
1019
+ new Promise((resolve2) => {
1020
+ var _a;
1021
+ timer = setTimeout(() => resolve2(false), Math.max(0, timeoutMs));
1022
+ (_a = timer.unref) == null ? void 0 : _a.call(timer);
1023
+ })
1024
+ ]);
1025
+ if (timer) clearTimeout(timer);
1026
+ return settledInTime;
1027
+ }
1028
+ function parseRetryAfter(headers) {
1029
+ var _a;
1030
+ const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
1031
+ if (!value) return void 0;
1032
+ const asNumber = Number(value);
1033
+ if (value.trim() !== "" && !Number.isNaN(asNumber)) return asNumber * 1e3;
1034
+ const asDate = new Date(value).getTime();
1035
+ if (!Number.isNaN(asDate)) {
1036
+ const delta = asDate - Date.now();
1037
+ return delta > 0 ? delta : 0;
1038
+ }
1039
+ return void 0;
1040
+ }
1041
+ function getRetryDelayMs(attemptNumber, previousError) {
1042
+ if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
1043
+ const v = previousError.retryAfterMs;
1044
+ if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
1045
+ }
1046
+ if (attemptNumber <= 1) return 0;
1047
+ const base = 500;
1048
+ const factor = Math.pow(2, attemptNumber - 2);
1049
+ return Math.min(base * factor, MAX_RETRY_DELAY_MS);
1050
+ }
1051
+ async function withRetry(operation, opName, opts) {
1052
+ const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
1053
+ let lastError = void 0;
1054
+ for (let attemptNumber = 1; attemptNumber <= opts.maxAttempts; attemptNumber++) {
1055
+ if (attemptNumber > 1) {
1056
+ const delay = getRetryDelayMs(attemptNumber, lastError);
1057
+ if (opts.debug) {
1058
+ console.warn(
1059
+ `${prefix} ${opName} retry ${attemptNumber}/${opts.maxAttempts} in ${delay}ms`
1060
+ );
1061
+ }
1062
+ if (delay > 0) await wait(delay);
1063
+ } else if (opts.debug) {
1064
+ console.log(`${prefix} ${opName} attempt ${attemptNumber}/${opts.maxAttempts}`);
1065
+ }
1066
+ try {
1067
+ return await operation();
1068
+ } catch (err) {
1069
+ lastError = err;
1070
+ if (opts.debug) {
1071
+ const msg = err instanceof Error ? err.message : String(err);
1072
+ console.warn(
1073
+ `${prefix} ${opName} attempt ${attemptNumber} failed: ${msg}${attemptNumber === opts.maxAttempts ? " (no more retries)" : ""}`
1074
+ );
1075
+ }
1076
+ if (lastError && typeof lastError === "object" && "retryable" in lastError && !lastError.retryable)
1077
+ break;
1078
+ if (attemptNumber === opts.maxAttempts) break;
1079
+ }
1080
+ }
1081
+ throw lastError instanceof Error ? lastError : new Error(String(lastError));
1082
+ }
1083
+ async function postJson(url, body, headers, opts) {
1084
+ var _a;
1085
+ const opName = `POST ${redactUrlForLog(url)}`;
1086
+ const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
1087
+ await withRetry(
1088
+ async () => {
1089
+ const resp = await runWithTracingSuppressed(
1090
+ () => fetch(url, {
1091
+ method: "POST",
1092
+ redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
1093
+ headers: {
1094
+ "Content-Type": "application/json",
1095
+ ...headers
1096
+ },
1097
+ body: JSON.stringify(body),
1098
+ signal: AbortSignal.timeout(timeoutMs)
1099
+ })
1100
+ );
1101
+ if (!resp.ok) {
1102
+ const text = await resp.text().catch(() => "");
1103
+ const err = new Error(
1104
+ `HTTP ${resp.status} ${resp.statusText}${text ? `: ${text}` : ""}`
1105
+ );
1106
+ const retryAfterMs = parseRetryAfter(resp.headers);
1107
+ if (typeof retryAfterMs === "number") err.retryAfterMs = retryAfterMs;
1108
+ err.retryable = resp.status === 429 || resp.status >= 500;
1109
+ throw err;
1110
+ }
1111
+ },
1112
+ opName,
1113
+ opts
1114
+ );
1115
+ }
1116
+ var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
1117
+ var TRUNCATION_MARKER = "...[truncated by raindrop]";
1118
+ var BOUNDED_CLONE_MAX_DEPTH = 12;
1119
+ var BOUNDED_CLONE_SLACK = 256;
1120
+ var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
1121
+ function setDefaultMaxTextFieldChars(value) {
1122
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
1123
+ currentDefaultMaxTextFieldChars = Math.floor(value);
1124
+ }
1125
+ }
1126
+ function resolveMaxTextFieldChars(value) {
1127
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
1128
+ return Math.floor(value);
1129
+ }
1130
+ return currentDefaultMaxTextFieldChars;
1131
+ }
1132
+ function truncateToLimit(text, limit) {
1133
+ if (limit > TRUNCATION_MARKER.length) {
1134
+ return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
1135
+ }
1136
+ return text.slice(0, Math.max(0, limit));
1137
+ }
1138
+ function capText(value, limit) {
1139
+ if (typeof value !== "string") return value;
1140
+ const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
1141
+ if (value.length <= max) return value;
1142
+ return truncateToLimit(value, max);
1143
+ }
1144
+ function boundedClone(obj, charBudget) {
1145
+ let budget = charBudget;
1146
+ const seen = /* @__PURE__ */ new WeakSet();
1147
+ const walk = (node, depth) => {
1148
+ if (budget <= 0) return TRUNCATION_MARKER;
1149
+ if (typeof node === "string") {
1150
+ if (node.length > budget) {
1151
+ const taken = node.slice(0, Math.max(0, budget)) + TRUNCATION_MARKER;
1152
+ budget = 0;
1153
+ return taken;
1154
+ }
1155
+ budget -= Math.max(node.length, 1);
1156
+ return node;
1000
1157
  }
1001
- this._syncStack = stack;
1002
- return true;
1003
- }
1004
- push(binding) {
1005
- this.maybeAdoptAsyncLocalStorage();
1006
- if (this._als && !this._enterWithUsable) {
1007
- this.warnNoAmbientBindOnce();
1008
- return;
1158
+ if (node === null || typeof node === "number" || typeof node === "boolean") {
1159
+ budget -= 8;
1160
+ return node;
1009
1161
  }
1010
- let stack = this.getStack();
1011
- if (stack.length >= MAX_BINDING_STACK) {
1012
- stack = stack.filter((b) => b.isLive());
1013
- if (stack.length >= MAX_BINDING_STACK) {
1014
- stack = stack.slice(stack.length - (MAX_BINDING_STACK - 1));
1015
- }
1162
+ if (typeof node !== "object") {
1163
+ budget -= 8;
1164
+ return node;
1016
1165
  }
1017
- this.setAmbientStack([...stack, binding]);
1018
- }
1019
- remove(binding) {
1020
- binding.markDead();
1021
- const stack = this.getStack();
1022
- const pruned = stack.filter((b) => b !== binding && b.isLive());
1023
- if (pruned.length !== stack.length) {
1024
- this.setAmbientStack(pruned);
1166
+ if (seen.has(node)) return "[CIRCULAR]";
1167
+ if (depth >= BOUNDED_CLONE_MAX_DEPTH) {
1168
+ budget -= 16;
1169
+ return `<max depth>`;
1025
1170
  }
1026
- }
1027
- runWith(binding, callback) {
1028
- this.maybeAdoptAsyncLocalStorage();
1029
- if (this._als) {
1030
- return this._als.run([...this.getStack(), binding], callback);
1171
+ seen.add(node);
1172
+ if (Array.isArray(node)) {
1173
+ const out2 = [];
1174
+ for (const v of node) {
1175
+ if (budget <= 0) {
1176
+ out2.push(TRUNCATION_MARKER);
1177
+ break;
1178
+ }
1179
+ out2.push(walk(v, depth + 1));
1180
+ }
1181
+ return out2;
1031
1182
  }
1032
- const previous = this._syncStack;
1033
- this._syncStack = [...previous, binding];
1034
- try {
1035
- return callback();
1036
- } finally {
1037
- this._syncStack = previous;
1183
+ if (typeof node.toJSON === "function") {
1184
+ budget -= 16;
1185
+ return node;
1038
1186
  }
1187
+ const out = {};
1188
+ for (const k in node) {
1189
+ if (!Object.prototype.hasOwnProperty.call(node, k)) continue;
1190
+ if (budget <= 0) {
1191
+ out["..."] = TRUNCATION_MARKER;
1192
+ break;
1193
+ }
1194
+ const key = walk(k, depth + 1);
1195
+ out[key] = walk(node[k], depth + 1);
1196
+ }
1197
+ return out;
1198
+ };
1199
+ return walk(obj, 0);
1200
+ }
1201
+ function stringifyBounded(value, limit) {
1202
+ const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
1203
+ if (typeof value === "string") {
1204
+ return capText(value, max);
1039
1205
  }
1040
- current() {
1041
- const stack = this.getStack();
1042
- if (stack.length === 0) return void 0;
1043
- const top = stack[stack.length - 1];
1044
- if (top.isLive()) return top;
1045
- const pruned = stack.filter((b) => b.isLive());
1046
- this.setAmbientStack(pruned);
1047
- return pruned.length > 0 ? pruned[pruned.length - 1] : void 0;
1048
- }
1049
- warnNoAmbientBindOnce() {
1050
- if (this._warnedNoAmbientBind) return;
1051
- this._warnedNoAmbientBind = true;
1052
- console.warn(this.noAmbientBindWarning);
1206
+ const pruned = boundedClone(value, max + TRUNCATION_MARKER.length + BOUNDED_CLONE_SLACK);
1207
+ let json;
1208
+ try {
1209
+ json = JSON.stringify(pruned);
1210
+ } catch (e) {
1211
+ json = void 0;
1053
1212
  }
1054
- };
1055
- 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.";
1056
- 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.";
1057
- var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
1058
- function routingStorage() {
1059
- const holder = globalThis;
1060
- let storage = holder[ROUTING_STORAGE_KEY];
1061
- if (!storage) {
1062
- storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
1063
- holder[ROUTING_STORAGE_KEY] = storage;
1213
+ if (json === void 0) {
1214
+ return capText(String(value), max);
1064
1215
  }
1065
- return storage;
1216
+ return json.length <= max ? json : truncateToLimit(json, max);
1066
1217
  }
1067
- function makeBoundContext(binding, owner) {
1068
- const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
1069
- const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
1070
- let dead = false;
1071
- return {
1072
- projectId: binding.projectId,
1073
- authHint: binding.authHint,
1074
- isLive() {
1075
- if (dead) return false;
1076
- return ref === void 0 || ref.deref() !== void 0;
1077
- },
1078
- markDead() {
1079
- dead = true;
1080
- }
1081
- };
1218
+ function attrString(key, value) {
1219
+ if (value === void 0) return void 0;
1220
+ return { key, value: { stringValue: value } };
1082
1221
  }
1083
- function bindRoutingContext(binding, owner) {
1084
- const bound = makeBoundContext(binding, owner);
1222
+ function attrInt(key, value) {
1223
+ if (value === void 0) return void 0;
1224
+ if (!Number.isFinite(value)) return void 0;
1225
+ return { key, value: { intValue: String(Math.trunc(value)) } };
1226
+ }
1227
+ function attrDouble(key, value) {
1228
+ if (value === void 0) return void 0;
1229
+ if (!Number.isFinite(value)) return void 0;
1230
+ return { key, value: { doubleValue: value } };
1231
+ }
1232
+ var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
1233
+ var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
1234
+ var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
1235
+ function readEnvVar(name) {
1236
+ var _a;
1085
1237
  try {
1086
- routingStorage().push(bound);
1238
+ const env = (_a = globalThis == null ? void 0 : globalThis.process) == null ? void 0 : _a.env;
1239
+ if (env && typeof env[name] === "string" && env[name].length > 0) {
1240
+ return env[name];
1241
+ }
1087
1242
  } catch (e) {
1088
1243
  }
1089
- return bound;
1244
+ return void 0;
1090
1245
  }
1091
- function unbindRoutingContext(bound) {
1092
- if (!bound || !isBoundContext(bound)) return;
1246
+ function readWorkshopEnv() {
1247
+ const raw = readEnvVar(WORKSHOP_ENV_VAR);
1248
+ if (raw === void 0) return void 0;
1249
+ const trimmed = raw.trim();
1250
+ if (trimmed.length === 0) return void 0;
1251
+ if (/^https?:\/\//i.test(trimmed)) return { url: trimmed };
1252
+ if (/^(1|true|yes|on)$/i.test(trimmed)) return "enable";
1253
+ if (/^(0|false|no|off)$/i.test(trimmed)) return "disable";
1254
+ return void 0;
1255
+ }
1256
+ function isLocalDevHost(hostname) {
1257
+ if (!hostname) return false;
1258
+ if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1") {
1259
+ return true;
1260
+ }
1261
+ if (hostname.endsWith(".localhost")) return true;
1262
+ return false;
1263
+ }
1264
+ function readRuntimeHostname() {
1093
1265
  try {
1094
- routingStorage().remove(bound);
1266
+ const loc = globalThis == null ? void 0 : globalThis.location;
1267
+ if (loc && typeof loc.hostname === "string" && loc.hostname.length > 0) {
1268
+ return loc.hostname;
1269
+ }
1095
1270
  } catch (e) {
1096
1271
  }
1272
+ return void 0;
1097
1273
  }
1098
- function withRoutingContext(binding, callback) {
1099
- const bound = makeBoundContext(binding);
1100
- return routingStorage().runWith(bound, callback);
1274
+ function shouldAutoEnableLocalWorkshop() {
1275
+ if (isLocalDevHost(readRuntimeHostname())) return true;
1276
+ if (readEnvVar("NODE_ENV") === "development") return true;
1277
+ return false;
1101
1278
  }
1102
- function currentRoutingBinding() {
1103
- const bound = routingStorage().current();
1104
- if (!bound) return void 0;
1105
- return { projectId: bound.projectId, authHint: bound.authHint };
1279
+ function resolveLocalDebuggerBaseUrl(baseUrl) {
1280
+ var _a, _b, _c;
1281
+ if (baseUrl === null) return null;
1282
+ if (typeof baseUrl === "string" && baseUrl.length > 0) {
1283
+ return (_a = formatEndpoint(baseUrl)) != null ? _a : null;
1284
+ }
1285
+ const explicitUrlEnv = readEnvVar(LOCAL_DEBUGGER_ENV_VAR);
1286
+ if (explicitUrlEnv) return (_b = formatEndpoint(explicitUrlEnv)) != null ? _b : null;
1287
+ const workshopEnv = readWorkshopEnv();
1288
+ if (workshopEnv === "disable") return null;
1289
+ if (workshopEnv === "enable") return DEFAULT_LOCAL_WORKSHOP_URL;
1290
+ if (workshopEnv && "url" in workshopEnv) return (_c = formatEndpoint(workshopEnv.url)) != null ? _c : null;
1291
+ if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
1292
+ return null;
1106
1293
  }
1107
- function isBoundContext(value) {
1108
- return typeof value.isLive === "function";
1294
+ function localDebuggerEnabled(baseUrl) {
1295
+ return resolveLocalDebuggerBaseUrl(baseUrl) !== null;
1109
1296
  }
1110
- var ATTRIBUTE_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.spanAttributeStorage");
1111
- function attributeStorage() {
1112
- const holder = globalThis;
1113
- let storage = holder[ATTRIBUTE_STORAGE_KEY];
1114
- if (!storage) {
1115
- storage = new ContextStackStorage(NO_AMBIENT_ATTRIBUTE_BIND_WARNING);
1116
- holder[ATTRIBUTE_STORAGE_KEY] = storage;
1117
- }
1118
- return storage;
1297
+ var LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS = 2e3;
1298
+ function mirrorTraceExportToLocalDebugger(body, options = {}) {
1299
+ var _a;
1300
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
1301
+ if (!baseUrl) return Promise.resolve();
1302
+ return postJson(`${baseUrl}traces`, body, {}, {
1303
+ maxAttempts: 1,
1304
+ debug: (_a = options.debug) != null ? _a : false,
1305
+ sdkName: options.sdkName,
1306
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
1307
+ }).catch(() => {
1308
+ });
1119
1309
  }
1120
- function makeAttributeFrame(attributes, owner) {
1121
- const snapshot = { ...attributes };
1122
- const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
1123
- const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
1124
- const owned = /* @__PURE__ */ new WeakSet();
1125
- let dead = false;
1126
- let sawError = false;
1127
- return {
1128
- attributes: snapshot,
1129
- sawErrorSpan: () => sawError,
1130
- ownsSpan: (span) => owned.has(span),
1131
- claimSpan(span) {
1132
- owned.add(span);
1133
- },
1134
- markErrorSpanSeen() {
1135
- sawError = true;
1136
- },
1137
- // A frame whose owner has been collected is dead even though nothing
1138
- // unbound it: a run that never settles must not keep handing its reverse
1139
- // reference to whatever runs next on a reused context.
1140
- isLive() {
1141
- if (dead) return false;
1142
- return ref === void 0 || ref.deref() !== void 0;
1310
+ function mirrorPartialEventToLocalDebugger(event, options = {}) {
1311
+ var _a;
1312
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
1313
+ if (!baseUrl) return Promise.resolve();
1314
+ const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
1315
+ return postJson(`${baseUrl}events/track_partial`, event, headers, {
1316
+ maxAttempts: 1,
1317
+ debug: (_a = options.debug) != null ? _a : false,
1318
+ sdkName: options.sdkName,
1319
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
1320
+ }).catch(() => {
1321
+ });
1322
+ }
1323
+ function sendLocalDebuggerLiveEvent(event, options = {}) {
1324
+ var _a, _b;
1325
+ const destination = currentReplayTraceDestination();
1326
+ if (destination && destination.kind !== "workshop") return;
1327
+ const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
1328
+ if (!baseUrl) return;
1329
+ void postJson(
1330
+ `${baseUrl}live`,
1331
+ {
1332
+ ...event,
1333
+ type: event.type,
1334
+ timestamp: (_a = event.timestamp) != null ? _a : Date.now()
1143
1335
  },
1144
- markDead() {
1145
- dead = true;
1336
+ {},
1337
+ {
1338
+ maxAttempts: 1,
1339
+ debug: (_b = options.debug) != null ? _b : false,
1340
+ sdkName: options.sdkName
1146
1341
  }
1147
- };
1148
- }
1149
- function bindSpanAttributes(attributes, owner) {
1150
- if (Object.keys(attributes).length === 0) return void 0;
1151
- const frame = makeAttributeFrame(attributes, owner);
1152
- try {
1153
- attributeStorage().push(frame);
1154
- } catch (e) {
1155
- return void 0;
1156
- }
1157
- return frame;
1342
+ ).catch(() => {
1343
+ });
1158
1344
  }
1159
- function unbindSpanAttributes(frame) {
1160
- if (!frame || !isAttributeFrame(frame)) return;
1161
- try {
1162
- attributeStorage().remove(frame);
1163
- } catch (e) {
1164
- }
1345
+ var PROJECT_ID_HEADER = "X-Raindrop-Project-Id";
1346
+ var PROJECT_ID_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
1347
+ function isValidProjectIdSlug(value) {
1348
+ return PROJECT_ID_SLUG_PATTERN.test(value);
1165
1349
  }
1166
- function applySpanAttributeFrames(span, setAttribute) {
1167
- try {
1168
- for (const frame of attributeStorage().getStack()) {
1169
- if (!frame.isLive()) continue;
1170
- frame.claimSpan(span);
1171
- for (const [key, value] of Object.entries(frame.attributes)) {
1172
- setAttribute(key, value);
1173
- }
1174
- }
1175
- } catch (e) {
1350
+ function normalizeProjectId(raw, opts) {
1351
+ if (typeof raw !== "string") return void 0;
1352
+ const trimmed = raw.trim();
1353
+ if (!trimmed) return void 0;
1354
+ if (!isValidProjectIdSlug(trimmed) && opts.debug) {
1355
+ console.warn(
1356
+ `${opts.prefix} projectId "${trimmed}" does not match slug ${PROJECT_ID_SLUG_PATTERN.source}; sending anyway \u2014 backend may reject with HTTP 400`
1357
+ );
1176
1358
  }
1359
+ return trimmed;
1177
1360
  }
1178
- function claimSpanForFrames(span) {
1179
- try {
1180
- for (const frame of attributeStorage().getStack()) {
1181
- if (frame.isLive()) frame.claimSpan(span);
1182
- }
1183
- } catch (e) {
1184
- }
1361
+ function projectIdHeaders(projectId) {
1362
+ return projectId ? { [PROJECT_ID_HEADER]: projectId } : {};
1185
1363
  }
1186
- function currentSpanAttributes() {
1187
- const merged = {};
1188
- try {
1189
- for (const frame of attributeStorage().getStack()) {
1190
- if (!frame.isLive()) continue;
1191
- Object.assign(merged, frame.attributes);
1192
- }
1193
- } catch (e) {
1194
- }
1195
- return merged;
1364
+ var SHUTDOWN_DEADLINE_MS = 1e4;
1365
+ var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
1366
+ var MODEL_USAGE_ATTRIBUTES = {
1367
+ usageSource: "raindrop.usage.source",
1368
+ providerName: "gen_ai.provider.name",
1369
+ requestModel: "gen_ai.request.model",
1370
+ responseModel: "gen_ai.response.model",
1371
+ inputTokens: "gen_ai.usage.input_tokens",
1372
+ outputTokens: "gen_ai.usage.output_tokens",
1373
+ reasoningTokens: "gen_ai.usage.reasoning_tokens",
1374
+ cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
1375
+ cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
1376
+ providerReportedCost: "gen_ai.usage.provider_reported_cost",
1377
+ nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
1378
+ nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
1379
+ totalOutputTokens: "raindrop.usage.output_tokens.total",
1380
+ reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
1381
+ cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
1382
+ cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
1383
+ };
1384
+ var MODEL_PROVIDER_NAMES = [
1385
+ ["google.vertex.anthropic", "google-vertex"],
1386
+ ["google.vertex_ai", "google-vertex"],
1387
+ ["google.vertex", "google-vertex"],
1388
+ ["gcp.vertex_ai", "google-vertex"],
1389
+ ["gcp.gemini", "google"],
1390
+ ["google_vertexai", "google-vertex"],
1391
+ ["vertex.anthropic", "google-vertex"],
1392
+ ["amazon-bedrock", "amazon-bedrock"],
1393
+ ["aws.bedrock", "amazon-bedrock"],
1394
+ ["bedrock", "amazon-bedrock"],
1395
+ ["anthropic", "anthropic"],
1396
+ ["openai", "openai"],
1397
+ ["google", "google"],
1398
+ ["az.ai.openai", "azure"],
1399
+ ["azure", "azure"],
1400
+ ["openrouter", "openrouter"]
1401
+ ];
1402
+ function canonicalModelProvider(provider) {
1403
+ var _a;
1404
+ const normalizedProvider = provider.trim().toLowerCase();
1405
+ if (normalizedProvider === "gateway") return "vercel";
1406
+ const match = MODEL_PROVIDER_NAMES.find(
1407
+ ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
1408
+ );
1409
+ return (_a = match == null ? void 0 : match[1]) != null ? _a : normalizedProvider;
1196
1410
  }
1197
- function markErrorSpanSeen(span) {
1198
- try {
1199
- for (const frame of attributeStorage().getStack()) {
1200
- if (frame.ownsSpan(span)) frame.markErrorSpanSeen();
1201
- }
1202
- } catch (e) {
1203
- }
1411
+ function tokenCount(value) {
1412
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
1204
1413
  }
1205
- function isAttributeFrame(value) {
1206
- return typeof value.isLive === "function";
1414
+ function buildRawModelUsageAttributes(usage) {
1415
+ const providerReportedCost = typeof usage.providerReportedCost === "number" && Number.isFinite(usage.providerReportedCost) && usage.providerReportedCost >= 0 ? usage.providerReportedCost : void 0;
1416
+ return [
1417
+ attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, tokenCount(usage.inputTokens)),
1418
+ attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, tokenCount(usage.outputTokens)),
1419
+ attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, tokenCount(usage.reasoningTokens)),
1420
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, tokenCount(usage.cacheReadInputTokens)),
1421
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, tokenCount(usage.cacheWriteInputTokens)),
1422
+ attrDouble(MODEL_USAGE_ATTRIBUTES.providerReportedCost, providerReportedCost)
1423
+ ].filter((attribute) => attribute !== void 0);
1207
1424
  }
1425
+ var STRING_ALIASES = {
1426
+ [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
1427
+ };
1428
+ var NUMBER_ALIASES = {
1429
+ [MODEL_USAGE_ATTRIBUTES.inputTokens]: [
1430
+ "gen_ai.usage.prompt_tokens",
1431
+ "ai.usage.prompt_tokens",
1432
+ "ai.usage.promptTokens",
1433
+ "ai.usage.input_tokens",
1434
+ "ai.usage.inputTokens"
1435
+ ],
1436
+ [MODEL_USAGE_ATTRIBUTES.outputTokens]: [
1437
+ "gen_ai.usage.completion_tokens",
1438
+ "ai.usage.completion_tokens",
1439
+ "ai.usage.completionTokens",
1440
+ "ai.usage.output_tokens",
1441
+ "ai.usage.outputTokens"
1442
+ ],
1443
+ [MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
1444
+ "gen_ai.usage.reasoning.output_tokens",
1445
+ "ai.usage.reasoningTokens",
1446
+ "ai.usage.thoughts_tokens"
1447
+ ],
1448
+ [MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
1449
+ "gen_ai.usage.cache_read_tokens",
1450
+ "gen_ai.usage.cache_read.input_tokens",
1451
+ "ai.usage.cachedInputTokens",
1452
+ "ai.usage.cached_tokens",
1453
+ "ai.usage.cache_read_tokens",
1454
+ "ai.usage.cache_read_input_tokens",
1455
+ "ai.usage.cacheReadInputTokens"
1456
+ ],
1457
+ [MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
1458
+ "gen_ai.usage.cache_creation_input_tokens",
1459
+ "gen_ai.usage.cache_creation.input_tokens",
1460
+ "ai.usage.cacheWriteInputTokens",
1461
+ "ai.usage.cache_creation_input_tokens",
1462
+ "ai.usage.cacheCreationInputTokens"
1463
+ ]
1464
+ };
1465
+ var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
1466
+ "ai.request.providerOptions",
1467
+ "ai.response.providerMetadata"
1468
+ ];
1469
+ var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
1208
1470
 
1209
1471
  // ../core/dist/index.node.js
1210
1472
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
1473
+ import { execFile } from "child_process";
1474
+ import { resolve } from "path";
1475
+ var resolveLocal = (sourceDirectory, detectBranch, completed) => {
1476
+ const cwd = sourceDirectory ? resolve(sourceDirectory) : process.cwd();
1477
+ let cancelled = false;
1478
+ let active;
1479
+ let deadlineTimer;
1480
+ const deadline = Date.now() + 1e3;
1481
+ const run = (args, done) => {
1482
+ if (cancelled) return;
1483
+ const remaining = deadline - Date.now();
1484
+ if (remaining <= 0) {
1485
+ done(void 0);
1486
+ return;
1487
+ }
1488
+ try {
1489
+ active = execFile(
1490
+ "git",
1491
+ args,
1492
+ {
1493
+ cwd,
1494
+ encoding: "utf8",
1495
+ maxBuffer: 4096,
1496
+ windowsHide: true,
1497
+ env: {
1498
+ ...process.env,
1499
+ // The selected application cwd, not the invoking shell's Git plumbing, owns identity.
1500
+ GIT_DIR: void 0,
1501
+ GIT_WORK_TREE: void 0,
1502
+ GIT_COMMON_DIR: void 0,
1503
+ GIT_NAMESPACE: void 0,
1504
+ GIT_INDEX_FILE: void 0,
1505
+ GIT_OBJECT_DIRECTORY: void 0,
1506
+ GIT_ALTERNATE_OBJECT_DIRECTORIES: void 0,
1507
+ GIT_CONFIG_PARAMETERS: void 0,
1508
+ GIT_CONFIG_COUNT: "0",
1509
+ GIT_TERMINAL_PROMPT: "0",
1510
+ GIT_OPTIONAL_LOCKS: "0"
1511
+ }
1512
+ },
1513
+ (error, stdout) => {
1514
+ clearTimeout(deadlineTimer);
1515
+ deadlineTimer = void 0;
1516
+ active = void 0;
1517
+ if (!cancelled) {
1518
+ try {
1519
+ done(error ? void 0 : stdout.trim());
1520
+ } catch (e) {
1521
+ }
1522
+ }
1523
+ }
1524
+ );
1525
+ active.unref();
1526
+ if (active.stdout && "unref" in active.stdout && typeof active.stdout.unref === "function")
1527
+ active.stdout.unref();
1528
+ if (active.stderr && "unref" in active.stderr && typeof active.stderr.unref === "function")
1529
+ active.stderr.unref();
1530
+ deadlineTimer = setTimeout(() => {
1531
+ try {
1532
+ active == null ? void 0 : active.kill("SIGKILL");
1533
+ } catch (e) {
1534
+ }
1535
+ }, remaining);
1536
+ deadlineTimer.unref();
1537
+ } catch (e) {
1538
+ try {
1539
+ done(void 0);
1540
+ } catch (e2) {
1541
+ }
1542
+ }
1543
+ };
1544
+ const scheduled = setTimeout(() => {
1545
+ try {
1546
+ run(["rev-parse", "HEAD"], (rawSha) => {
1547
+ const commitSha = validAppCommitSha(rawSha);
1548
+ if (!commitSha) {
1549
+ completed({});
1550
+ return;
1551
+ }
1552
+ run(["-c", "core.fsmonitor=false", "status", "--porcelain"], (status) => {
1553
+ const finish = (branch) => {
1554
+ run(["rev-parse", "HEAD"], (finalSha) => {
1555
+ const coherent = validAppCommitSha(finalSha) === commitSha;
1556
+ completed({
1557
+ commitSha,
1558
+ ...coherent && status !== void 0 ? { commitDirty: status !== "" } : {},
1559
+ ...coherent && branch && branch !== "HEAD" ? { branch } : {}
1560
+ });
1561
+ });
1562
+ };
1563
+ if (detectBranch) run(["symbolic-ref", "--quiet", "--short", "HEAD"], finish);
1564
+ else finish();
1565
+ });
1566
+ });
1567
+ } catch (e) {
1568
+ completed({});
1569
+ }
1570
+ }, 0);
1571
+ scheduled.unref();
1572
+ return () => {
1573
+ cancelled = true;
1574
+ clearTimeout(scheduled);
1575
+ clearTimeout(deadlineTimer);
1576
+ try {
1577
+ active == null ? void 0 : active.kill("SIGKILL");
1578
+ } catch (e) {
1579
+ }
1580
+ };
1581
+ };
1582
+ function createAppGitContext(options, runtime = {}) {
1583
+ return new AppGitContext(options, { ...runtime, resolveLocal });
1584
+ }
1211
1585
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage2;
1212
1586
  var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
1213
1587
  "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
@@ -1238,7 +1612,7 @@ installTracingSuppressionHook();
1238
1612
  // package.json
1239
1613
  var package_default = {
1240
1614
  name: "raindrop-ai",
1241
- version: "0.7.0",
1615
+ version: "0.7.1",
1242
1616
  main: "dist/index.js",
1243
1617
  module: "dist/index.mjs",
1244
1618
  types: "dist/index.d.ts",
@@ -1577,11 +1951,38 @@ function stampContextAttributes(span) {
1577
1951
  }
1578
1952
  }
1579
1953
  var RaindropContextSpanProcessor = class {
1954
+ constructor(appGitSnapshot) {
1955
+ this.appGitSnapshot = appGitSnapshot;
1956
+ this.appGitAdded = /* @__PURE__ */ new WeakMap();
1957
+ }
1580
1958
  onStart(span) {
1959
+ var _a, _b, _c, _d;
1581
1960
  try {
1582
1961
  stampContextAttributes(span);
1583
1962
  rememberReplayTraceDestination(span);
1584
1963
  const bound = currentRoutingBinding();
1964
+ const keys = ["raindrop.app.commit_sha", "raindrop.app.commit_dirty", "raindrop.app.branch"];
1965
+ const attrs = (_a = span.attributes) != null ? _a : {};
1966
+ const properties = {};
1967
+ for (const key of keys) {
1968
+ const existing = [key, `ai.telemetry.metadata.${key}`, `traceloop.association.properties.${key}`].find((name) => Object.prototype.hasOwnProperty.call(attrs, name));
1969
+ if (existing) properties[key] = attrs[existing];
1970
+ }
1971
+ const source = { ...(_d = (_c = (_b = this.appGitSnapshot) == null ? void 0 : _b.call(this, properties)) != null ? _c : bound == null ? void 0 : bound.appGit) != null ? _d : {} };
1972
+ if (Object.prototype.hasOwnProperty.call(properties, "raindrop.app.commit_sha") && properties["raindrop.app.commit_sha"] !== source["raindrop.app.commit_sha"]) {
1973
+ delete source["raindrop.app.commit_dirty"];
1974
+ delete source["raindrop.app.branch"];
1975
+ }
1976
+ const added = /* @__PURE__ */ new Map();
1977
+ for (const key of keys) {
1978
+ if ([key, `ai.telemetry.metadata.${key}`, `traceloop.association.properties.${key}`].some((name) => Object.prototype.hasOwnProperty.call(attrs, name))) continue;
1979
+ const value = source == null ? void 0 : source[key];
1980
+ if (typeof value === "string" || typeof value === "boolean") {
1981
+ span.setAttribute(key, value);
1982
+ added.set(key, value);
1983
+ }
1984
+ }
1985
+ this.appGitAdded.set(span, added);
1585
1986
  if (!bound) return;
1586
1987
  stampSpan(span, bound.projectId, bound.authHint);
1587
1988
  } catch (e) {
@@ -1590,6 +1991,16 @@ var RaindropContextSpanProcessor = class {
1590
1991
  onEnd(span) {
1591
1992
  var _a;
1592
1993
  try {
1994
+ if (span == null ? void 0 : span.attributes) {
1995
+ const attrs = span.attributes;
1996
+ const added = this.appGitAdded.get(span);
1997
+ const shaKey = "raindrop.app.commit_sha";
1998
+ const hasAlias = (key) => [`ai.telemetry.metadata.${key}`, `traceloop.association.properties.${key}`].some((name) => Object.prototype.hasOwnProperty.call(attrs, name));
1999
+ const shaChanged = (added == null ? void 0 : added.has(shaKey)) && (attrs[shaKey] !== added.get(shaKey) || hasAlias(shaKey));
2000
+ for (const [key, value] of added != null ? added : []) {
2001
+ if (attrs[key] === value && (hasAlias(key) || shaChanged && key !== shaKey)) delete attrs[key];
2002
+ }
2003
+ }
1593
2004
  if (((_a = span == null ? void 0 : span.status) == null ? void 0 : _a.code) !== SPAN_STATUS_ERROR) return;
1594
2005
  markErrorSpanSeen(span);
1595
2006
  } catch (e) {
@@ -1720,7 +2131,7 @@ import { WeakValueMap } from "weakref";
1720
2131
 
1721
2132
  // src/tracing/direct/http.ts
1722
2133
  function wait2(ms) {
1723
- return new Promise((resolve) => setTimeout(resolve, ms));
2134
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
1724
2135
  }
1725
2136
  function formatEndpoint2(endpoint) {
1726
2137
  if (!endpoint) return void 0;
@@ -1965,6 +2376,7 @@ var DirectTraceShipper = class {
1965
2376
  */
1966
2377
  this.hasShutdown = false;
1967
2378
  var _a, _b, _c, _d, _e, _f;
2379
+ this.appGitSnapshot = opts.appGitSnapshot;
1968
2380
  this.writeKey = opts.writeKey;
1969
2381
  this.baseUrl = (_a = formatEndpoint2(opts.endpoint)) != null ? _a : "https://api.raindrop.ai/v1/";
1970
2382
  this.enabled = (_b = opts.enabled) != null ? _b : true;
@@ -1979,6 +2391,14 @@ var DirectTraceShipper = class {
1979
2391
  this.localDebuggerUrl = resolved;
1980
2392
  }
1981
2393
  }
2394
+ applicationGitSnapshot(properties) {
2395
+ var _a, _b, _c, _d;
2396
+ try {
2397
+ return (_d = (_c = (_a = this.appGitSnapshot) == null ? void 0 : _a.call(this, properties)) != null ? _c : (_b = currentRoutingBinding()) == null ? void 0 : _b.appGit) != null ? _d : {};
2398
+ } catch (e) {
2399
+ return {};
2400
+ }
2401
+ }
1982
2402
  /**
1983
2403
  * Build and enqueue an OTLP span for a trackTool call.
1984
2404
  *
@@ -2053,7 +2473,10 @@ var DirectTraceShipper = class {
2053
2473
  name: params.name,
2054
2474
  startTimeUnixNano,
2055
2475
  endTimeUnixNano,
2056
- attributes,
2476
+ attributes: withAppGitAttributes(
2477
+ attributes,
2478
+ params.appGitSnapshot !== void 0 ? params.appGitSnapshot : this.applicationGitSnapshot(params.properties)
2479
+ ),
2057
2480
  status
2058
2481
  });
2059
2482
  const replayDestination = currentReplayTraceDestination();
@@ -3694,7 +4117,7 @@ var LiveInteraction = class {
3694
4117
  span.end();
3695
4118
  }
3696
4119
  async finish(resultEvent) {
3697
- var _a, _b, _c, _d, _e, _f, _g;
4120
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
3698
4121
  unbindRoutingContext(this.boundContext);
3699
4122
  this.boundContext = void 0;
3700
4123
  unbindSpanAttributes(this.toolEventsFrame);
@@ -3704,10 +4127,19 @@ var LiveInteraction = class {
3704
4127
  }
3705
4128
  const eventId = resultEvent.eventId || this.context.eventId;
3706
4129
  if (!this.usageEmitted) {
3707
- (_c = this.analytics) == null ? void 0 : _c._recordModelUsage(
4130
+ const appGitSnapshot = (_b = (_a = this.analytics) == null ? void 0 : _a.getAppGitSnapshot(resultEvent.properties, eventId)) != null ? _b : {};
4131
+ const usageProperties = {};
4132
+ for (const key of [APP_COMMIT_SHA, APP_COMMIT_DIRTY, APP_BRANCH]) {
4133
+ if (resultEvent.properties && Object.prototype.hasOwnProperty.call(resultEvent.properties, key)) {
4134
+ usageProperties[key] = resultEvent.properties[key];
4135
+ }
4136
+ }
4137
+ (_e = this.analytics) == null ? void 0 : _e._recordModelUsage(
3708
4138
  eventId,
3709
- (_a = resultEvent.model) != null ? _a : this.context.model,
3710
- (_b = resultEvent.usage) != null ? _b : this.context.usage
4139
+ (_c = resultEvent.model) != null ? _c : this.context.model,
4140
+ (_d = resultEvent.usage) != null ? _d : this.context.usage,
4141
+ withAppGitProperties(usageProperties, appGitSnapshot),
4142
+ appGitSnapshot
3711
4143
  );
3712
4144
  this.usageEmitted = true;
3713
4145
  }
@@ -3718,20 +4150,20 @@ var LiveInteraction = class {
3718
4150
  } catch (e) {
3719
4151
  }
3720
4152
  })();
3721
- if ((_e = (_d = this.analytics) == null ? void 0 : _d._shouldBlockFinishFlush()) != null ? _e : true) {
4153
+ if ((_g = (_f = this.analytics) == null ? void 0 : _f._shouldBlockFinishFlush()) != null ? _g : true) {
3722
4154
  await spanFlush;
3723
4155
  } else {
3724
4156
  void spanFlush.catch(() => {
3725
4157
  });
3726
4158
  }
3727
4159
  try {
3728
- await ((_f = this.analytics) == null ? void 0 : _f._drainInFlightPartials(eventId));
4160
+ await ((_h = this.analytics) == null ? void 0 : _h._drainInFlightPartials(eventId));
3729
4161
  } catch (e) {
3730
4162
  }
3731
4163
  if (this.traceId) {
3732
4164
  this.setProperty("trace_id", this.traceId);
3733
4165
  }
3734
- await ((_g = this.analytics) == null ? void 0 : _g._trackAiPartial({
4166
+ await ((_i = this.analytics) == null ? void 0 : _i._trackAiPartial({
3735
4167
  ...resultEvent,
3736
4168
  eventId,
3737
4169
  isPending: false
@@ -4538,6 +4970,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
4538
4970
  }
4539
4971
  }
4540
4972
  const directShipper = bypassOtelForTools ? new DirectTraceShipper({
4973
+ appGitSnapshot: (properties) => analytics.getAppGitSnapshot(properties),
4541
4974
  writeKey,
4542
4975
  endpoint: directEndpoint != null ? directEndpoint : apiUrl,
4543
4976
  debug: isDebug,
@@ -4597,7 +5030,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
4597
5030
  tracingEnabled: tracingEnabled !== false,
4598
5031
  disableBatch: disableBatching,
4599
5032
  processor: new CompositeSpanProcessor(
4600
- [new RaindropContextSpanProcessor(), localDebuggerProcessor],
5033
+ [new RaindropContextSpanProcessor((properties) => analytics.getAppGitSnapshot(properties)), localDebuggerProcessor],
4601
5034
  []
4602
5035
  ),
4603
5036
  instrumentations: createConfiguredInstrumentations(otherOptions)
@@ -4621,7 +5054,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
4621
5054
  tracingEnabled: tracingEnabled !== false,
4622
5055
  disableBatch: disableBatching,
4623
5056
  processor: new CompositeSpanProcessor(
4624
- [new RaindropContextSpanProcessor(), replayOnlyProcessor],
5057
+ [new RaindropContextSpanProcessor((properties) => analytics.getAppGitSnapshot(properties)), replayOnlyProcessor],
4625
5058
  []
4626
5059
  ),
4627
5060
  instrumentations: createConfiguredInstrumentations(otherOptions)
@@ -4662,7 +5095,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
4662
5095
  );
4663
5096
  }
4664
5097
  if (guardedExporter) {
4665
- const contextProcessor = new RaindropContextSpanProcessor();
5098
+ const contextProcessor = new RaindropContextSpanProcessor((properties) => analytics.getAppGitSnapshot(properties));
4666
5099
  const callerProcessor = otherOptions.processor;
4667
5100
  const processor = new CompositeSpanProcessor(
4668
5101
  callerProcessor ? [contextProcessor, callerProcessor] : [contextProcessor],
@@ -4725,9 +5158,9 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
4725
5158
  traceId,
4726
5159
  analytics,
4727
5160
  directShipper,
4728
- { projectId, authHint }
5161
+ { projectId, authHint, appGit: analytics.getAppGitSnapshot(traceContext.properties, traceContext.eventId) }
4729
5162
  );
4730
- const bound = bindRoutingContext({ projectId, authHint }, interaction);
5163
+ const bound = bindRoutingContext({ projectId, authHint, appGit: analytics.getAppGitSnapshot(traceContext.properties, traceContext.eventId) }, interaction);
4731
5164
  interaction.setBoundContext(bound);
4732
5165
  try {
4733
5166
  analytics._trackAiPartial({
@@ -4804,7 +5237,8 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
4804
5237
  directShipper.sendSpan({
4805
5238
  name: "ai.model.usage",
4806
5239
  spanKind: "llm",
4807
- properties: { event_id: params.eventId },
5240
+ properties: { ...params.appGitProperties, event_id: params.eventId },
5241
+ appGitSnapshot: params.appGitSnapshot,
4808
5242
  startTimeMs: timeMs,
4809
5243
  endTimeMs: timeMs,
4810
5244
  durationMs: 0,
@@ -4812,9 +5246,23 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
4812
5246
  });
4813
5247
  return;
4814
5248
  }
4815
- const span = trace6.getTracer("traceloop.tracer").startSpan("ai.model.usage");
4816
- span.setAttribute("traceloop.span.kind", "llm");
4817
- span.setAttribute("traceloop.association.properties.event_id", params.eventId);
5249
+ const initialAttributes = {
5250
+ "traceloop.span.kind": "llm"
5251
+ };
5252
+ for (const [key, value] of Object.entries({
5253
+ ...params.appGitProperties,
5254
+ event_id: params.eventId
5255
+ })) {
5256
+ if (value !== void 0) {
5257
+ initialAttributes[`traceloop.association.properties.${key}`] = stringifyBounded(value);
5258
+ }
5259
+ }
5260
+ for (const [key, value] of Object.entries(params.appGitSnapshot)) {
5261
+ if (typeof value === "string" || typeof value === "boolean") {
5262
+ initialAttributes[key] = value;
5263
+ }
5264
+ }
5265
+ const span = trace6.getTracer("traceloop.tracer").startSpan("ai.model.usage", { attributes: initialAttributes });
4818
5266
  stampSpan(span, projectId, authHint);
4819
5267
  for (const attribute of attributes) {
4820
5268
  const value = attribute.value;
@@ -4935,7 +5383,7 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
4935
5383
  allowedInstrumentationLibraries: processorOptions == null ? void 0 : processorOptions.allowedInstrumentationLibraries,
4936
5384
  localDebuggerUrlOpt: localDebuggerUrl
4937
5385
  });
4938
- const contextProcessor = new RaindropContextSpanProcessor();
5386
+ const contextProcessor = new RaindropContextSpanProcessor((properties) => analytics.getAppGitSnapshot(properties));
4939
5387
  return withV2SpanCompat(
4940
5388
  new CompositeSpanProcessor(
4941
5389
  [contextProcessor, productionProcessor],
@@ -5013,6 +5461,15 @@ export {
5013
5461
  withEvalScope,
5014
5462
  currentEvalScope,
5015
5463
  evalScopeAttributes,
5464
+ withReplayTraceDestination,
5465
+ currentReplayTraceDestination,
5466
+ withRoutingContext,
5467
+ currentRoutingBinding,
5468
+ APP_COMMIT_SHA,
5469
+ APP_COMMIT_DIRTY,
5470
+ APP_BRANCH,
5471
+ validAppCommitSha,
5472
+ withAppGitProperties,
5016
5473
  normalizeFeatureFlags,
5017
5474
  randomUUID,
5018
5475
  runWithTracingSuppressed,
@@ -5026,8 +5483,6 @@ export {
5026
5483
  capText,
5027
5484
  boundedClone,
5028
5485
  stringifyBounded,
5029
- withReplayTraceDestination,
5030
- currentReplayTraceDestination,
5031
5486
  resolveLocalDebuggerBaseUrl,
5032
5487
  localDebuggerEnabled,
5033
5488
  mirrorPartialEventToLocalDebugger,
@@ -5035,7 +5490,7 @@ export {
5035
5490
  projectIdHeaders,
5036
5491
  SHUTDOWN_DEADLINE_MS,
5037
5492
  POST_SHUTDOWN_TIMEOUT_MS,
5038
- withRoutingContext,
5493
+ createAppGitContext,
5039
5494
  package_default,
5040
5495
  authHintForKey,
5041
5496
  withTask,