@raindrop-ai/claude-code 0.0.18 → 0.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -21,11 +21,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
23
  DEFAULT_CATEGORY_KEYS: () => DEFAULT_CATEGORY_KEYS,
24
- EventShipper: () => EventShipper2,
24
+ EventShipper: () => EventShipper3,
25
25
  PACKAGE_NAME: () => PACKAGE_NAME,
26
26
  PACKAGE_VERSION: () => PACKAGE_VERSION,
27
27
  TOOL_SCHEMA: () => TOOL_SCHEMA,
28
- TraceShipper: () => TraceShipper2,
28
+ TraceShipper: () => TraceShipper3,
29
+ createAppGitContext: () => createAppGitContext2,
29
30
  detectLocalDebugger: () => detectLocalDebugger,
30
31
  executeTool: () => executeTool,
31
32
  extractAppendSystemPrompt: () => extractAppendSystemPrompt,
@@ -43,7 +44,521 @@ __export(src_exports, {
43
44
  });
44
45
  module.exports = __toCommonJS(src_exports);
45
46
 
46
- // ../core/dist/chunk-WKRW55KX.js
47
+ // ../core/dist/chunk-BYBMRLZM.js
48
+ var EVAL_CORRELATION_ID_ATTRIBUTE = "raindrop.eval_correlation_id";
49
+ var PROBE_SCOPE = { correlationId: "" };
50
+ var EvalScopeStorage = class {
51
+ constructor() {
52
+ this._als = null;
53
+ this._probed = false;
54
+ }
55
+ maybeAdoptAsyncLocalStorage() {
56
+ if (this._probed) return;
57
+ this._probed = true;
58
+ try {
59
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
60
+ if (!Ctor) return;
61
+ const als = new Ctor();
62
+ als.run(PROBE_SCOPE, () => {
63
+ });
64
+ this._als = als;
65
+ } catch (e) {
66
+ this._als = null;
67
+ }
68
+ }
69
+ run(scope, callback) {
70
+ this.maybeAdoptAsyncLocalStorage();
71
+ if (this._als) return this._als.run(scope, callback);
72
+ const previous = this._syncScope;
73
+ this._syncScope = scope;
74
+ try {
75
+ return callback();
76
+ } finally {
77
+ this._syncScope = previous;
78
+ }
79
+ }
80
+ current() {
81
+ this.maybeAdoptAsyncLocalStorage();
82
+ if (this._als) return this._als.getStore();
83
+ return this._syncScope;
84
+ }
85
+ };
86
+ var EVAL_SCOPE_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.evalScopeStorage");
87
+ function evalScopeStorage() {
88
+ const holder = globalThis;
89
+ let storage = holder[EVAL_SCOPE_STORAGE_KEY];
90
+ if (!storage) {
91
+ storage = new EvalScopeStorage();
92
+ holder[EVAL_SCOPE_STORAGE_KEY] = storage;
93
+ }
94
+ return storage;
95
+ }
96
+ function currentEvalScope() {
97
+ try {
98
+ return evalScopeStorage().current();
99
+ } catch (e) {
100
+ return void 0;
101
+ }
102
+ }
103
+ function evalScopeAttributes() {
104
+ const scope = currentEvalScope();
105
+ if (!scope) return {};
106
+ return { [EVAL_CORRELATION_ID_ATTRIBUTE]: scope.correlationId };
107
+ }
108
+
109
+ // ../core/dist/chunk-WJOR6M7H.js
110
+ var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
111
+ function state() {
112
+ const holder = globalThis;
113
+ let current = holder[STATE_KEY];
114
+ if (!current) {
115
+ let storage = null;
116
+ try {
117
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
118
+ if (Ctor) storage = new Ctor();
119
+ } catch (e) {
120
+ storage = null;
121
+ }
122
+ current = {
123
+ storage,
124
+ syncDestination: void 0,
125
+ spanDestinations: /* @__PURE__ */ new WeakMap()
126
+ };
127
+ holder[STATE_KEY] = current;
128
+ }
129
+ return current;
130
+ }
131
+ function currentReplayTraceDestination() {
132
+ var _a, _b;
133
+ const routing = state();
134
+ return (_b = (_a = routing.storage) == null ? void 0 : _a.getStore()) != null ? _b : routing.syncDestination;
135
+ }
136
+ function rememberReplayTraceDestination(span) {
137
+ const destination = currentReplayTraceDestination();
138
+ if (destination) state().spanDestinations.set(span, destination);
139
+ }
140
+ function copyReplayTraceDestination(source, target) {
141
+ const destination = state().spanDestinations.get(source);
142
+ if (destination) state().spanDestinations.set(target, destination);
143
+ }
144
+ function replayTraceDestinationFor(value) {
145
+ if (typeof value !== "object" && typeof value !== "function" || value === null) {
146
+ return void 0;
147
+ }
148
+ return state().spanDestinations.get(value);
149
+ }
150
+ function recordReplayExportedSpan(destination, spanId, traceId) {
151
+ var _a, _b;
152
+ if (!destination || !spanId || !traceId) return;
153
+ (_a = destination.exportedSpanIds) == null ? void 0 : _a.add(spanId);
154
+ (_b = destination.exportedTraceIds) == null ? void 0 : _b.add(traceId);
155
+ }
156
+ function replayTraceUrl(baseUrl) {
157
+ return `${baseUrl.replace(/\/+$/, "")}/v1/traces`;
158
+ }
159
+ function getAsyncLocalStorageCtor() {
160
+ return globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
161
+ }
162
+ var MAX_BINDING_STACK = 128;
163
+ var EMPTY_STACK = [];
164
+ var ContextStackStorage = class {
165
+ /** Emitted once when this runtime cannot support an ambient push. */
166
+ constructor(noAmbientBindWarning) {
167
+ this.noAmbientBindWarning = noAmbientBindWarning;
168
+ this._als = null;
169
+ this._enterWithUsable = false;
170
+ this._syncStack = EMPTY_STACK;
171
+ this._adoptionChecked = false;
172
+ this._warnedNoAmbientBind = false;
173
+ }
174
+ maybeAdoptAsyncLocalStorage() {
175
+ if (this._adoptionChecked) return;
176
+ this._adoptionChecked = true;
177
+ const Ctor = getAsyncLocalStorageCtor();
178
+ if (!Ctor) return;
179
+ const als = new Ctor();
180
+ try {
181
+ als.run(EMPTY_STACK, () => {
182
+ });
183
+ } catch (e) {
184
+ return;
185
+ }
186
+ this._als = als;
187
+ if (typeof als.enterWith === "function") {
188
+ try {
189
+ als.enterWith(EMPTY_STACK);
190
+ this._enterWithUsable = true;
191
+ } catch (e) {
192
+ this._enterWithUsable = false;
193
+ }
194
+ }
195
+ }
196
+ getStack() {
197
+ var _a;
198
+ this.maybeAdoptAsyncLocalStorage();
199
+ if (this._als) return (_a = this._als.getStore()) != null ? _a : this._syncStack;
200
+ return this._syncStack;
201
+ }
202
+ /**
203
+ * Persist an ambient stack mutation. Returns false when an ALS is adopted but
204
+ * `enterWith` is unusable (workerd): callers must NOT fall back to the
205
+ * process-shared sync stack, since concurrent isolates would cross-stamp.
206
+ */
207
+ setAmbientStack(stack) {
208
+ if (this._als) {
209
+ if (!this._enterWithUsable) return false;
210
+ this._als.enterWith(stack);
211
+ return true;
212
+ }
213
+ this._syncStack = stack;
214
+ return true;
215
+ }
216
+ push(binding) {
217
+ this.maybeAdoptAsyncLocalStorage();
218
+ if (this._als && !this._enterWithUsable) {
219
+ this.warnNoAmbientBindOnce();
220
+ return;
221
+ }
222
+ let stack = this.getStack();
223
+ if (stack.length >= MAX_BINDING_STACK) {
224
+ stack = stack.filter((b) => b.isLive());
225
+ if (stack.length >= MAX_BINDING_STACK) {
226
+ stack = stack.slice(stack.length - (MAX_BINDING_STACK - 1));
227
+ }
228
+ }
229
+ this.setAmbientStack([...stack, binding]);
230
+ }
231
+ remove(binding) {
232
+ binding.markDead();
233
+ const stack = this.getStack();
234
+ const pruned = stack.filter((b) => b !== binding && b.isLive());
235
+ if (pruned.length !== stack.length) {
236
+ this.setAmbientStack(pruned);
237
+ }
238
+ }
239
+ runWith(binding, callback) {
240
+ this.maybeAdoptAsyncLocalStorage();
241
+ if (this._als) {
242
+ return this._als.run([...this.getStack(), binding], callback);
243
+ }
244
+ const previous = this._syncStack;
245
+ this._syncStack = [...previous, binding];
246
+ try {
247
+ return callback();
248
+ } finally {
249
+ this._syncStack = previous;
250
+ }
251
+ }
252
+ current() {
253
+ const stack = this.getStack();
254
+ if (stack.length === 0) return void 0;
255
+ const top = stack[stack.length - 1];
256
+ if (top.isLive()) return top;
257
+ const pruned = stack.filter((b) => b.isLive());
258
+ this.setAmbientStack(pruned);
259
+ return pruned.length > 0 ? pruned[pruned.length - 1] : void 0;
260
+ }
261
+ warnNoAmbientBindOnce() {
262
+ if (this._warnedNoAmbientBind) return;
263
+ this._warnedNoAmbientBind = true;
264
+ console.warn(this.noAmbientBindWarning);
265
+ }
266
+ };
267
+ 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.";
268
+ var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
269
+ function routingStorage() {
270
+ const holder = globalThis;
271
+ let storage = holder[ROUTING_STORAGE_KEY];
272
+ if (!storage) {
273
+ storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
274
+ holder[ROUTING_STORAGE_KEY] = storage;
275
+ }
276
+ return storage;
277
+ }
278
+ function currentRoutingBinding() {
279
+ const bound = routingStorage().current();
280
+ if (!bound) return void 0;
281
+ return {
282
+ projectId: bound.projectId,
283
+ authHint: bound.authHint,
284
+ ...bound.appGit ? { appGit: bound.appGit } : {},
285
+ ...bound.appGitSource ? { appGitSource: bound.appGitSource } : {}
286
+ };
287
+ }
288
+ var APP_COMMIT_SHA = "raindrop.app.commit_sha";
289
+ var APP_COMMIT_DIRTY = "raindrop.app.commit_dirty";
290
+ var APP_BRANCH = "raindrop.app.branch";
291
+ var APP_GIT_KEYS = [APP_COMMIT_SHA, APP_COMMIT_DIRTY, APP_BRANCH];
292
+ var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
293
+ function appGitExplicitKeys(properties) {
294
+ const keys = /* @__PURE__ */ new Set();
295
+ if (!properties) return keys;
296
+ for (const key of APP_GIT_KEYS) {
297
+ if (own(properties, key)) keys.add(key);
298
+ }
299
+ return keys;
300
+ }
301
+ function mergeAppGitSnapshot(base, properties, explicitKeys = /* @__PURE__ */ new Set()) {
302
+ try {
303
+ const result = { ...base };
304
+ if (properties && own(properties, APP_COMMIT_SHA) && properties[APP_COMMIT_SHA] !== result[APP_COMMIT_SHA]) {
305
+ if (!explicitKeys.has(APP_COMMIT_DIRTY) && !own(properties, APP_COMMIT_DIRTY)) {
306
+ delete result[APP_COMMIT_DIRTY];
307
+ }
308
+ if (!explicitKeys.has(APP_BRANCH) && !own(properties, APP_BRANCH)) {
309
+ delete result[APP_BRANCH];
310
+ }
311
+ }
312
+ for (const key of APP_GIT_KEYS) {
313
+ if (properties && own(properties, key)) result[key] = properties[key];
314
+ }
315
+ return Object.freeze(result);
316
+ } catch (e) {
317
+ return Object.freeze({});
318
+ }
319
+ }
320
+ function validAppCommitSha(value) {
321
+ if (typeof value !== "string" || value.length > 128) return void 0;
322
+ const cleaned = value.trim();
323
+ return /^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/.test(cleaned) ? cleaned.toLowerCase() : void 0;
324
+ }
325
+ function shortString(value) {
326
+ if (typeof value !== "string" || value.length > 1024) return void 0;
327
+ const cleaned = value.trim();
328
+ return cleaned.length > 0 ? cleaned : void 0;
329
+ }
330
+ function flag(value) {
331
+ if (value === true || value === "true" || value === "1") return true;
332
+ if (value === false || value === "false" || value === "0") return false;
333
+ return void 0;
334
+ }
335
+ function environment() {
336
+ try {
337
+ return typeof process === "undefined" ? {} : process.env;
338
+ } catch (e) {
339
+ return {};
340
+ }
341
+ }
342
+ var DEPLOYMENTS = [
343
+ ["VERCEL", "VERCEL_GIT_COMMIT_SHA", "VERCEL_GIT_COMMIT_REF"],
344
+ ["NETLIFY", "COMMIT_REF", "BRANCH"],
345
+ ["RAILWAY_ENVIRONMENT_NAME", "RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH"],
346
+ ["RAILWAY_ENVIRONMENT", "RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH"],
347
+ ["RENDER", "RENDER_GIT_COMMIT", "RENDER_GIT_BRANCH"],
348
+ ["DYNO", "HEROKU_BUILD_COMMIT", ""],
349
+ ["DYNO", "HEROKU_SLUG_COMMIT", ""],
350
+ ["AWS_APP_ID", "AWS_COMMIT_ID", "AWS_BRANCH"],
351
+ ["WORKERS_CI", "WORKERS_CI_COMMIT_SHA", "WORKERS_CI_BRANCH"]
352
+ ];
353
+ var CI_SOURCES = [
354
+ ["GITHUB_ACTIONS", "GITHUB_SHA", "GITHUB_REF_NAME"],
355
+ ["GITLAB_CI", "CI_COMMIT_SHA", "CI_COMMIT_REF_NAME"],
356
+ ["CIRCLECI", "CIRCLE_SHA1", "CIRCLE_BRANCH"]
357
+ ];
358
+ function detectedSource(env, table, detectBranch) {
359
+ for (const row of table) {
360
+ const [marker, shaKey, branchKey] = row;
361
+ if (!marker || !shaKey || !shortString(env[marker]) || flag(env[marker]) === false) continue;
362
+ const commitSha = validAppCommitSha(env[shaKey]);
363
+ if (commitSha)
364
+ return {
365
+ commitSha,
366
+ ...detectBranch && branchKey ? { branch: shortString(env[branchKey]) } : {}
367
+ };
368
+ }
369
+ return {};
370
+ }
371
+ var AppGitContextImpl = class {
372
+ constructor(options, runtime = {}) {
373
+ this.defaults = {};
374
+ this.explicit = {};
375
+ this.explicitKeys = /* @__PURE__ */ new Set();
376
+ this.disposed = false;
377
+ this.operations = /* @__PURE__ */ new Map();
378
+ this.operationOverflow = false;
379
+ this.detectBranch = false;
380
+ this.disabled = false;
381
+ var _a, _b, _c, _d, _e, _f;
382
+ try {
383
+ if (options === false) {
384
+ this.disabled = true;
385
+ return;
386
+ }
387
+ const opts = options != null ? options : {};
388
+ const env = (_a = runtime.env) != null ? _a : environment();
389
+ const detectBranch = (_c = (_b = flag(opts.detectBranch)) != null ? _b : flag(env.RAINDROP_GIT_DETECT_BRANCH)) != null ? _c : false;
390
+ this.detectBranch = detectBranch;
391
+ const collectBranch = detectBranch || runtime.captureLegacyReplayBranch === true;
392
+ const autoDetect = runtime.allowDiscovery === false ? false : (_e = (_d = flag(opts.autoDetect)) != null ? _d : flag(env.RAINDROP_GIT_AUTO_DETECT)) != null ? _e : true;
393
+ const configuredSha = opts.commitSha;
394
+ const explicitSha = configuredSha === void 0 ? env.RAINDROP_COMMIT_SHA : configuredSha;
395
+ if (explicitSha === null) this.explicit[APP_COMMIT_SHA] = null;
396
+ else if (typeof explicitSha === "string" && validAppCommitSha(explicitSha))
397
+ this.explicit[APP_COMMIT_SHA] = explicitSha;
398
+ const configuredDirty = opts.commitDirty;
399
+ const explicitDirty = configuredDirty === void 0 ? flag(env.RAINDROP_COMMIT_DIRTY) : configuredDirty;
400
+ if (explicitDirty === null || typeof explicitDirty === "boolean")
401
+ this.explicit[APP_COMMIT_DIRTY] = explicitDirty;
402
+ const configuredBranch = opts.branch;
403
+ const explicitBranch = configuredBranch === void 0 ? env.RAINDROP_BRANCH : configuredBranch;
404
+ if (explicitBranch === null) this.explicit[APP_BRANCH] = null;
405
+ else if (typeof explicitBranch === "string" && explicitBranch.length <= 1024)
406
+ this.explicit[APP_BRANCH] = explicitBranch;
407
+ this.explicitKeys = appGitExplicitKeys(this.explicit);
408
+ this.defaults = { ...this.explicit };
409
+ if (!autoDetect || explicitSha !== void 0) return;
410
+ const sourceDirectory = (_f = shortString(opts.sourceDirectory)) != null ? _f : shortString(env.RAINDROP_GIT_SOURCE_DIRECTORY);
411
+ if (runtime.allowLocalGit === false && !sourceDirectory) return;
412
+ const deploy = sourceDirectory ? {} : detectedSource(env, DEPLOYMENTS, collectBranch);
413
+ if (deploy.commitSha) {
414
+ this.accept(deploy);
415
+ return;
416
+ }
417
+ const fallback = sourceDirectory ? {} : detectedSource(env, CI_SOURCES, collectBranch);
418
+ if (runtime.resolveLocal && (runtime.allowLocalGit !== false || sourceDirectory)) {
419
+ this.cancel = runtime.resolveLocal(sourceDirectory, collectBranch, (result) => {
420
+ try {
421
+ if (!this.disposed) this.accept(result.commitSha ? result : fallback);
422
+ } catch (e) {
423
+ }
424
+ });
425
+ } else this.accept(fallback);
426
+ } catch (e) {
427
+ }
428
+ }
429
+ accept(result) {
430
+ const sha = validAppCommitSha(result.commitSha);
431
+ if (!sha) return;
432
+ this.replayBranch = shortString(result.branch);
433
+ this.defaults = {
434
+ [APP_COMMIT_SHA]: sha,
435
+ ...typeof result.commitDirty === "boolean" ? { [APP_COMMIT_DIRTY]: result.commitDirty } : {},
436
+ ...this.detectBranch && this.replayBranch ? { [APP_BRANCH]: this.replayBranch } : {},
437
+ ...this.explicit
438
+ };
439
+ }
440
+ snapshot(properties) {
441
+ var _a;
442
+ try {
443
+ const binding = currentRoutingBinding();
444
+ const base = this.disabled && (binding == null ? void 0 : binding.appGitSource) !== "replay-override" ? {} : (_a = binding == null ? void 0 : binding.appGit) != null ? _a : this.defaults;
445
+ return this.mergeSnapshot(base, properties);
446
+ } catch (e) {
447
+ return Object.freeze({});
448
+ }
449
+ }
450
+ /** Safe defaults when no operation/span start was observed; never infer identity. */
451
+ snapshotExplicit(properties, eventId) {
452
+ try {
453
+ const base = { ...this.explicit };
454
+ const operation = eventId ? this.operations.get(eventId) : void 0;
455
+ if (operation) {
456
+ for (const key of operation.explicitKeys) base[key] = operation.snapshot[key];
457
+ }
458
+ return this.mergeSnapshot(base, properties, new Set(Object.keys(base)));
459
+ } catch (e) {
460
+ return mergeAppGitSnapshot({}, properties);
461
+ }
462
+ }
463
+ /** Existing replay branch reporting stays separate from opt-in event properties. */
464
+ snapshotForReplay() {
465
+ const snapshot = this.snapshot();
466
+ if (this.replayBranch && !own(snapshot, APP_BRANCH) && snapshot[APP_COMMIT_SHA] === this.defaults[APP_COMMIT_SHA]) {
467
+ return Object.freeze({ ...snapshot, [APP_BRANCH]: this.replayBranch });
468
+ }
469
+ return snapshot;
470
+ }
471
+ /** Merge a frozen span snapshot without losing client-authored provenance. */
472
+ mergeSnapshot(base, properties, explicitKeys = this.explicitKeys) {
473
+ return mergeAppGitSnapshot(base, properties, /* @__PURE__ */ new Set([...this.explicitKeys, ...explicitKeys]));
474
+ }
475
+ /** Shippers belonging to one client share this bounded operation registry. */
476
+ forOperation(eventId, properties, owner) {
477
+ if (!eventId) return this.snapshot(properties);
478
+ let operation = this.operations.get(eventId);
479
+ if (!operation) {
480
+ if (this.operations.size >= 1e4) this.operationOverflow = true;
481
+ if (this.operationOverflow) return this.mergeSnapshot({}, properties);
482
+ operation = {
483
+ snapshot: this.snapshot(properties),
484
+ owners: /* @__PURE__ */ new Set(),
485
+ unowned: owner === void 0,
486
+ explicitKeys: appGitExplicitKeys(properties)
487
+ };
488
+ this.operations.set(eventId, operation);
489
+ } else if (properties && APP_GIT_KEYS.some((key) => own(properties, key))) {
490
+ const explicitKeys = /* @__PURE__ */ new Set([...this.explicitKeys, ...operation.explicitKeys]);
491
+ operation.snapshot = this.mergeSnapshot(operation.snapshot, properties, explicitKeys);
492
+ for (const key of appGitExplicitKeys(properties)) operation.explicitKeys.add(key);
493
+ }
494
+ if (!owner) operation.unowned = true;
495
+ if (owner) operation.owners.add(owner);
496
+ return operation.snapshot;
497
+ }
498
+ releaseOperation(eventId, owner) {
499
+ const operation = this.operations.get(eventId);
500
+ if (!operation) return;
501
+ operation.owners.delete(owner);
502
+ if (operation.owners.size === 0 && !operation.unowned) this.operations.delete(eventId);
503
+ }
504
+ finishOperation(eventId) {
505
+ this.operations.delete(eventId);
506
+ }
507
+ dispose() {
508
+ var _a;
509
+ this.disposed = true;
510
+ try {
511
+ (_a = this.cancel) == null ? void 0 : _a.call(this);
512
+ } catch (e) {
513
+ }
514
+ }
515
+ };
516
+ var AppGitContext = AppGitContextImpl;
517
+ function createAppGitContext(options, runtime = {}) {
518
+ return new AppGitContext(options, runtime);
519
+ }
520
+ function withAppGitProperties(properties, snapshot) {
521
+ try {
522
+ const merged = { ...properties };
523
+ for (const key of APP_GIT_KEYS)
524
+ if (!own(merged, key) && own(snapshot, key)) merged[key] = snapshot[key];
525
+ return merged;
526
+ } catch (e) {
527
+ return properties != null ? properties : {};
528
+ }
529
+ }
530
+ function withAppGitAttributes(attributes, snapshot) {
531
+ try {
532
+ const present = new Set(attributes.flatMap((attr) => attr ? [attr.key] : []));
533
+ const added = [];
534
+ for (const key of APP_GIT_KEYS) {
535
+ if (present.has(key) || present.has(`ai.telemetry.metadata.${key}`) || present.has(`traceloop.association.properties.${key}`))
536
+ continue;
537
+ const value = snapshot[key];
538
+ if (typeof value === "string") added.push({ key, value: { stringValue: value } });
539
+ else if (typeof value === "boolean") added.push({ key, value: { boolValue: value } });
540
+ }
541
+ return added.length ? [...attributes, ...added] : attributes;
542
+ } catch (e) {
543
+ return attributes;
544
+ }
545
+ }
546
+ function appGitPropertiesFromAttributes(attributes) {
547
+ var _a, _b, _c;
548
+ const properties = {};
549
+ try {
550
+ for (const attr of attributes) {
551
+ if (!attr) continue;
552
+ for (const key of APP_GIT_KEYS) {
553
+ if (attr.key === key || attr.key === `ai.telemetry.metadata.${key}` || attr.key === `traceloop.association.properties.${key}`) {
554
+ properties[key] = (_c = (_b = (_a = attr.value.stringValue) != null ? _a : attr.value.boolValue) != null ? _b : attr.value.intValue) != null ? _c : null;
555
+ }
556
+ }
557
+ }
558
+ } catch (e) {
559
+ }
560
+ return properties;
561
+ }
47
562
  function getCrypto() {
48
563
  const c = globalThis.crypto;
49
564
  return c;
@@ -58,6 +573,17 @@ function randomBytes(length) {
58
573
  for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
59
574
  return out;
60
575
  }
576
+ function randomUUID() {
577
+ const cryptoObj = getCrypto();
578
+ if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
579
+ return cryptoObj.randomUUID();
580
+ }
581
+ const b = randomBytes(16);
582
+ b[6] = b[6] & 15 | 64;
583
+ b[8] = b[8] & 63 | 128;
584
+ const hex = [...b].map((x) => x.toString(16).padStart(2, "0")).join("");
585
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
586
+ }
61
587
  function base64Encode(bytes) {
62
588
  const maybeBuffer = globalThis.Buffer;
63
589
  if (maybeBuffer) {
@@ -87,6 +613,39 @@ function base64Encode(bytes) {
87
613
  }
88
614
  return out;
89
615
  }
616
+ function base64ToHex(value) {
617
+ if (!value) return "";
618
+ const maybeBuffer = globalThis.Buffer;
619
+ if (maybeBuffer) {
620
+ return maybeBuffer.from(value, "base64").toString("hex");
621
+ }
622
+ const atobFn = globalThis.atob;
623
+ if (typeof atobFn !== "function") return "";
624
+ try {
625
+ const binary = atobFn(value);
626
+ let hex = "";
627
+ for (let i = 0; i < binary.length; i++) {
628
+ hex += (binary.charCodeAt(i) & 255).toString(16).padStart(2, "0");
629
+ }
630
+ return hex;
631
+ } catch (e) {
632
+ return "";
633
+ }
634
+ }
635
+ var HANDOFF_ATTRIBUTE_SUFFIXES = {
636
+ mode: "raindrop.handoff.mode",
637
+ childEventId: "raindrop.handoff.childEventId",
638
+ parentEventId: "raindrop.handoff.parentEventId",
639
+ parentSpanId: "raindrop.handoff.parentSpanId",
640
+ name: "raindrop.handoff.name",
641
+ terminal: "raindrop.handoff.terminal",
642
+ agentRole: "raindrop.agent.role"
643
+ };
644
+ var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
645
+ var TRACEPARENT_HEADER = "traceparent";
646
+ var BAGGAGE_HEADER = "baggage";
647
+ var HANDOFF_HEADER = "x-raindrop-handoff";
648
+ 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.`;
90
649
  function runWithTracingSuppressed(fn) {
91
650
  const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
92
651
  if (typeof hook !== "function") return fn();
@@ -104,7 +663,7 @@ function runWithTracingSuppressed(fn) {
104
663
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
105
664
  var MAX_RETRY_DELAY_MS = 3e4;
106
665
  function wait(ms) {
107
- return new Promise((resolve) => setTimeout(resolve, ms));
666
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
108
667
  }
109
668
  function formatEndpoint(endpoint) {
110
669
  if (!endpoint) return void 0;
@@ -141,9 +700,9 @@ async function raceWithTimeout(promise, timeoutMs) {
141
700
  () => true,
142
701
  () => true
143
702
  ),
144
- new Promise((resolve) => {
703
+ new Promise((resolve2) => {
145
704
  var _a;
146
- timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
705
+ timer = setTimeout(() => resolve2(false), Math.max(0, timeoutMs));
147
706
  (_a = timer.unref) == null ? void 0 : _a.call(timer);
148
707
  })
149
708
  ]);
@@ -214,6 +773,7 @@ async function postJson(url, body, headers, opts) {
214
773
  const resp = await runWithTracingSuppressed(
215
774
  () => fetch(url, {
216
775
  method: "POST",
776
+ redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
217
777
  headers: {
218
778
  "Content-Type": "application/json",
219
779
  ...headers
@@ -377,26 +937,29 @@ function resolveLocalDebuggerBaseUrl(baseUrl) {
377
937
  if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
378
938
  return null;
379
939
  }
940
+ var LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS = 2e3;
380
941
  function mirrorTraceExportToLocalDebugger(body, options = {}) {
381
942
  var _a;
382
943
  const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
383
- if (!baseUrl) return;
384
- void postJson(`${baseUrl}traces`, body, {}, {
944
+ if (!baseUrl) return Promise.resolve();
945
+ return postJson(`${baseUrl}traces`, body, {}, {
385
946
  maxAttempts: 1,
386
947
  debug: (_a = options.debug) != null ? _a : false,
387
- sdkName: options.sdkName
948
+ sdkName: options.sdkName,
949
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
388
950
  }).catch(() => {
389
951
  });
390
952
  }
391
953
  function mirrorPartialEventToLocalDebugger(event, options = {}) {
392
954
  var _a;
393
955
  const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
394
- if (!baseUrl) return;
956
+ if (!baseUrl) return Promise.resolve();
395
957
  const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
396
- void postJson(`${baseUrl}events/track_partial`, event, headers, {
958
+ return postJson(`${baseUrl}events/track_partial`, event, headers, {
397
959
  maxAttempts: 1,
398
960
  debug: (_a = options.debug) != null ? _a : false,
399
- sdkName: options.sdkName
961
+ sdkName: options.sdkName,
962
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
400
963
  }).catch(() => {
401
964
  });
402
965
  }
@@ -421,35 +984,81 @@ function projectIdHeaders(projectId) {
421
984
  }
422
985
  var SHUTDOWN_DEADLINE_MS = 1e4;
423
986
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
987
+ function appGitOwnerId(prefix) {
988
+ try {
989
+ return `${prefix}:${randomUUID()}`;
990
+ } catch (e) {
991
+ return `${prefix}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
992
+ }
993
+ }
424
994
  function mergePatches(target, source) {
425
- var _a, _b, _c, _d;
995
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
426
996
  const out = { ...target, ...source };
427
997
  if (target.properties || source.properties) {
428
998
  out.properties = { ...(_a = target.properties) != null ? _a : {}, ...(_b = source.properties) != null ? _b : {} };
429
999
  }
1000
+ if (target.usage || source.usage) {
1001
+ out.usage = { ...(_c = target.usage) != null ? _c : {} };
1002
+ if (((_d = source.usage) == null ? void 0 : _d.promptTokens) !== void 0) {
1003
+ out.usage.promptTokens = source.usage.promptTokens;
1004
+ }
1005
+ if (((_e = source.usage) == null ? void 0 : _e.completionTokens) !== void 0) {
1006
+ out.usage.completionTokens = source.usage.completionTokens;
1007
+ }
1008
+ }
1009
+ if (source.error === void 0) {
1010
+ out.error = target.error;
1011
+ }
1012
+ if (target.featureFlags || source.featureFlags) {
1013
+ out.featureFlags = { ...(_f = target.featureFlags) != null ? _f : {}, ...(_g = source.featureFlags) != null ? _g : {} };
1014
+ }
430
1015
  if (target.attachments || source.attachments) {
431
- out.attachments = [...(_c = target.attachments) != null ? _c : [], ...(_d = source.attachments) != null ? _d : []];
1016
+ out.attachments = [...(_h = target.attachments) != null ? _h : [], ...(_i = source.attachments) != null ? _i : []];
432
1017
  }
433
1018
  return out;
434
1019
  }
1020
+ function isErrorLike(value) {
1021
+ return value !== null && typeof value === "object";
1022
+ }
1023
+ function eventTelemetryProperties(patch) {
1024
+ var _a, _b;
1025
+ const properties = {};
1026
+ if (((_a = patch.usage) == null ? void 0 : _a.promptTokens) !== void 0 && Number.isFinite(patch.usage.promptTokens)) {
1027
+ properties["ai.usage.prompt_tokens"] = patch.usage.promptTokens;
1028
+ }
1029
+ if (((_b = patch.usage) == null ? void 0 : _b.completionTokens) !== void 0 && Number.isFinite(patch.usage.completionTokens)) {
1030
+ properties["ai.usage.completion_tokens"] = patch.usage.completionTokens;
1031
+ }
1032
+ if (patch.error !== void 0 && patch.error !== null) {
1033
+ const constructorName = patch.error instanceof Error && patch.error.constructor !== Error ? patch.error.constructor.name : void 0;
1034
+ const errorType = patch.error instanceof Error ? patch.error.name && patch.error.name !== "Error" ? patch.error.name : constructorName || patch.error.name || "Error" : isErrorLike(patch.error) && typeof patch.error.name === "string" && patch.error.name.length > 0 ? patch.error.name : "Error";
1035
+ const errorMessage = patch.error instanceof Error ? patch.error.message : isErrorLike(patch.error) && typeof patch.error.message === "string" ? patch.error.message : String(patch.error);
1036
+ properties["error.type"] = errorType;
1037
+ properties["error.message"] = errorMessage;
1038
+ }
1039
+ return properties;
1040
+ }
435
1041
  var EventShipper = class {
436
1042
  constructor(opts) {
1043
+ this.appGitOwner = appGitOwnerId("event");
437
1044
  this.buffers = /* @__PURE__ */ new Map();
438
1045
  this.sticky = /* @__PURE__ */ new Map();
439
1046
  this.timers = /* @__PURE__ */ new Map();
440
1047
  this.inFlight = /* @__PURE__ */ new Set();
441
1048
  this.hasShutdown = false;
442
- var _a, _b, _c, _d, _e, _f, _g, _h;
443
- this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
444
- this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
1049
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1050
+ this.appGitContext = (_a = opts.appGitContext) != null ? _a : createAppGitContext(opts.appGit);
1051
+ this.ownsAppGitContext = opts.appGitContext === void 0;
1052
+ this.writeKey = (_b = opts.writeKey) == null ? void 0 : _b.trim();
1053
+ this.baseUrl = (_c = formatEndpoint(opts.endpoint)) != null ? _c : "https://api.raindrop.ai/v1/";
445
1054
  this.enabled = opts.enabled !== false;
446
1055
  this.debug = opts.debug;
447
- this.partialFlushMs = (_c = opts.partialFlushMs) != null ? _c : 1e3;
448
- this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
1056
+ this.partialFlushMs = (_d = opts.partialFlushMs) != null ? _d : 1e3;
1057
+ this.sdkName = (_e = opts.sdkName) != null ? _e : "core";
449
1058
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
450
- this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
1059
+ this.defaultEventName = (_f = opts.defaultEventName) != null ? _f : "ai_generation";
451
1060
  this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
452
- this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
1061
+ this.localDebuggerUrl = (_g = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _g : void 0;
453
1062
  if (this.debug && this.localDebuggerUrl) {
454
1063
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
455
1064
  }
@@ -460,8 +1069,8 @@ var EventShipper = class {
460
1069
  const isNode = typeof process !== "undefined" && typeof process.version === "string";
461
1070
  this.context = {
462
1071
  library: {
463
- name: (_g = opts.libraryName) != null ? _g : "@raindrop-ai/core",
464
- version: (_h = opts.libraryVersion) != null ? _h : "0.0.0"
1072
+ name: (_h = opts.libraryName) != null ? _h : "@raindrop-ai/core",
1073
+ version: (_i = opts.libraryVersion) != null ? _i : "0.0.0"
465
1074
  },
466
1075
  metadata: {
467
1076
  jsRuntime: isNode ? "node" : "web",
@@ -469,6 +1078,9 @@ var EventShipper = class {
469
1078
  }
470
1079
  };
471
1080
  }
1081
+ ownAppGitContextForShutdown() {
1082
+ this.ownsAppGitContext = true;
1083
+ }
472
1084
  isDebugEnabled() {
473
1085
  return this.debug;
474
1086
  }
@@ -517,6 +1129,15 @@ var EventShipper = class {
517
1129
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
518
1130
  if (!this.enabled) return;
519
1131
  if (!eventId || !eventId.trim()) return;
1132
+ const capturedSticky = this.sticky.get(eventId);
1133
+ const eventAlreadyKnown = capturedSticky !== void 0 || this.buffers.has(eventId);
1134
+ const destination = currentReplayTraceDestination();
1135
+ const cloudReplay = destination !== void 0 && destination.kind !== "workshop";
1136
+ if ((capturedSticky == null ? void 0 : capturedSticky.replayOnly) === true || !eventAlreadyKnown && cloudReplay) {
1137
+ if (patch.isPending === false) this.sticky.delete(eventId);
1138
+ else this.sticky.set(eventId, { ...capturedSticky, replayOnly: true });
1139
+ return;
1140
+ }
520
1141
  const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
521
1142
  if (typeof patch.input === "string" && patch.input.length > maxChars) {
522
1143
  patch = { ...patch, input: capText(patch.input, maxChars) };
@@ -538,6 +1159,13 @@ var EventShipper = class {
538
1159
  }
539
1160
  const sticky = (_c = this.sticky.get(eventId)) != null ? _c : {};
540
1161
  const existing = (_d = this.buffers.get(eventId)) != null ? _d : {};
1162
+ patch = {
1163
+ ...patch,
1164
+ properties: withAppGitProperties(
1165
+ patch.properties,
1166
+ this.appGitContext.forOperation(eventId, patch.properties, this.appGitOwner)
1167
+ )
1168
+ };
541
1169
  const merged = mergePatches(existing, patch);
542
1170
  merged.isPending = (_g = (_f = (_e = patch.isPending) != null ? _e : existing.isPending) != null ? _f : sticky.isPending) != null ? _g : true;
543
1171
  this.buffers.set(eventId, merged);
@@ -551,6 +1179,8 @@ var EventShipper = class {
551
1179
  if (t) clearTimeout(t);
552
1180
  if (merged.isPending === false) {
553
1181
  await this.flushOne(eventId);
1182
+ if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(eventId, this.appGitOwner);
1183
+ else this.appGitContext.finishOperation(eventId);
554
1184
  return;
555
1185
  }
556
1186
  const timeout = setTimeout(() => {
@@ -575,15 +1205,21 @@ var EventShipper = class {
575
1205
  try {
576
1206
  for (const t of this.timers.values()) clearTimeout(t);
577
1207
  this.timers.clear();
1208
+ for (const [eventId, sticky] of this.sticky) {
1209
+ if (sticky.replayOnly) this.sticky.delete(eventId);
1210
+ }
578
1211
  const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
579
1212
  if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
580
1213
  } finally {
581
1214
  this.shutdownDeadlineAt = void 0;
1215
+ if (this.ownsAppGitContext) this.appGitContext.dispose();
582
1216
  }
583
1217
  }
584
1218
  async trackSignal(signal) {
585
1219
  var _a, _b;
586
1220
  if (!this.enabled) return;
1221
+ const destination = currentReplayTraceDestination();
1222
+ if (destination && destination.kind !== "workshop") return;
587
1223
  const body = [
588
1224
  {
589
1225
  event_id: signal.eventId,
@@ -618,6 +1254,8 @@ var EventShipper = class {
618
1254
  }
619
1255
  async identify(users) {
620
1256
  if (!this.enabled) return;
1257
+ const destination = currentReplayTraceDestination();
1258
+ if (destination && destination.kind !== "workshop") return;
621
1259
  const list = Array.isArray(users) ? users : [users];
622
1260
  const body = list.filter((user) => {
623
1261
  if (!(user == null ? void 0 : user.userId) || !user.userId.trim()) {
@@ -670,8 +1308,9 @@ var EventShipper = class {
670
1308
  this.buffers.delete(eventId);
671
1309
  if (!accumulated) return;
672
1310
  const sticky = (_a = this.sticky.get(eventId)) != null ? _a : {};
673
- const eventName = (_c = (_b = accumulated.eventName) != null ? _b : sticky.eventName) != null ? _c : this.defaultEventName;
674
- const userId = (_d = accumulated.userId) != null ? _d : sticky.userId;
1311
+ const isPending = (_c = (_b = accumulated.isPending) != null ? _b : sticky.isPending) != null ? _c : true;
1312
+ const eventName = (_e = (_d = accumulated.eventName) != null ? _d : sticky.eventName) != null ? _e : this.defaultEventName;
1313
+ const userId = (_f = accumulated.userId) != null ? _f : sticky.userId;
675
1314
  if (!userId) {
676
1315
  if (this.debug) {
677
1316
  console.warn(`${this.prefix} skipping track_partial for ${eventId}: missing userId`);
@@ -679,9 +1318,13 @@ var EventShipper = class {
679
1318
  this.sticky.delete(eventId);
680
1319
  return;
681
1320
  }
682
- const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
683
- const convoId = (_f = accumulated.convoId) != null ? _f : sticky.convoId;
684
- const isPending = (_h = (_g = accumulated.isPending) != null ? _g : sticky.isPending) != null ? _h : true;
1321
+ const { wizardSession, ...restProperties } = (_g = accumulated.properties) != null ? _g : {};
1322
+ let telemetryProperties = {};
1323
+ try {
1324
+ telemetryProperties = eventTelemetryProperties(accumulated);
1325
+ } catch (e) {
1326
+ }
1327
+ const convoId = (_h = accumulated.convoId) != null ? _h : sticky.convoId;
685
1328
  const payload = {
686
1329
  event_id: eventId,
687
1330
  user_id: userId,
@@ -689,15 +1332,17 @@ var EventShipper = class {
689
1332
  timestamp: (_i = accumulated.timestamp) != null ? _i : (/* @__PURE__ */ new Date()).toISOString(),
690
1333
  ai_data: {
691
1334
  input: accumulated.input,
692
- output: accumulated.output,
1335
+ output: accumulated.error !== void 0 && accumulated.error !== null ? void 0 : accumulated.output,
693
1336
  model: accumulated.model,
694
1337
  convo_id: convoId
695
1338
  },
696
1339
  properties: {
697
1340
  ...restProperties,
1341
+ ...telemetryProperties,
698
1342
  ...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
699
1343
  $context: this.context
700
1344
  },
1345
+ ...accumulated.featureFlags && Object.keys(accumulated.featureFlags).length > 0 ? { feature_flags: accumulated.featureFlags } : {},
701
1346
  attachments: accumulated.attachments,
702
1347
  is_pending: isPending
703
1348
  };
@@ -721,29 +1366,30 @@ var EventShipper = class {
721
1366
  endpoint: url
722
1367
  });
723
1368
  }
724
- if (this.localDebuggerUrl) {
725
- mirrorPartialEventToLocalDebugger(payload, {
726
- baseUrl: this.localDebuggerUrl,
727
- writeKey: this.writeKey,
728
- debug: this.debug,
729
- sdkName: this.sdkName
730
- });
731
- }
1369
+ const mirror = this.localDebuggerUrl ? mirrorPartialEventToLocalDebugger(payload, {
1370
+ baseUrl: this.localDebuggerUrl,
1371
+ writeKey: this.writeKey,
1372
+ debug: this.debug,
1373
+ sdkName: this.sdkName
1374
+ }) : void 0;
732
1375
  if (!this.writeKey) {
733
1376
  if (!isPending) this.sticky.delete(eventId);
1377
+ if (mirror) await mirror;
734
1378
  return;
735
1379
  }
736
1380
  const opts = this.requestOpts();
737
1381
  if (!opts) {
738
1382
  this.warnShutdownDrop(`track_partial ${eventId}`);
739
1383
  if (!isPending) this.sticky.delete(eventId);
1384
+ if (mirror) await mirror;
740
1385
  return;
741
1386
  }
742
1387
  const p = postJson(url, payload, this.requestHeaders(), opts);
743
- this.inFlight.add(p);
1388
+ const combined = mirror ? Promise.all([mirror, p]).then(() => void 0) : p;
1389
+ this.inFlight.add(combined);
744
1390
  try {
745
1391
  try {
746
- await p;
1392
+ await combined;
747
1393
  if (this.debug) {
748
1394
  console.log(`${this.prefix} sent track_partial ${eventId} (${eventName})`);
749
1395
  }
@@ -755,13 +1401,156 @@ var EventShipper = class {
755
1401
  );
756
1402
  }
757
1403
  } finally {
758
- this.inFlight.delete(p);
1404
+ this.inFlight.delete(combined);
759
1405
  }
760
1406
  if (!isPending) {
761
1407
  this.sticky.delete(eventId);
762
1408
  }
763
1409
  }
764
1410
  };
1411
+ var MODEL_USAGE_ATTRIBUTES = {
1412
+ usageSource: "raindrop.usage.source",
1413
+ providerName: "gen_ai.provider.name",
1414
+ requestModel: "gen_ai.request.model",
1415
+ responseModel: "gen_ai.response.model",
1416
+ inputTokens: "gen_ai.usage.input_tokens",
1417
+ outputTokens: "gen_ai.usage.output_tokens",
1418
+ reasoningTokens: "gen_ai.usage.reasoning_tokens",
1419
+ cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
1420
+ cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
1421
+ providerReportedCost: "gen_ai.usage.provider_reported_cost",
1422
+ nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
1423
+ nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
1424
+ totalOutputTokens: "raindrop.usage.output_tokens.total",
1425
+ reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
1426
+ cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
1427
+ cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
1428
+ };
1429
+ var MODEL_PROVIDER_NAMES = [
1430
+ ["google.vertex.anthropic", "google-vertex"],
1431
+ ["google.vertex_ai", "google-vertex"],
1432
+ ["google.vertex", "google-vertex"],
1433
+ ["gcp.vertex_ai", "google-vertex"],
1434
+ ["gcp.gemini", "google"],
1435
+ ["google_vertexai", "google-vertex"],
1436
+ ["vertex.anthropic", "google-vertex"],
1437
+ ["amazon-bedrock", "amazon-bedrock"],
1438
+ ["aws.bedrock", "amazon-bedrock"],
1439
+ ["bedrock", "amazon-bedrock"],
1440
+ ["anthropic", "anthropic"],
1441
+ ["openai", "openai"],
1442
+ ["google", "google"],
1443
+ ["az.ai.openai", "azure"],
1444
+ ["azure", "azure"],
1445
+ ["openrouter", "openrouter"]
1446
+ ];
1447
+ function canonicalModelProvider(provider) {
1448
+ var _a;
1449
+ const normalizedProvider = provider.trim().toLowerCase();
1450
+ if (normalizedProvider === "gateway") return "vercel";
1451
+ const match = MODEL_PROVIDER_NAMES.find(
1452
+ ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
1453
+ );
1454
+ return (_a = match == null ? void 0 : match[1]) != null ? _a : normalizedProvider;
1455
+ }
1456
+ var STRING_ALIASES = {
1457
+ [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
1458
+ };
1459
+ var NUMBER_ALIASES = {
1460
+ [MODEL_USAGE_ATTRIBUTES.inputTokens]: [
1461
+ "gen_ai.usage.prompt_tokens",
1462
+ "ai.usage.prompt_tokens",
1463
+ "ai.usage.promptTokens",
1464
+ "ai.usage.input_tokens",
1465
+ "ai.usage.inputTokens"
1466
+ ],
1467
+ [MODEL_USAGE_ATTRIBUTES.outputTokens]: [
1468
+ "gen_ai.usage.completion_tokens",
1469
+ "ai.usage.completion_tokens",
1470
+ "ai.usage.completionTokens",
1471
+ "ai.usage.output_tokens",
1472
+ "ai.usage.outputTokens"
1473
+ ],
1474
+ [MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
1475
+ "gen_ai.usage.reasoning.output_tokens",
1476
+ "ai.usage.reasoningTokens",
1477
+ "ai.usage.thoughts_tokens"
1478
+ ],
1479
+ [MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
1480
+ "gen_ai.usage.cache_read_tokens",
1481
+ "gen_ai.usage.cache_read.input_tokens",
1482
+ "ai.usage.cachedInputTokens",
1483
+ "ai.usage.cached_tokens",
1484
+ "ai.usage.cache_read_tokens",
1485
+ "ai.usage.cache_read_input_tokens",
1486
+ "ai.usage.cacheReadInputTokens"
1487
+ ],
1488
+ [MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
1489
+ "gen_ai.usage.cache_creation_input_tokens",
1490
+ "gen_ai.usage.cache_creation.input_tokens",
1491
+ "ai.usage.cacheWriteInputTokens",
1492
+ "ai.usage.cache_creation_input_tokens",
1493
+ "ai.usage.cacheCreationInputTokens"
1494
+ ]
1495
+ };
1496
+ function hasAttribute(attributes, key) {
1497
+ return attributes.some((attribute) => attribute.key === key);
1498
+ }
1499
+ function findStringAttribute(attributes, keys) {
1500
+ return attributes.find(
1501
+ (attribute) => keys.includes(attribute.key) && typeof attribute.value.stringValue === "string" && attribute.value.stringValue.length > 0
1502
+ );
1503
+ }
1504
+ function findNumberAttribute(attributes, keys) {
1505
+ return attributes.find((attribute) => {
1506
+ if (!keys.includes(attribute.key)) return false;
1507
+ if (typeof attribute.value.doubleValue === "number" && Number.isSafeInteger(attribute.value.doubleValue) && attribute.value.doubleValue >= 0) {
1508
+ return true;
1509
+ }
1510
+ const intValue = attribute.value.intValue;
1511
+ return integerValue(intValue) !== void 0;
1512
+ });
1513
+ }
1514
+ function integerValue(value) {
1515
+ if (typeof value === "number") {
1516
+ return Number.isSafeInteger(value) && value >= 0 ? value : void 0;
1517
+ }
1518
+ if (typeof value !== "string") return void 0;
1519
+ const parsed = Number(value);
1520
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
1521
+ }
1522
+ function appendAlias(attributes, target, source) {
1523
+ if (!source || hasAttribute(attributes, target)) return attributes;
1524
+ return [...attributes, { key: target, value: { ...source.value } }];
1525
+ }
1526
+ function appendProviderAlias(attributes) {
1527
+ if (hasAttribute(attributes, MODEL_USAGE_ATTRIBUTES.providerName)) return attributes;
1528
+ const source = findStringAttribute(attributes, ["gen_ai.system", "ai.model.provider"]);
1529
+ if (!(source == null ? void 0 : source.value.stringValue)) return attributes;
1530
+ const attribute = attrString(
1531
+ MODEL_USAGE_ATTRIBUTES.providerName,
1532
+ canonicalModelProvider(source.value.stringValue)
1533
+ );
1534
+ return attribute ? [...attributes, attribute] : attributes;
1535
+ }
1536
+ function normalizeModelUsageSpan(span) {
1537
+ const original = span.attributes;
1538
+ if (!original || original.length === 0) return span;
1539
+ const responseModel = findStringAttribute(original, [
1540
+ MODEL_USAGE_ATTRIBUTES.responseModel,
1541
+ "ai.response.model"
1542
+ ]);
1543
+ if (!responseModel) return span;
1544
+ let attributes = appendAlias(original, MODEL_USAGE_ATTRIBUTES.responseModel, responseModel);
1545
+ attributes = appendProviderAlias(attributes);
1546
+ for (const [target, aliases] of Object.entries(STRING_ALIASES)) {
1547
+ attributes = appendAlias(attributes, target, findStringAttribute(attributes, aliases));
1548
+ }
1549
+ for (const [target, aliases] of Object.entries(NUMBER_ALIASES)) {
1550
+ attributes = appendAlias(attributes, target, findNumberAttribute(attributes, aliases));
1551
+ }
1552
+ return attributes === original ? span : { ...span, attributes };
1553
+ }
765
1554
  var DEFAULT_SECRET_KEY_NAMES = [
766
1555
  "apikey",
767
1556
  "apisecret",
@@ -851,6 +1640,20 @@ function redactJsonAttributeValue(key, value) {
851
1640
  if (scrubbedJson === json) return void 0;
852
1641
  return { stringValue: scrubbedJson };
853
1642
  }
1643
+ function appGitOwnerId2(prefix) {
1644
+ try {
1645
+ return `${prefix}:${randomUUID()}`;
1646
+ } catch (e) {
1647
+ return `${prefix}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
1648
+ }
1649
+ }
1650
+ function identityAttrs(args) {
1651
+ return [
1652
+ attrString("ai.telemetry.metadata.raindrop.ai.userId", args.userId),
1653
+ attrString("ai.telemetry.metadata.raindrop.convoId", args.convoId),
1654
+ attrString("ai.telemetry.metadata.raindrop.eventName", args.eventName)
1655
+ ];
1656
+ }
854
1657
  function applyOtelSpanAttributeLimit(limit) {
855
1658
  var _a, _b;
856
1659
  try {
@@ -866,23 +1669,30 @@ function applyOtelSpanAttributeLimit(limit) {
866
1669
  }
867
1670
  var TraceShipper = class {
868
1671
  constructor(opts) {
1672
+ this.appGitOwner = appGitOwnerId2("trace");
1673
+ this.appGitEventIds = /* @__PURE__ */ new WeakMap();
1674
+ this.appGitActiveSpans = /* @__PURE__ */ new Map();
1675
+ this.appGitCaptured = /* @__PURE__ */ new WeakSet();
1676
+ this.appGitAdded = /* @__PURE__ */ new WeakMap();
869
1677
  this.queue = [];
870
1678
  this.inFlight = /* @__PURE__ */ new Set();
871
1679
  this.hasShutdown = false;
872
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
873
- this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
874
- this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
1680
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1681
+ this.appGitContext = (_a = opts.appGitContext) != null ? _a : createAppGitContext(opts.appGit);
1682
+ this.ownsAppGitContext = opts.appGitContext === void 0;
1683
+ this.writeKey = (_b = opts.writeKey) == null ? void 0 : _b.trim();
1684
+ this.baseUrl = (_c = formatEndpoint(opts.endpoint)) != null ? _c : "https://api.raindrop.ai/v1/";
875
1685
  this.enabled = opts.enabled !== false;
876
1686
  this.debug = opts.debug;
877
1687
  this.debugSpans = opts.debugSpans === true;
878
- this.flushIntervalMs = (_c = opts.flushIntervalMs) != null ? _c : 1e3;
879
- this.maxBatchSize = (_d = opts.maxBatchSize) != null ? _d : 50;
880
- this.maxQueueSize = (_e = opts.maxQueueSize) != null ? _e : 5e3;
881
- this.sdkName = (_f = opts.sdkName) != null ? _f : "core";
1688
+ this.flushIntervalMs = (_d = opts.flushIntervalMs) != null ? _d : 1e3;
1689
+ this.maxBatchSize = (_e = opts.maxBatchSize) != null ? _e : 50;
1690
+ this.maxQueueSize = (_f = opts.maxQueueSize) != null ? _f : 5e3;
1691
+ this.sdkName = (_g = opts.sdkName) != null ? _g : "core";
882
1692
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
883
- this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
884
- this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
885
- this.localDebuggerUrl = (_i = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _i : void 0;
1693
+ this.serviceName = (_h = opts.serviceName) != null ? _h : "raindrop.core";
1694
+ this.serviceVersion = (_i = opts.serviceVersion) != null ? _i : "0.0.0";
1695
+ this.localDebuggerUrl = (_j = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _j : void 0;
886
1696
  if (this.debug && this.localDebuggerUrl) {
887
1697
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
888
1698
  }
@@ -946,7 +1756,10 @@ var TraceShipper = class {
946
1756
  try {
947
1757
  const result = this.transformSpanHook(current);
948
1758
  if (result === null) return null;
949
- if (result !== void 0) current = result;
1759
+ if (result !== void 0) {
1760
+ copyReplayTraceDestination(current, result);
1761
+ current = result;
1762
+ }
950
1763
  } catch (err) {
951
1764
  if (this.debug) {
952
1765
  const msg = err instanceof Error ? err.message : String(err);
@@ -955,10 +1768,20 @@ var TraceShipper = class {
955
1768
  return null;
956
1769
  }
957
1770
  }
1771
+ try {
1772
+ const normalized = normalizeModelUsageSpan(current);
1773
+ copyReplayTraceDestination(current, normalized);
1774
+ current = normalized;
1775
+ } catch (e) {
1776
+ }
958
1777
  if (!this.disableDefaultRedaction) {
959
- current = defaultTransformSpan(current);
1778
+ const redacted = defaultTransformSpan(current);
1779
+ copyReplayTraceDestination(current, redacted);
1780
+ current = redacted;
960
1781
  }
961
- return this.capSpanAttributes(current);
1782
+ const capped = this.capSpanAttributes(current);
1783
+ copyReplayTraceDestination(current, capped);
1784
+ return capped;
962
1785
  }
963
1786
  isDebugEnabled() {
964
1787
  return this.debug;
@@ -970,45 +1793,84 @@ var TraceShipper = class {
970
1793
  return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
971
1794
  }
972
1795
  startSpan(args) {
973
- var _a, _b;
1796
+ var _a, _b, _c;
974
1797
  const ids = createSpanIds(args.parent);
975
1798
  const started = (_a = args.startTimeUnixNano) != null ? _a : nowUnixNanoString();
976
1799
  const attrs = [
977
1800
  attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
978
- attrString("ai.operationId", args.operationId)
1801
+ ...identityAttrs(args),
1802
+ attrString("ai.operationId", args.operationId),
1803
+ ...Object.entries(evalScopeAttributes()).map(([key, value]) => attrString(key, value))
979
1804
  ];
980
1805
  if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
981
- const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
982
- this.mirrorToLocalDebugger(
983
- buildOtlpSpan({
984
- ids: span.ids,
985
- name: span.name,
986
- startTimeUnixNano: span.startTimeUnixNano,
987
- endTimeUnixNano: span.startTimeUnixNano,
988
- // placeholder — will be updated on endSpan
989
- attributes: span.attributes,
990
- status: { code: SpanStatusCode.UNSET }
991
- })
992
- );
1806
+ const tracksAppGit = this.appGitActiveSpans.has(args.eventId) || this.appGitActiveSpans.size < 1e4;
1807
+ const appGitOverrides = appGitPropertiesFromAttributes(attrs);
1808
+ const span = {
1809
+ ids,
1810
+ name: args.name,
1811
+ startTimeUnixNano: started,
1812
+ attributes: withAppGitAttributes(
1813
+ attrs,
1814
+ this.appGitContext.forOperation(args.eventId, appGitOverrides, this.appGitOwner)
1815
+ )
1816
+ };
1817
+ rememberReplayTraceDestination(span);
1818
+ this.appGitCaptured.add(span);
1819
+ this.appGitEventIds.set(span, args.eventId);
1820
+ if (tracksAppGit) {
1821
+ this.appGitActiveSpans.set(args.eventId, ((_c = this.appGitActiveSpans.get(args.eventId)) != null ? _c : 0) + 1);
1822
+ }
1823
+ this.appGitAdded.set(span, new Set(span.attributes.filter((attr) => !attrs.includes(attr))));
1824
+ const openSpan = buildOtlpSpan({
1825
+ ids: span.ids,
1826
+ name: span.name,
1827
+ startTimeUnixNano: span.startTimeUnixNano,
1828
+ endTimeUnixNano: span.startTimeUnixNano,
1829
+ // placeholder — will be updated on endSpan
1830
+ attributes: span.attributes,
1831
+ status: { code: SpanStatusCode.UNSET }
1832
+ });
1833
+ copyReplayTraceDestination(span, openSpan);
1834
+ this.mirrorToLocalDebugger(openSpan);
993
1835
  return span;
994
1836
  }
995
- mirrorToLocalDebugger(span) {
996
- if (!this.localDebuggerUrl) return;
1837
+ mirrorToLocalDebugger(span, completed = false) {
1838
+ const destination = replayTraceDestinationFor(span);
1839
+ if (destination && destination.kind !== "workshop") return;
1840
+ const localDebuggerUrl = (destination == null ? void 0 : destination.kind) === "workshop" ? destination.url : this.localDebuggerUrl;
1841
+ if (!localDebuggerUrl) return;
997
1842
  const redacted = this.redactSpan(span);
998
1843
  if (redacted === null) return;
1844
+ if (completed && (destination == null ? void 0 : destination.kind) === "workshop") {
1845
+ recordReplayExportedSpan(
1846
+ destination,
1847
+ base64ToHex(redacted.spanId),
1848
+ base64ToHex(redacted.traceId)
1849
+ );
1850
+ }
999
1851
  const body = buildExportTraceServiceRequest([redacted], this.serviceName, this.serviceVersion);
1000
- mirrorTraceExportToLocalDebugger(body, {
1001
- baseUrl: this.localDebuggerUrl,
1852
+ const p = mirrorTraceExportToLocalDebugger(body, {
1853
+ baseUrl: localDebuggerUrl,
1002
1854
  debug: false,
1003
1855
  sdkName: this.sdkName
1004
1856
  });
1857
+ this.inFlight.add(p);
1858
+ void p.finally(() => {
1859
+ this.inFlight.delete(p);
1860
+ });
1005
1861
  }
1006
1862
  endSpan(span, extra) {
1007
- var _a, _b;
1863
+ var _a, _b, _c;
1008
1864
  if (span.endTimeUnixNano) return;
1009
1865
  span.endTimeUnixNano = (_a = extra == null ? void 0 : extra.endTimeUnixNano) != null ? _a : nowUnixNanoString();
1010
1866
  if ((_b = extra == null ? void 0 : extra.attributes) == null ? void 0 : _b.length) {
1867
+ const overrides = appGitPropertiesFromAttributes(extra.attributes);
1868
+ const eventId2 = this.appGitEventIds.get(span);
1869
+ const snapshot = eventId2 ? this.appGitContext.forOperation(eventId2, overrides, this.appGitOwner) : overrides;
1870
+ const added = this.appGitAdded.get(span);
1871
+ span.attributes = span.attributes.filter((attr) => !(added == null ? void 0 : added.has(attr)) || !attr || !Object.prototype.hasOwnProperty.call(overrides, APP_COMMIT_SHA) && !Object.prototype.hasOwnProperty.call(overrides, attr.key));
1011
1872
  span.attributes.push(...extra.attributes);
1873
+ span.attributes = withAppGitAttributes(span.attributes, snapshot);
1012
1874
  }
1013
1875
  let status = extra == null ? void 0 : extra.status;
1014
1876
  if (!status && (extra == null ? void 0 : extra.error) !== void 0) {
@@ -1023,29 +1885,65 @@ var TraceShipper = class {
1023
1885
  attributes: span.attributes,
1024
1886
  status
1025
1887
  });
1888
+ copyReplayTraceDestination(span, otlp);
1889
+ this.appGitCaptured.add(otlp);
1026
1890
  this.enqueue(otlp);
1027
- this.mirrorToLocalDebugger(otlp);
1891
+ this.mirrorToLocalDebugger(otlp, true);
1892
+ const eventId = this.appGitEventIds.get(span);
1893
+ if (eventId && this.appGitActiveSpans.has(eventId)) {
1894
+ const remaining = ((_c = this.appGitActiveSpans.get(eventId)) != null ? _c : 1) - 1;
1895
+ if (remaining > 0) this.appGitActiveSpans.set(eventId, remaining);
1896
+ else {
1897
+ this.appGitActiveSpans.delete(eventId);
1898
+ if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(eventId, this.appGitOwner);
1899
+ else if (this.ownsAppGitContext) this.appGitContext.finishOperation(eventId);
1900
+ }
1901
+ }
1028
1902
  }
1029
1903
  createSpan(args) {
1030
1904
  var _a;
1031
1905
  const ids = createSpanIds(args.parent);
1032
1906
  const attrs = [
1033
- attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId)
1907
+ attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
1908
+ ...identityAttrs(args),
1909
+ ...Object.entries(evalScopeAttributes()).map(([key, value]) => attrString(key, value))
1034
1910
  ];
1035
1911
  if ((_a = args.attributes) == null ? void 0 : _a.length) attrs.push(...args.attributes);
1912
+ const tracksAppGit = !this.appGitActiveSpans.has(args.eventId);
1036
1913
  const otlp = buildOtlpSpan({
1037
1914
  ids,
1038
1915
  name: args.name,
1039
1916
  startTimeUnixNano: args.startTimeUnixNano,
1040
1917
  endTimeUnixNano: args.endTimeUnixNano,
1041
- attributes: attrs,
1918
+ attributes: withAppGitAttributes(
1919
+ attrs,
1920
+ this.appGitContext.forOperation(
1921
+ args.eventId,
1922
+ appGitPropertiesFromAttributes(attrs),
1923
+ this.appGitOwner
1924
+ )
1925
+ ),
1042
1926
  status: args.status
1043
1927
  });
1928
+ rememberReplayTraceDestination(otlp);
1929
+ this.appGitCaptured.add(otlp);
1930
+ if (tracksAppGit) {
1931
+ if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(args.eventId, this.appGitOwner);
1932
+ else if (this.ownsAppGitContext) this.appGitContext.finishOperation(args.eventId);
1933
+ }
1044
1934
  this.enqueue(otlp);
1045
- this.mirrorToLocalDebugger(otlp);
1935
+ this.mirrorToLocalDebugger(otlp, true);
1046
1936
  }
1047
1937
  enqueue(span) {
1938
+ var _a;
1048
1939
  if (!this.enabled) return;
1940
+ const attributes = (_a = span.attributes) != null ? _a : [];
1941
+ const enriched = this.appGitCaptured.has(span) ? attributes : withAppGitAttributes(attributes, this.appGitContext.snapshot(appGitPropertiesFromAttributes(attributes)));
1942
+ if (enriched !== attributes) {
1943
+ const original = span;
1944
+ span = { ...span, attributes: enriched.filter((attr) => attr !== void 0) };
1945
+ copyReplayTraceDestination(original, span);
1946
+ }
1049
1947
  if (this.debugSpans) {
1050
1948
  const short = (s) => s ? s.slice(-8) : "none";
1051
1949
  console.log(
@@ -1056,6 +1954,13 @@ var TraceShipper = class {
1056
1954
  }
1057
1955
  const redacted = this.redactSpan(span);
1058
1956
  if (redacted === null) return;
1957
+ const destination = replayTraceDestinationFor(redacted);
1958
+ recordReplayExportedSpan(
1959
+ destination,
1960
+ base64ToHex(redacted.spanId),
1961
+ base64ToHex(redacted.traceId)
1962
+ );
1963
+ if ((destination == null ? void 0 : destination.kind) === "workshop") return;
1059
1964
  if (this.queue.length >= this.maxQueueSize) {
1060
1965
  this.queue.shift();
1061
1966
  }
@@ -1074,47 +1979,67 @@ var TraceShipper = class {
1074
1979
  }
1075
1980
  }
1076
1981
  async flush() {
1077
- if (!this.enabled) return;
1982
+ var _a;
1983
+ if (!this.enabled) {
1984
+ await Promise.all([...this.inFlight].map((p) => p.catch(() => {
1985
+ })));
1986
+ return;
1987
+ }
1078
1988
  if (this.timer) {
1079
1989
  clearTimeout(this.timer);
1080
1990
  this.timer = void 0;
1081
1991
  }
1082
1992
  while (this.queue.length > 0) {
1083
1993
  const batch = this.queue.splice(0, this.maxBatchSize);
1084
- if (!this.writeKey) continue;
1085
- const opts = this.requestOpts();
1086
- if (!opts) {
1087
- rateLimitedLog(
1088
- `${this.prefix}.shutdown_deadline`,
1089
- () => console.warn(
1090
- `${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
1091
- )
1092
- );
1093
- continue;
1994
+ const groups = /* @__PURE__ */ new Map();
1995
+ for (const span of batch) {
1996
+ const destination = replayTraceDestinationFor(span);
1997
+ const spans = groups.get(destination);
1998
+ if (spans) spans.push(span);
1999
+ else groups.set(destination, [span]);
1094
2000
  }
1095
- const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
1096
- const url = `${this.baseUrl}traces`;
1097
- if (this.debug) {
1098
- console.log(`${this.prefix} sending traces batch`, {
1099
- spans: batch.length,
1100
- endpoint: url
1101
- });
1102
- }
1103
- const p = postJson(url, body, this.requestHeaders(), opts);
1104
- this.inFlight.add(p);
1105
- try {
1106
- try {
1107
- await p;
1108
- if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
1109
- } catch (err) {
1110
- const msg = err instanceof Error ? err.message : String(err);
2001
+ for (const [destination, spans] of groups) {
2002
+ if (!destination && !this.writeKey) continue;
2003
+ if ((destination == null ? void 0 : destination.kind) === "workshop") continue;
2004
+ const opts = this.requestOpts();
2005
+ if (!opts) {
1111
2006
  rateLimitedLog(
1112
- `${this.prefix}.send_spans_failed`,
1113
- () => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
2007
+ `${this.prefix}.shutdown_deadline`,
2008
+ () => console.warn(
2009
+ `${this.prefix} shutdown flush deadline exceeded; dropping ${spans.length} spans`
2010
+ )
1114
2011
  );
2012
+ continue;
2013
+ }
2014
+ const body = buildExportTraceServiceRequest(spans, this.serviceName, this.serviceVersion);
2015
+ const url = destination ? replayTraceUrl(destination.url) : `${this.baseUrl}traces`;
2016
+ const headers = destination ? {
2017
+ Authorization: `Bearer ${destination.apiKey}`,
2018
+ "X-Raindrop-Replay-Id": destination.replayId,
2019
+ "X-Raindrop-Project-Id": (_a = destination.projectId) != null ? _a : "default"
2020
+ } : this.requestHeaders();
2021
+ if (this.debug) {
2022
+ console.log(`${this.prefix} sending traces batch`, {
2023
+ spans: spans.length,
2024
+ endpoint: url
2025
+ });
2026
+ }
2027
+ const p = postJson(url, body, headers, opts);
2028
+ this.inFlight.add(p);
2029
+ try {
2030
+ try {
2031
+ await p;
2032
+ if (this.debug) console.log(`${this.prefix} sent ${spans.length} spans`);
2033
+ } catch (err) {
2034
+ const msg = err instanceof Error ? err.message : String(err);
2035
+ rateLimitedLog(
2036
+ `${this.prefix}.send_spans_failed`,
2037
+ () => console.warn(`${this.prefix} failed to send ${spans.length} spans: ${msg}`)
2038
+ );
2039
+ }
2040
+ } finally {
2041
+ this.inFlight.delete(p);
1115
2042
  }
1116
- } finally {
1117
- this.inFlight.delete(p);
1118
2043
  }
1119
2044
  }
1120
2045
  }
@@ -1157,17 +2082,165 @@ var TraceShipper = class {
1157
2082
  if (!settled) {
1158
2083
  rateLimitedLog(
1159
2084
  `${this.prefix}.shutdown_deadline`,
1160
- () => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
2085
+ () => console.warn(
2086
+ `${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`
2087
+ )
1161
2088
  );
1162
2089
  }
1163
2090
  } finally {
1164
2091
  this.shutdownDeadlineAt = void 0;
2092
+ if (this.ownsAppGitContext) this.appGitContext.dispose();
1165
2093
  }
1166
2094
  }
1167
2095
  };
1168
2096
 
1169
2097
  // ../core/dist/index.node.js
1170
2098
  var import_async_hooks = require("async_hooks");
2099
+ var import_child_process = require("child_process");
2100
+ var import_path = require("path");
2101
+ var resolveLocal = (sourceDirectory, detectBranch, completed) => {
2102
+ const cwd = sourceDirectory ? (0, import_path.resolve)(sourceDirectory) : process.cwd();
2103
+ let cancelled = false;
2104
+ let active;
2105
+ let deadlineTimer;
2106
+ const deadline = Date.now() + 1e3;
2107
+ const run = (args, done) => {
2108
+ if (cancelled) return;
2109
+ const remaining = deadline - Date.now();
2110
+ if (remaining <= 0) {
2111
+ done(void 0);
2112
+ return;
2113
+ }
2114
+ try {
2115
+ active = (0, import_child_process.execFile)(
2116
+ "git",
2117
+ args,
2118
+ {
2119
+ cwd,
2120
+ encoding: "utf8",
2121
+ maxBuffer: 4096,
2122
+ windowsHide: true,
2123
+ env: {
2124
+ ...process.env,
2125
+ // The selected application cwd, not the invoking shell's Git plumbing, owns identity.
2126
+ GIT_DIR: void 0,
2127
+ GIT_WORK_TREE: void 0,
2128
+ GIT_COMMON_DIR: void 0,
2129
+ GIT_NAMESPACE: void 0,
2130
+ GIT_INDEX_FILE: void 0,
2131
+ GIT_OBJECT_DIRECTORY: void 0,
2132
+ GIT_ALTERNATE_OBJECT_DIRECTORIES: void 0,
2133
+ GIT_CONFIG_PARAMETERS: void 0,
2134
+ GIT_CONFIG_COUNT: "0",
2135
+ GIT_TERMINAL_PROMPT: "0",
2136
+ GIT_OPTIONAL_LOCKS: "0"
2137
+ }
2138
+ },
2139
+ (error, stdout) => {
2140
+ clearTimeout(deadlineTimer);
2141
+ deadlineTimer = void 0;
2142
+ active = void 0;
2143
+ if (!cancelled) {
2144
+ try {
2145
+ done(error ? void 0 : stdout.trim());
2146
+ } catch (e) {
2147
+ }
2148
+ }
2149
+ }
2150
+ );
2151
+ active.unref();
2152
+ if (active.stdout && "unref" in active.stdout && typeof active.stdout.unref === "function")
2153
+ active.stdout.unref();
2154
+ if (active.stderr && "unref" in active.stderr && typeof active.stderr.unref === "function")
2155
+ active.stderr.unref();
2156
+ deadlineTimer = setTimeout(() => {
2157
+ try {
2158
+ active == null ? void 0 : active.kill("SIGKILL");
2159
+ } catch (e) {
2160
+ }
2161
+ }, remaining);
2162
+ deadlineTimer.unref();
2163
+ } catch (e) {
2164
+ try {
2165
+ done(void 0);
2166
+ } catch (e2) {
2167
+ }
2168
+ }
2169
+ };
2170
+ const scheduled = setTimeout(() => {
2171
+ try {
2172
+ run(["rev-parse", "HEAD"], (rawSha) => {
2173
+ const commitSha = validAppCommitSha(rawSha);
2174
+ if (!commitSha) {
2175
+ completed({});
2176
+ return;
2177
+ }
2178
+ run(["-c", "core.fsmonitor=false", "status", "--porcelain"], (status) => {
2179
+ const finish = (branch) => {
2180
+ run(["rev-parse", "HEAD"], (finalSha) => {
2181
+ const coherent = validAppCommitSha(finalSha) === commitSha;
2182
+ completed({
2183
+ commitSha,
2184
+ ...coherent && status !== void 0 ? { commitDirty: status !== "" } : {},
2185
+ ...coherent && branch && branch !== "HEAD" ? { branch } : {}
2186
+ });
2187
+ });
2188
+ };
2189
+ if (detectBranch) run(["symbolic-ref", "--quiet", "--short", "HEAD"], finish);
2190
+ else finish();
2191
+ });
2192
+ });
2193
+ } catch (e) {
2194
+ completed({});
2195
+ }
2196
+ }, 0);
2197
+ scheduled.unref();
2198
+ return () => {
2199
+ cancelled = true;
2200
+ clearTimeout(scheduled);
2201
+ clearTimeout(deadlineTimer);
2202
+ try {
2203
+ active == null ? void 0 : active.kill("SIGKILL");
2204
+ } catch (e) {
2205
+ }
2206
+ };
2207
+ };
2208
+ function createAppGitContext2(options, runtime = {}) {
2209
+ return new AppGitContext(options, { ...runtime, resolveLocal });
2210
+ }
2211
+ function hasDestination(options) {
2212
+ var _a;
2213
+ if (options.enabled === false) return false;
2214
+ return Boolean(((_a = options.writeKey) == null ? void 0 : _a.trim()) || resolveLocalDebuggerBaseUrl(options.localDebuggerUrl));
2215
+ }
2216
+ var EventShipper2 = class extends EventShipper {
2217
+ constructor(options) {
2218
+ var _a;
2219
+ const ownsAppGitContext = options.appGitContext === void 0;
2220
+ const appGitContext = (_a = options.appGitContext) != null ? _a : createAppGitContext2(options.appGit, { allowDiscovery: hasDestination(options) });
2221
+ try {
2222
+ super({ ...options, appGitContext });
2223
+ } catch (error) {
2224
+ if (ownsAppGitContext) appGitContext.dispose();
2225
+ throw error;
2226
+ }
2227
+ if (ownsAppGitContext) this.ownAppGitContextForShutdown();
2228
+ }
2229
+ };
2230
+ var TraceShipper2 = class extends TraceShipper {
2231
+ constructor(options) {
2232
+ var _a;
2233
+ const ownsAppGitContext = options.appGitContext === void 0;
2234
+ const appGitContext = (_a = options.appGitContext) != null ? _a : createAppGitContext2(options.appGit, { allowDiscovery: hasDestination(options) });
2235
+ try {
2236
+ super({ ...options, appGitContext });
2237
+ } catch (error) {
2238
+ if (ownsAppGitContext) appGitContext.dispose();
2239
+ throw error;
2240
+ }
2241
+ this.ownsAppGitContext = ownsAppGitContext;
2242
+ }
2243
+ };
1171
2244
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
1172
2245
  var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
1173
2246
  "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
@@ -1195,12 +2268,84 @@ function installTracingSuppressionHook() {
1195
2268
  }
1196
2269
  installTracingSuppressionHook();
1197
2270
 
2271
+ // package.json
2272
+ var package_default = {
2273
+ name: "@raindrop-ai/claude-code",
2274
+ version: "0.0.20",
2275
+ description: "Raindrop observability for Claude Code CLI \u2014 automatic session, tool call, and prompt tracing via hooks",
2276
+ license: "MIT",
2277
+ homepage: "https://www.raindrop.ai/docs/integrations/claude-code/",
2278
+ bugs: {
2279
+ url: "https://www.raindrop.ai/docs/support/"
2280
+ },
2281
+ type: "module",
2282
+ main: "dist/index.cjs",
2283
+ module: "dist/index.js",
2284
+ types: "dist/index.d.ts",
2285
+ bin: {
2286
+ "raindrop-claude-code": "dist/cli.js"
2287
+ },
2288
+ exports: {
2289
+ ".": {
2290
+ import: {
2291
+ types: "./dist/index.d.ts",
2292
+ default: "./dist/index.js"
2293
+ },
2294
+ require: {
2295
+ types: "./dist/index.d.cts",
2296
+ default: "./dist/index.cjs"
2297
+ }
2298
+ }
2299
+ },
2300
+ sideEffects: false,
2301
+ files: [
2302
+ "dist/**",
2303
+ "README.md",
2304
+ ".claude-plugin/**",
2305
+ "hooks/**",
2306
+ ".mcp.json"
2307
+ ],
2308
+ keywords: [
2309
+ "raindrop",
2310
+ "claude-code",
2311
+ "claude",
2312
+ "anthropic",
2313
+ "observability",
2314
+ "tracing",
2315
+ "hooks",
2316
+ "cli"
2317
+ ],
2318
+ engines: {
2319
+ node: ">=18"
2320
+ },
2321
+ scripts: {
2322
+ build: "tsup",
2323
+ postbuild: "chmod +x ./dist/cli.js && node ./scripts/sync-plugin-version.mjs",
2324
+ prepack: "node ./scripts/sync-plugin-version.mjs",
2325
+ dev: "tsup --watch",
2326
+ clean: "rm -rf dist",
2327
+ test: "pnpm build && cd tests && pnpm test",
2328
+ "test:watch": "cd tests && pnpm test:watch"
2329
+ },
2330
+ devDependencies: {
2331
+ "@raindrop-ai/core": "workspace:*",
2332
+ "@types/node": "^20.11.17",
2333
+ msw: "^2.12.7",
2334
+ tsup: "^8.4.0",
2335
+ typescript: "^5.3.3",
2336
+ vitest: "^2.1.0"
2337
+ },
2338
+ publishConfig: {
2339
+ access: "public"
2340
+ }
2341
+ };
2342
+
1198
2343
  // src/package-info.ts
1199
2344
  var PACKAGE_NAME = "@raindrop-ai/claude-code";
1200
- var PACKAGE_VERSION = "0.0.13";
2345
+ var PACKAGE_VERSION = package_default.version;
1201
2346
 
1202
2347
  // src/shipper.ts
1203
- var EventShipper2 = class extends EventShipper {
2348
+ var EventShipper3 = class extends EventShipper2 {
1204
2349
  constructor(opts) {
1205
2350
  var _a, _b, _c, _d;
1206
2351
  super({
@@ -1212,7 +2357,7 @@ var EventShipper2 = class extends EventShipper {
1212
2357
  });
1213
2358
  }
1214
2359
  };
1215
- var TraceShipper2 = class extends TraceShipper {
2360
+ var TraceShipper3 = class extends TraceShipper2 {
1216
2361
  constructor(opts) {
1217
2362
  var _a, _b, _c;
1218
2363
  super({
@@ -1293,7 +2438,20 @@ function loadConfig() {
1293
2438
  })(),
1294
2439
  eventName: (_l = (_k = process.env["RAINDROP_EVENT_NAME"]) != null ? _k : file.event_name) != null ? _l : "claude_code_session",
1295
2440
  customProperties,
1296
- selfDiagnostics
2441
+ selfDiagnostics,
2442
+ appGit: mapAppGit(file.app_git)
2443
+ };
2444
+ }
2445
+ function mapAppGit(value) {
2446
+ if (value === false) return false;
2447
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
2448
+ return {
2449
+ commitSha: "commit_sha" in value && (typeof value.commit_sha === "string" || value.commit_sha === null) ? value.commit_sha : void 0,
2450
+ commitDirty: "commit_dirty" in value && (typeof value.commit_dirty === "boolean" || value.commit_dirty === null) ? value.commit_dirty : void 0,
2451
+ branch: "branch" in value && (typeof value.branch === "string" || value.branch === null) ? value.branch : void 0,
2452
+ detectBranch: "detect_branch" in value && typeof value.detect_branch === "boolean" ? value.detect_branch : void 0,
2453
+ sourceDirectory: "source_directory" in value && typeof value.source_directory === "string" ? value.source_directory : void 0,
2454
+ autoDetect: "auto_detect" in value && typeof value.auto_detect === "boolean" ? value.auto_detect : void 0
1297
2455
  };
1298
2456
  }
1299
2457
  function getConfigPath() {
@@ -2247,7 +3405,6 @@ var import_node_path3 = require("path");
2247
3405
  var DEFAULT_PORT = 5899;
2248
3406
  var DEFAULT_URL = `http://localhost:${DEFAULT_PORT}/v1/`;
2249
3407
  var HEALTH_TIMEOUT_MS = 300;
2250
- var MIRROR_TIMEOUT_MS = 2e3;
2251
3408
  var CACHE_DIR = (0, import_node_path3.join)((0, import_node_os3.tmpdir)(), "raindrop-claude-code");
2252
3409
  var CACHE_FILE = (0, import_node_path3.join)(CACHE_DIR, "debugger_cache");
2253
3410
  var CACHE_TTL_MS = 5e3;
@@ -2309,20 +3466,22 @@ async function detectLocalDebugger(debug) {
2309
3466
  }
2310
3467
  return { url: detected, autoDetected: detected !== null };
2311
3468
  }
2312
- function mirrorEventToLocalDebugger(baseUrl, payload, debug) {
3469
+ async function mirrorEventToLocalDebugger(baseUrl, payload, debug) {
2313
3470
  const url = `${baseUrl}events/track_partial`;
2314
3471
  if (debug) {
2315
3472
  console.log(`[raindrop-ai/claude-code] Mirroring event to local debugger: ${url}`);
2316
3473
  }
2317
- void runWithTracingSuppressed(
2318
- () => fetch(url, {
2319
- method: "POST",
2320
- headers: { "Content-Type": "application/json" },
2321
- body: JSON.stringify(payload),
2322
- signal: AbortSignal.timeout(MIRROR_TIMEOUT_MS)
2323
- })
2324
- ).catch(() => {
2325
- });
3474
+ try {
3475
+ await runWithTracingSuppressed(
3476
+ () => fetch(url, {
3477
+ method: "POST",
3478
+ headers: { "Content-Type": "application/json" },
3479
+ body: JSON.stringify(payload),
3480
+ signal: AbortSignal.timeout(LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS)
3481
+ })
3482
+ );
3483
+ } catch (e) {
3484
+ }
2326
3485
  }
2327
3486
 
2328
3487
  // src/mcp-serve.ts
@@ -2432,6 +3591,7 @@ function resolveCurrentEventId() {
2432
3591
  }
2433
3592
  }
2434
3593
  async function executeTool(args) {
3594
+ var _a;
2435
3595
  const category = typeof args["category"] === "string" ? args["category"] : "";
2436
3596
  const detail = typeof args["detail"] === "string" ? args["detail"] : "";
2437
3597
  if (!category || !activeSignals[category]) {
@@ -2457,12 +3617,17 @@ async function executeTool(args) {
2457
3617
  if (!eventId) {
2458
3618
  return { content: [{ type: "text", text: "Signal noted (no active event found)." }] };
2459
3619
  }
2460
- const shipper = new EventShipper2({
3620
+ const appGitContext = createAppGitContext2(
3621
+ ((_a = config.writeKey) == null ? void 0 : _a.trim()) ? config.appGit : false,
3622
+ { allowLocalGit: false }
3623
+ );
3624
+ const shipper = new EventShipper3({
2461
3625
  writeKey: config.writeKey,
2462
3626
  endpoint: config.endpoint,
2463
3627
  debug: false,
2464
3628
  projectId: config.projectId,
2465
- enabled: true
3629
+ enabled: true,
3630
+ appGitContext
2466
3631
  });
2467
3632
  try {
2468
3633
  const signalDef = activeSignals[category];
@@ -2489,6 +3654,11 @@ async function executeTool(args) {
2489
3654
  });
2490
3655
  await shipper.shutdown();
2491
3656
  } catch (e) {
3657
+ } finally {
3658
+ try {
3659
+ appGitContext.dispose();
3660
+ } catch (e) {
3661
+ }
2492
3662
  }
2493
3663
  return { content: [{ type: "text", text: "Signal recorded." }] };
2494
3664
  }
@@ -2616,6 +3786,7 @@ async function handleLine(line, toolSchema) {
2616
3786
  PACKAGE_VERSION,
2617
3787
  TOOL_SCHEMA,
2618
3788
  TraceShipper,
3789
+ createAppGitContext,
2619
3790
  detectLocalDebugger,
2620
3791
  executeTool,
2621
3792
  extractAppendSystemPrompt,