@raindrop-ai/claude-code 0.0.19 → 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-5PSWD267.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,25 @@ 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
+ }
90
635
  var HANDOFF_ATTRIBUTE_SUFFIXES = {
91
636
  mode: "raindrop.handoff.mode",
92
637
  childEventId: "raindrop.handoff.childEventId",
@@ -118,7 +663,7 @@ function runWithTracingSuppressed(fn) {
118
663
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
119
664
  var MAX_RETRY_DELAY_MS = 3e4;
120
665
  function wait(ms) {
121
- return new Promise((resolve) => setTimeout(resolve, ms));
666
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
122
667
  }
123
668
  function formatEndpoint(endpoint) {
124
669
  if (!endpoint) return void 0;
@@ -155,9 +700,9 @@ async function raceWithTimeout(promise, timeoutMs) {
155
700
  () => true,
156
701
  () => true
157
702
  ),
158
- new Promise((resolve) => {
703
+ new Promise((resolve2) => {
159
704
  var _a;
160
- timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
705
+ timer = setTimeout(() => resolve2(false), Math.max(0, timeoutMs));
161
706
  (_a = timer.unref) == null ? void 0 : _a.call(timer);
162
707
  })
163
708
  ]);
@@ -228,6 +773,7 @@ async function postJson(url, body, headers, opts) {
228
773
  const resp = await runWithTracingSuppressed(
229
774
  () => fetch(url, {
230
775
  method: "POST",
776
+ redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
231
777
  headers: {
232
778
  "Content-Type": "application/json",
233
779
  ...headers
@@ -438,6 +984,13 @@ function projectIdHeaders(projectId) {
438
984
  }
439
985
  var SHUTDOWN_DEADLINE_MS = 1e4;
440
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
+ }
441
994
  function mergePatches(target, source) {
442
995
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
443
996
  const out = { ...target, ...source };
@@ -487,22 +1040,25 @@ function eventTelemetryProperties(patch) {
487
1040
  }
488
1041
  var EventShipper = class {
489
1042
  constructor(opts) {
1043
+ this.appGitOwner = appGitOwnerId("event");
490
1044
  this.buffers = /* @__PURE__ */ new Map();
491
1045
  this.sticky = /* @__PURE__ */ new Map();
492
1046
  this.timers = /* @__PURE__ */ new Map();
493
1047
  this.inFlight = /* @__PURE__ */ new Set();
494
1048
  this.hasShutdown = false;
495
- var _a, _b, _c, _d, _e, _f, _g, _h;
496
- this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
497
- 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/";
498
1054
  this.enabled = opts.enabled !== false;
499
1055
  this.debug = opts.debug;
500
- this.partialFlushMs = (_c = opts.partialFlushMs) != null ? _c : 1e3;
501
- 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";
502
1058
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
503
- this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
1059
+ this.defaultEventName = (_f = opts.defaultEventName) != null ? _f : "ai_generation";
504
1060
  this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
505
- this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
1061
+ this.localDebuggerUrl = (_g = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _g : void 0;
506
1062
  if (this.debug && this.localDebuggerUrl) {
507
1063
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
508
1064
  }
@@ -513,8 +1069,8 @@ var EventShipper = class {
513
1069
  const isNode = typeof process !== "undefined" && typeof process.version === "string";
514
1070
  this.context = {
515
1071
  library: {
516
- name: (_g = opts.libraryName) != null ? _g : "@raindrop-ai/core",
517
- 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"
518
1074
  },
519
1075
  metadata: {
520
1076
  jsRuntime: isNode ? "node" : "web",
@@ -522,6 +1078,9 @@ var EventShipper = class {
522
1078
  }
523
1079
  };
524
1080
  }
1081
+ ownAppGitContextForShutdown() {
1082
+ this.ownsAppGitContext = true;
1083
+ }
525
1084
  isDebugEnabled() {
526
1085
  return this.debug;
527
1086
  }
@@ -570,6 +1129,15 @@ var EventShipper = class {
570
1129
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
571
1130
  if (!this.enabled) return;
572
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
+ }
573
1141
  const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
574
1142
  if (typeof patch.input === "string" && patch.input.length > maxChars) {
575
1143
  patch = { ...patch, input: capText(patch.input, maxChars) };
@@ -591,6 +1159,13 @@ var EventShipper = class {
591
1159
  }
592
1160
  const sticky = (_c = this.sticky.get(eventId)) != null ? _c : {};
593
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
+ };
594
1169
  const merged = mergePatches(existing, patch);
595
1170
  merged.isPending = (_g = (_f = (_e = patch.isPending) != null ? _e : existing.isPending) != null ? _f : sticky.isPending) != null ? _g : true;
596
1171
  this.buffers.set(eventId, merged);
@@ -604,6 +1179,8 @@ var EventShipper = class {
604
1179
  if (t) clearTimeout(t);
605
1180
  if (merged.isPending === false) {
606
1181
  await this.flushOne(eventId);
1182
+ if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(eventId, this.appGitOwner);
1183
+ else this.appGitContext.finishOperation(eventId);
607
1184
  return;
608
1185
  }
609
1186
  const timeout = setTimeout(() => {
@@ -628,15 +1205,21 @@ var EventShipper = class {
628
1205
  try {
629
1206
  for (const t of this.timers.values()) clearTimeout(t);
630
1207
  this.timers.clear();
1208
+ for (const [eventId, sticky] of this.sticky) {
1209
+ if (sticky.replayOnly) this.sticky.delete(eventId);
1210
+ }
631
1211
  const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
632
1212
  if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
633
1213
  } finally {
634
1214
  this.shutdownDeadlineAt = void 0;
1215
+ if (this.ownsAppGitContext) this.appGitContext.dispose();
635
1216
  }
636
1217
  }
637
1218
  async trackSignal(signal) {
638
1219
  var _a, _b;
639
1220
  if (!this.enabled) return;
1221
+ const destination = currentReplayTraceDestination();
1222
+ if (destination && destination.kind !== "workshop") return;
640
1223
  const body = [
641
1224
  {
642
1225
  event_id: signal.eventId,
@@ -671,6 +1254,8 @@ var EventShipper = class {
671
1254
  }
672
1255
  async identify(users) {
673
1256
  if (!this.enabled) return;
1257
+ const destination = currentReplayTraceDestination();
1258
+ if (destination && destination.kind !== "workshop") return;
674
1259
  const list = Array.isArray(users) ? users : [users];
675
1260
  const body = list.filter((user) => {
676
1261
  if (!(user == null ? void 0 : user.userId) || !user.userId.trim()) {
@@ -723,8 +1308,9 @@ var EventShipper = class {
723
1308
  this.buffers.delete(eventId);
724
1309
  if (!accumulated) return;
725
1310
  const sticky = (_a = this.sticky.get(eventId)) != null ? _a : {};
726
- const eventName = (_c = (_b = accumulated.eventName) != null ? _b : sticky.eventName) != null ? _c : this.defaultEventName;
727
- 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;
728
1314
  if (!userId) {
729
1315
  if (this.debug) {
730
1316
  console.warn(`${this.prefix} skipping track_partial for ${eventId}: missing userId`);
@@ -732,14 +1318,13 @@ var EventShipper = class {
732
1318
  this.sticky.delete(eventId);
733
1319
  return;
734
1320
  }
735
- const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
1321
+ const { wizardSession, ...restProperties } = (_g = accumulated.properties) != null ? _g : {};
736
1322
  let telemetryProperties = {};
737
1323
  try {
738
1324
  telemetryProperties = eventTelemetryProperties(accumulated);
739
1325
  } catch (e) {
740
1326
  }
741
- const convoId = (_f = accumulated.convoId) != null ? _f : sticky.convoId;
742
- const isPending = (_h = (_g = accumulated.isPending) != null ? _g : sticky.isPending) != null ? _h : true;
1327
+ const convoId = (_h = accumulated.convoId) != null ? _h : sticky.convoId;
743
1328
  const payload = {
744
1329
  event_id: eventId,
745
1330
  user_id: userId,
@@ -1055,6 +1640,13 @@ function redactJsonAttributeValue(key, value) {
1055
1640
  if (scrubbedJson === json) return void 0;
1056
1641
  return { stringValue: scrubbedJson };
1057
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
+ }
1058
1650
  function identityAttrs(args) {
1059
1651
  return [
1060
1652
  attrString("ai.telemetry.metadata.raindrop.ai.userId", args.userId),
@@ -1077,23 +1669,30 @@ function applyOtelSpanAttributeLimit(limit) {
1077
1669
  }
1078
1670
  var TraceShipper = class {
1079
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();
1080
1677
  this.queue = [];
1081
1678
  this.inFlight = /* @__PURE__ */ new Set();
1082
1679
  this.hasShutdown = false;
1083
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1084
- this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
1085
- 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/";
1086
1685
  this.enabled = opts.enabled !== false;
1087
1686
  this.debug = opts.debug;
1088
1687
  this.debugSpans = opts.debugSpans === true;
1089
- this.flushIntervalMs = (_c = opts.flushIntervalMs) != null ? _c : 1e3;
1090
- this.maxBatchSize = (_d = opts.maxBatchSize) != null ? _d : 50;
1091
- this.maxQueueSize = (_e = opts.maxQueueSize) != null ? _e : 5e3;
1092
- 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";
1093
1692
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
1094
- this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
1095
- this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
1096
- 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;
1097
1696
  if (this.debug && this.localDebuggerUrl) {
1098
1697
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
1099
1698
  }
@@ -1157,7 +1756,10 @@ var TraceShipper = class {
1157
1756
  try {
1158
1757
  const result = this.transformSpanHook(current);
1159
1758
  if (result === null) return null;
1160
- if (result !== void 0) current = result;
1759
+ if (result !== void 0) {
1760
+ copyReplayTraceDestination(current, result);
1761
+ current = result;
1762
+ }
1161
1763
  } catch (err) {
1162
1764
  if (this.debug) {
1163
1765
  const msg = err instanceof Error ? err.message : String(err);
@@ -1167,13 +1769,19 @@ var TraceShipper = class {
1167
1769
  }
1168
1770
  }
1169
1771
  try {
1170
- current = normalizeModelUsageSpan(current);
1772
+ const normalized = normalizeModelUsageSpan(current);
1773
+ copyReplayTraceDestination(current, normalized);
1774
+ current = normalized;
1171
1775
  } catch (e) {
1172
1776
  }
1173
1777
  if (!this.disableDefaultRedaction) {
1174
- current = defaultTransformSpan(current);
1778
+ const redacted = defaultTransformSpan(current);
1779
+ copyReplayTraceDestination(current, redacted);
1780
+ current = redacted;
1175
1781
  }
1176
- return this.capSpanAttributes(current);
1782
+ const capped = this.capSpanAttributes(current);
1783
+ copyReplayTraceDestination(current, capped);
1784
+ return capped;
1177
1785
  }
1178
1786
  isDebugEnabled() {
1179
1787
  return this.debug;
@@ -1185,36 +1793,64 @@ var TraceShipper = class {
1185
1793
  return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
1186
1794
  }
1187
1795
  startSpan(args) {
1188
- var _a, _b;
1796
+ var _a, _b, _c;
1189
1797
  const ids = createSpanIds(args.parent);
1190
1798
  const started = (_a = args.startTimeUnixNano) != null ? _a : nowUnixNanoString();
1191
1799
  const attrs = [
1192
1800
  attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
1193
1801
  ...identityAttrs(args),
1194
- attrString("ai.operationId", args.operationId)
1802
+ attrString("ai.operationId", args.operationId),
1803
+ ...Object.entries(evalScopeAttributes()).map(([key, value]) => attrString(key, value))
1195
1804
  ];
1196
1805
  if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
1197
- const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
1198
- this.mirrorToLocalDebugger(
1199
- buildOtlpSpan({
1200
- ids: span.ids,
1201
- name: span.name,
1202
- startTimeUnixNano: span.startTimeUnixNano,
1203
- endTimeUnixNano: span.startTimeUnixNano,
1204
- // placeholder — will be updated on endSpan
1205
- attributes: span.attributes,
1206
- status: { code: SpanStatusCode.UNSET }
1207
- })
1208
- );
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);
1209
1835
  return span;
1210
1836
  }
1211
- mirrorToLocalDebugger(span) {
1212
- 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;
1213
1842
  const redacted = this.redactSpan(span);
1214
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
+ }
1215
1851
  const body = buildExportTraceServiceRequest([redacted], this.serviceName, this.serviceVersion);
1216
1852
  const p = mirrorTraceExportToLocalDebugger(body, {
1217
- baseUrl: this.localDebuggerUrl,
1853
+ baseUrl: localDebuggerUrl,
1218
1854
  debug: false,
1219
1855
  sdkName: this.sdkName
1220
1856
  });
@@ -1224,11 +1860,17 @@ var TraceShipper = class {
1224
1860
  });
1225
1861
  }
1226
1862
  endSpan(span, extra) {
1227
- var _a, _b;
1863
+ var _a, _b, _c;
1228
1864
  if (span.endTimeUnixNano) return;
1229
1865
  span.endTimeUnixNano = (_a = extra == null ? void 0 : extra.endTimeUnixNano) != null ? _a : nowUnixNanoString();
1230
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));
1231
1872
  span.attributes.push(...extra.attributes);
1873
+ span.attributes = withAppGitAttributes(span.attributes, snapshot);
1232
1874
  }
1233
1875
  let status = extra == null ? void 0 : extra.status;
1234
1876
  if (!status && (extra == null ? void 0 : extra.error) !== void 0) {
@@ -1243,30 +1885,65 @@ var TraceShipper = class {
1243
1885
  attributes: span.attributes,
1244
1886
  status
1245
1887
  });
1888
+ copyReplayTraceDestination(span, otlp);
1889
+ this.appGitCaptured.add(otlp);
1246
1890
  this.enqueue(otlp);
1247
- 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
+ }
1248
1902
  }
1249
1903
  createSpan(args) {
1250
1904
  var _a;
1251
1905
  const ids = createSpanIds(args.parent);
1252
1906
  const attrs = [
1253
1907
  attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
1254
- ...identityAttrs(args)
1908
+ ...identityAttrs(args),
1909
+ ...Object.entries(evalScopeAttributes()).map(([key, value]) => attrString(key, value))
1255
1910
  ];
1256
1911
  if ((_a = args.attributes) == null ? void 0 : _a.length) attrs.push(...args.attributes);
1912
+ const tracksAppGit = !this.appGitActiveSpans.has(args.eventId);
1257
1913
  const otlp = buildOtlpSpan({
1258
1914
  ids,
1259
1915
  name: args.name,
1260
1916
  startTimeUnixNano: args.startTimeUnixNano,
1261
1917
  endTimeUnixNano: args.endTimeUnixNano,
1262
- attributes: attrs,
1918
+ attributes: withAppGitAttributes(
1919
+ attrs,
1920
+ this.appGitContext.forOperation(
1921
+ args.eventId,
1922
+ appGitPropertiesFromAttributes(attrs),
1923
+ this.appGitOwner
1924
+ )
1925
+ ),
1263
1926
  status: args.status
1264
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
+ }
1265
1934
  this.enqueue(otlp);
1266
- this.mirrorToLocalDebugger(otlp);
1935
+ this.mirrorToLocalDebugger(otlp, true);
1267
1936
  }
1268
1937
  enqueue(span) {
1938
+ var _a;
1269
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
+ }
1270
1947
  if (this.debugSpans) {
1271
1948
  const short = (s) => s ? s.slice(-8) : "none";
1272
1949
  console.log(
@@ -1277,6 +1954,13 @@ var TraceShipper = class {
1277
1954
  }
1278
1955
  const redacted = this.redactSpan(span);
1279
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;
1280
1964
  if (this.queue.length >= this.maxQueueSize) {
1281
1965
  this.queue.shift();
1282
1966
  }
@@ -1295,6 +1979,7 @@ var TraceShipper = class {
1295
1979
  }
1296
1980
  }
1297
1981
  async flush() {
1982
+ var _a;
1298
1983
  if (!this.enabled) {
1299
1984
  await Promise.all([...this.inFlight].map((p) => p.catch(() => {
1300
1985
  })));
@@ -1306,40 +1991,55 @@ var TraceShipper = class {
1306
1991
  }
1307
1992
  while (this.queue.length > 0) {
1308
1993
  const batch = this.queue.splice(0, this.maxBatchSize);
1309
- if (!this.writeKey) continue;
1310
- const opts = this.requestOpts();
1311
- if (!opts) {
1312
- rateLimitedLog(
1313
- `${this.prefix}.shutdown_deadline`,
1314
- () => console.warn(
1315
- `${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
1316
- )
1317
- );
1318
- continue;
1319
- }
1320
- const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
1321
- const url = `${this.baseUrl}traces`;
1322
- if (this.debug) {
1323
- console.log(`${this.prefix} sending traces batch`, {
1324
- spans: batch.length,
1325
- endpoint: url
1326
- });
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]);
1327
2000
  }
1328
- const p = postJson(url, body, this.requestHeaders(), opts);
1329
- this.inFlight.add(p);
1330
- try {
1331
- try {
1332
- await p;
1333
- if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
1334
- } catch (err) {
1335
- 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) {
1336
2006
  rateLimitedLog(
1337
- `${this.prefix}.send_spans_failed`,
1338
- () => 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
+ )
1339
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);
1340
2042
  }
1341
- } finally {
1342
- this.inFlight.delete(p);
1343
2043
  }
1344
2044
  }
1345
2045
  }
@@ -1382,17 +2082,165 @@ var TraceShipper = class {
1382
2082
  if (!settled) {
1383
2083
  rateLimitedLog(
1384
2084
  `${this.prefix}.shutdown_deadline`,
1385
- () => 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
+ )
1386
2088
  );
1387
2089
  }
1388
2090
  } finally {
1389
2091
  this.shutdownDeadlineAt = void 0;
2092
+ if (this.ownsAppGitContext) this.appGitContext.dispose();
1390
2093
  }
1391
2094
  }
1392
2095
  };
1393
2096
 
1394
2097
  // ../core/dist/index.node.js
1395
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
+ };
1396
2244
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
1397
2245
  var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
1398
2246
  "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
@@ -1423,7 +2271,7 @@ installTracingSuppressionHook();
1423
2271
  // package.json
1424
2272
  var package_default = {
1425
2273
  name: "@raindrop-ai/claude-code",
1426
- version: "0.0.19",
2274
+ version: "0.0.20",
1427
2275
  description: "Raindrop observability for Claude Code CLI \u2014 automatic session, tool call, and prompt tracing via hooks",
1428
2276
  license: "MIT",
1429
2277
  homepage: "https://www.raindrop.ai/docs/integrations/claude-code/",
@@ -1497,7 +2345,7 @@ var PACKAGE_NAME = "@raindrop-ai/claude-code";
1497
2345
  var PACKAGE_VERSION = package_default.version;
1498
2346
 
1499
2347
  // src/shipper.ts
1500
- var EventShipper2 = class extends EventShipper {
2348
+ var EventShipper3 = class extends EventShipper2 {
1501
2349
  constructor(opts) {
1502
2350
  var _a, _b, _c, _d;
1503
2351
  super({
@@ -1509,7 +2357,7 @@ var EventShipper2 = class extends EventShipper {
1509
2357
  });
1510
2358
  }
1511
2359
  };
1512
- var TraceShipper2 = class extends TraceShipper {
2360
+ var TraceShipper3 = class extends TraceShipper2 {
1513
2361
  constructor(opts) {
1514
2362
  var _a, _b, _c;
1515
2363
  super({
@@ -1590,7 +2438,20 @@ function loadConfig() {
1590
2438
  })(),
1591
2439
  eventName: (_l = (_k = process.env["RAINDROP_EVENT_NAME"]) != null ? _k : file.event_name) != null ? _l : "claude_code_session",
1592
2440
  customProperties,
1593
- 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
1594
2455
  };
1595
2456
  }
1596
2457
  function getConfigPath() {
@@ -2730,6 +3591,7 @@ function resolveCurrentEventId() {
2730
3591
  }
2731
3592
  }
2732
3593
  async function executeTool(args) {
3594
+ var _a;
2733
3595
  const category = typeof args["category"] === "string" ? args["category"] : "";
2734
3596
  const detail = typeof args["detail"] === "string" ? args["detail"] : "";
2735
3597
  if (!category || !activeSignals[category]) {
@@ -2755,12 +3617,17 @@ async function executeTool(args) {
2755
3617
  if (!eventId) {
2756
3618
  return { content: [{ type: "text", text: "Signal noted (no active event found)." }] };
2757
3619
  }
2758
- 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({
2759
3625
  writeKey: config.writeKey,
2760
3626
  endpoint: config.endpoint,
2761
3627
  debug: false,
2762
3628
  projectId: config.projectId,
2763
- enabled: true
3629
+ enabled: true,
3630
+ appGitContext
2764
3631
  });
2765
3632
  try {
2766
3633
  const signalDef = activeSignals[category];
@@ -2787,6 +3654,11 @@ async function executeTool(args) {
2787
3654
  });
2788
3655
  await shipper.shutdown();
2789
3656
  } catch (e) {
3657
+ } finally {
3658
+ try {
3659
+ appGitContext.dispose();
3660
+ } catch (e) {
3661
+ }
2790
3662
  }
2791
3663
  return { content: [{ type: "text", text: "Signal recorded." }] };
2792
3664
  }
@@ -2914,6 +3786,7 @@ async function handleLine(line, toolSchema) {
2914
3786
  PACKAGE_VERSION,
2915
3787
  TOOL_SCHEMA,
2916
3788
  TraceShipper,
3789
+ createAppGitContext,
2917
3790
  detectLocalDebugger,
2918
3791
  executeTool,
2919
3792
  extractAppendSystemPrompt,