@raindrop-ai/opencode-plugin 0.1.2 → 0.1.4

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
@@ -24,7 +24,521 @@ __export(index_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(index_exports);
26
26
 
27
- // ../core/dist/chunk-YUIXRQWN.js
27
+ // ../core/dist/chunk-BYBMRLZM.js
28
+ var EVAL_CORRELATION_ID_ATTRIBUTE = "raindrop.eval_correlation_id";
29
+ var PROBE_SCOPE = { correlationId: "" };
30
+ var EvalScopeStorage = class {
31
+ constructor() {
32
+ this._als = null;
33
+ this._probed = false;
34
+ }
35
+ maybeAdoptAsyncLocalStorage() {
36
+ if (this._probed) return;
37
+ this._probed = true;
38
+ try {
39
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
40
+ if (!Ctor) return;
41
+ const als = new Ctor();
42
+ als.run(PROBE_SCOPE, () => {
43
+ });
44
+ this._als = als;
45
+ } catch (e) {
46
+ this._als = null;
47
+ }
48
+ }
49
+ run(scope, callback) {
50
+ this.maybeAdoptAsyncLocalStorage();
51
+ if (this._als) return this._als.run(scope, callback);
52
+ const previous = this._syncScope;
53
+ this._syncScope = scope;
54
+ try {
55
+ return callback();
56
+ } finally {
57
+ this._syncScope = previous;
58
+ }
59
+ }
60
+ current() {
61
+ this.maybeAdoptAsyncLocalStorage();
62
+ if (this._als) return this._als.getStore();
63
+ return this._syncScope;
64
+ }
65
+ };
66
+ var EVAL_SCOPE_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.evalScopeStorage");
67
+ function evalScopeStorage() {
68
+ const holder = globalThis;
69
+ let storage = holder[EVAL_SCOPE_STORAGE_KEY];
70
+ if (!storage) {
71
+ storage = new EvalScopeStorage();
72
+ holder[EVAL_SCOPE_STORAGE_KEY] = storage;
73
+ }
74
+ return storage;
75
+ }
76
+ function currentEvalScope() {
77
+ try {
78
+ return evalScopeStorage().current();
79
+ } catch (e) {
80
+ return void 0;
81
+ }
82
+ }
83
+ function evalScopeAttributes() {
84
+ const scope = currentEvalScope();
85
+ if (!scope) return {};
86
+ return { [EVAL_CORRELATION_ID_ATTRIBUTE]: scope.correlationId };
87
+ }
88
+
89
+ // ../core/dist/chunk-WJOR6M7H.js
90
+ var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
91
+ function state() {
92
+ const holder = globalThis;
93
+ let current = holder[STATE_KEY];
94
+ if (!current) {
95
+ let storage = null;
96
+ try {
97
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
98
+ if (Ctor) storage = new Ctor();
99
+ } catch (e) {
100
+ storage = null;
101
+ }
102
+ current = {
103
+ storage,
104
+ syncDestination: void 0,
105
+ spanDestinations: /* @__PURE__ */ new WeakMap()
106
+ };
107
+ holder[STATE_KEY] = current;
108
+ }
109
+ return current;
110
+ }
111
+ function currentReplayTraceDestination() {
112
+ var _a, _b;
113
+ const routing = state();
114
+ return (_b = (_a = routing.storage) == null ? void 0 : _a.getStore()) != null ? _b : routing.syncDestination;
115
+ }
116
+ function rememberReplayTraceDestination(span) {
117
+ const destination = currentReplayTraceDestination();
118
+ if (destination) state().spanDestinations.set(span, destination);
119
+ }
120
+ function copyReplayTraceDestination(source, target) {
121
+ const destination = state().spanDestinations.get(source);
122
+ if (destination) state().spanDestinations.set(target, destination);
123
+ }
124
+ function replayTraceDestinationFor(value) {
125
+ if (typeof value !== "object" && typeof value !== "function" || value === null) {
126
+ return void 0;
127
+ }
128
+ return state().spanDestinations.get(value);
129
+ }
130
+ function recordReplayExportedSpan(destination, spanId, traceId) {
131
+ var _a, _b;
132
+ if (!destination || !spanId || !traceId) return;
133
+ (_a = destination.exportedSpanIds) == null ? void 0 : _a.add(spanId);
134
+ (_b = destination.exportedTraceIds) == null ? void 0 : _b.add(traceId);
135
+ }
136
+ function replayTraceUrl(baseUrl) {
137
+ return `${baseUrl.replace(/\/+$/, "")}/v1/traces`;
138
+ }
139
+ function getAsyncLocalStorageCtor() {
140
+ return globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
141
+ }
142
+ var MAX_BINDING_STACK = 128;
143
+ var EMPTY_STACK = [];
144
+ var ContextStackStorage = class {
145
+ /** Emitted once when this runtime cannot support an ambient push. */
146
+ constructor(noAmbientBindWarning) {
147
+ this.noAmbientBindWarning = noAmbientBindWarning;
148
+ this._als = null;
149
+ this._enterWithUsable = false;
150
+ this._syncStack = EMPTY_STACK;
151
+ this._adoptionChecked = false;
152
+ this._warnedNoAmbientBind = false;
153
+ }
154
+ maybeAdoptAsyncLocalStorage() {
155
+ if (this._adoptionChecked) return;
156
+ this._adoptionChecked = true;
157
+ const Ctor = getAsyncLocalStorageCtor();
158
+ if (!Ctor) return;
159
+ const als = new Ctor();
160
+ try {
161
+ als.run(EMPTY_STACK, () => {
162
+ });
163
+ } catch (e) {
164
+ return;
165
+ }
166
+ this._als = als;
167
+ if (typeof als.enterWith === "function") {
168
+ try {
169
+ als.enterWith(EMPTY_STACK);
170
+ this._enterWithUsable = true;
171
+ } catch (e) {
172
+ this._enterWithUsable = false;
173
+ }
174
+ }
175
+ }
176
+ getStack() {
177
+ var _a;
178
+ this.maybeAdoptAsyncLocalStorage();
179
+ if (this._als) return (_a = this._als.getStore()) != null ? _a : this._syncStack;
180
+ return this._syncStack;
181
+ }
182
+ /**
183
+ * Persist an ambient stack mutation. Returns false when an ALS is adopted but
184
+ * `enterWith` is unusable (workerd): callers must NOT fall back to the
185
+ * process-shared sync stack, since concurrent isolates would cross-stamp.
186
+ */
187
+ setAmbientStack(stack) {
188
+ if (this._als) {
189
+ if (!this._enterWithUsable) return false;
190
+ this._als.enterWith(stack);
191
+ return true;
192
+ }
193
+ this._syncStack = stack;
194
+ return true;
195
+ }
196
+ push(binding) {
197
+ this.maybeAdoptAsyncLocalStorage();
198
+ if (this._als && !this._enterWithUsable) {
199
+ this.warnNoAmbientBindOnce();
200
+ return;
201
+ }
202
+ let stack = this.getStack();
203
+ if (stack.length >= MAX_BINDING_STACK) {
204
+ stack = stack.filter((b) => b.isLive());
205
+ if (stack.length >= MAX_BINDING_STACK) {
206
+ stack = stack.slice(stack.length - (MAX_BINDING_STACK - 1));
207
+ }
208
+ }
209
+ this.setAmbientStack([...stack, binding]);
210
+ }
211
+ remove(binding) {
212
+ binding.markDead();
213
+ const stack = this.getStack();
214
+ const pruned = stack.filter((b) => b !== binding && b.isLive());
215
+ if (pruned.length !== stack.length) {
216
+ this.setAmbientStack(pruned);
217
+ }
218
+ }
219
+ runWith(binding, callback) {
220
+ this.maybeAdoptAsyncLocalStorage();
221
+ if (this._als) {
222
+ return this._als.run([...this.getStack(), binding], callback);
223
+ }
224
+ const previous = this._syncStack;
225
+ this._syncStack = [...previous, binding];
226
+ try {
227
+ return callback();
228
+ } finally {
229
+ this._syncStack = previous;
230
+ }
231
+ }
232
+ current() {
233
+ const stack = this.getStack();
234
+ if (stack.length === 0) return void 0;
235
+ const top = stack[stack.length - 1];
236
+ if (top.isLive()) return top;
237
+ const pruned = stack.filter((b) => b.isLive());
238
+ this.setAmbientStack(pruned);
239
+ return pruned.length > 0 ? pruned[pruned.length - 1] : void 0;
240
+ }
241
+ warnNoAmbientBindOnce() {
242
+ if (this._warnedNoAmbientBind) return;
243
+ this._warnedNoAmbientBind = true;
244
+ console.warn(this.noAmbientBindWarning);
245
+ }
246
+ };
247
+ 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.";
248
+ var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
249
+ function routingStorage() {
250
+ const holder = globalThis;
251
+ let storage = holder[ROUTING_STORAGE_KEY];
252
+ if (!storage) {
253
+ storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
254
+ holder[ROUTING_STORAGE_KEY] = storage;
255
+ }
256
+ return storage;
257
+ }
258
+ function currentRoutingBinding() {
259
+ const bound = routingStorage().current();
260
+ if (!bound) return void 0;
261
+ return {
262
+ projectId: bound.projectId,
263
+ authHint: bound.authHint,
264
+ ...bound.appGit ? { appGit: bound.appGit } : {},
265
+ ...bound.appGitSource ? { appGitSource: bound.appGitSource } : {}
266
+ };
267
+ }
268
+ var APP_COMMIT_SHA = "raindrop.app.commit_sha";
269
+ var APP_COMMIT_DIRTY = "raindrop.app.commit_dirty";
270
+ var APP_BRANCH = "raindrop.app.branch";
271
+ var APP_GIT_KEYS = [APP_COMMIT_SHA, APP_COMMIT_DIRTY, APP_BRANCH];
272
+ var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
273
+ function appGitExplicitKeys(properties) {
274
+ const keys = /* @__PURE__ */ new Set();
275
+ if (!properties) return keys;
276
+ for (const key of APP_GIT_KEYS) {
277
+ if (own(properties, key)) keys.add(key);
278
+ }
279
+ return keys;
280
+ }
281
+ function mergeAppGitSnapshot(base, properties, explicitKeys = /* @__PURE__ */ new Set()) {
282
+ try {
283
+ const result = { ...base };
284
+ if (properties && own(properties, APP_COMMIT_SHA) && properties[APP_COMMIT_SHA] !== result[APP_COMMIT_SHA]) {
285
+ if (!explicitKeys.has(APP_COMMIT_DIRTY) && !own(properties, APP_COMMIT_DIRTY)) {
286
+ delete result[APP_COMMIT_DIRTY];
287
+ }
288
+ if (!explicitKeys.has(APP_BRANCH) && !own(properties, APP_BRANCH)) {
289
+ delete result[APP_BRANCH];
290
+ }
291
+ }
292
+ for (const key of APP_GIT_KEYS) {
293
+ if (properties && own(properties, key)) result[key] = properties[key];
294
+ }
295
+ return Object.freeze(result);
296
+ } catch (e) {
297
+ return Object.freeze({});
298
+ }
299
+ }
300
+ function validAppCommitSha(value) {
301
+ if (typeof value !== "string" || value.length > 128) return void 0;
302
+ const cleaned = value.trim();
303
+ return /^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/.test(cleaned) ? cleaned.toLowerCase() : void 0;
304
+ }
305
+ function shortString(value) {
306
+ if (typeof value !== "string" || value.length > 1024) return void 0;
307
+ const cleaned = value.trim();
308
+ return cleaned.length > 0 ? cleaned : void 0;
309
+ }
310
+ function flag(value) {
311
+ if (value === true || value === "true" || value === "1") return true;
312
+ if (value === false || value === "false" || value === "0") return false;
313
+ return void 0;
314
+ }
315
+ function environment() {
316
+ try {
317
+ return typeof process === "undefined" ? {} : process.env;
318
+ } catch (e) {
319
+ return {};
320
+ }
321
+ }
322
+ var DEPLOYMENTS = [
323
+ ["VERCEL", "VERCEL_GIT_COMMIT_SHA", "VERCEL_GIT_COMMIT_REF"],
324
+ ["NETLIFY", "COMMIT_REF", "BRANCH"],
325
+ ["RAILWAY_ENVIRONMENT_NAME", "RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH"],
326
+ ["RAILWAY_ENVIRONMENT", "RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH"],
327
+ ["RENDER", "RENDER_GIT_COMMIT", "RENDER_GIT_BRANCH"],
328
+ ["DYNO", "HEROKU_BUILD_COMMIT", ""],
329
+ ["DYNO", "HEROKU_SLUG_COMMIT", ""],
330
+ ["AWS_APP_ID", "AWS_COMMIT_ID", "AWS_BRANCH"],
331
+ ["WORKERS_CI", "WORKERS_CI_COMMIT_SHA", "WORKERS_CI_BRANCH"]
332
+ ];
333
+ var CI_SOURCES = [
334
+ ["GITHUB_ACTIONS", "GITHUB_SHA", "GITHUB_REF_NAME"],
335
+ ["GITLAB_CI", "CI_COMMIT_SHA", "CI_COMMIT_REF_NAME"],
336
+ ["CIRCLECI", "CIRCLE_SHA1", "CIRCLE_BRANCH"]
337
+ ];
338
+ function detectedSource(env, table, detectBranch) {
339
+ for (const row of table) {
340
+ const [marker, shaKey, branchKey] = row;
341
+ if (!marker || !shaKey || !shortString(env[marker]) || flag(env[marker]) === false) continue;
342
+ const commitSha = validAppCommitSha(env[shaKey]);
343
+ if (commitSha)
344
+ return {
345
+ commitSha,
346
+ ...detectBranch && branchKey ? { branch: shortString(env[branchKey]) } : {}
347
+ };
348
+ }
349
+ return {};
350
+ }
351
+ var AppGitContextImpl = class {
352
+ constructor(options, runtime = {}) {
353
+ this.defaults = {};
354
+ this.explicit = {};
355
+ this.explicitKeys = /* @__PURE__ */ new Set();
356
+ this.disposed = false;
357
+ this.operations = /* @__PURE__ */ new Map();
358
+ this.operationOverflow = false;
359
+ this.detectBranch = false;
360
+ this.disabled = false;
361
+ var _a, _b, _c, _d, _e, _f;
362
+ try {
363
+ if (options === false) {
364
+ this.disabled = true;
365
+ return;
366
+ }
367
+ const opts = options != null ? options : {};
368
+ const env = (_a = runtime.env) != null ? _a : environment();
369
+ const detectBranch = (_c = (_b = flag(opts.detectBranch)) != null ? _b : flag(env.RAINDROP_GIT_DETECT_BRANCH)) != null ? _c : false;
370
+ this.detectBranch = detectBranch;
371
+ const collectBranch = detectBranch || runtime.captureLegacyReplayBranch === true;
372
+ const autoDetect = runtime.allowDiscovery === false ? false : (_e = (_d = flag(opts.autoDetect)) != null ? _d : flag(env.RAINDROP_GIT_AUTO_DETECT)) != null ? _e : true;
373
+ const configuredSha = opts.commitSha;
374
+ const explicitSha = configuredSha === void 0 ? env.RAINDROP_COMMIT_SHA : configuredSha;
375
+ if (explicitSha === null) this.explicit[APP_COMMIT_SHA] = null;
376
+ else if (typeof explicitSha === "string" && validAppCommitSha(explicitSha))
377
+ this.explicit[APP_COMMIT_SHA] = explicitSha;
378
+ const configuredDirty = opts.commitDirty;
379
+ const explicitDirty = configuredDirty === void 0 ? flag(env.RAINDROP_COMMIT_DIRTY) : configuredDirty;
380
+ if (explicitDirty === null || typeof explicitDirty === "boolean")
381
+ this.explicit[APP_COMMIT_DIRTY] = explicitDirty;
382
+ const configuredBranch = opts.branch;
383
+ const explicitBranch = configuredBranch === void 0 ? env.RAINDROP_BRANCH : configuredBranch;
384
+ if (explicitBranch === null) this.explicit[APP_BRANCH] = null;
385
+ else if (typeof explicitBranch === "string" && explicitBranch.length <= 1024)
386
+ this.explicit[APP_BRANCH] = explicitBranch;
387
+ this.explicitKeys = appGitExplicitKeys(this.explicit);
388
+ this.defaults = { ...this.explicit };
389
+ if (!autoDetect || explicitSha !== void 0) return;
390
+ const sourceDirectory = (_f = shortString(opts.sourceDirectory)) != null ? _f : shortString(env.RAINDROP_GIT_SOURCE_DIRECTORY);
391
+ if (runtime.allowLocalGit === false && !sourceDirectory) return;
392
+ const deploy = sourceDirectory ? {} : detectedSource(env, DEPLOYMENTS, collectBranch);
393
+ if (deploy.commitSha) {
394
+ this.accept(deploy);
395
+ return;
396
+ }
397
+ const fallback = sourceDirectory ? {} : detectedSource(env, CI_SOURCES, collectBranch);
398
+ if (runtime.resolveLocal && (runtime.allowLocalGit !== false || sourceDirectory)) {
399
+ this.cancel = runtime.resolveLocal(sourceDirectory, collectBranch, (result) => {
400
+ try {
401
+ if (!this.disposed) this.accept(result.commitSha ? result : fallback);
402
+ } catch (e) {
403
+ }
404
+ });
405
+ } else this.accept(fallback);
406
+ } catch (e) {
407
+ }
408
+ }
409
+ accept(result) {
410
+ const sha = validAppCommitSha(result.commitSha);
411
+ if (!sha) return;
412
+ this.replayBranch = shortString(result.branch);
413
+ this.defaults = {
414
+ [APP_COMMIT_SHA]: sha,
415
+ ...typeof result.commitDirty === "boolean" ? { [APP_COMMIT_DIRTY]: result.commitDirty } : {},
416
+ ...this.detectBranch && this.replayBranch ? { [APP_BRANCH]: this.replayBranch } : {},
417
+ ...this.explicit
418
+ };
419
+ }
420
+ snapshot(properties) {
421
+ var _a;
422
+ try {
423
+ const binding = currentRoutingBinding();
424
+ const base = this.disabled && (binding == null ? void 0 : binding.appGitSource) !== "replay-override" ? {} : (_a = binding == null ? void 0 : binding.appGit) != null ? _a : this.defaults;
425
+ return this.mergeSnapshot(base, properties);
426
+ } catch (e) {
427
+ return Object.freeze({});
428
+ }
429
+ }
430
+ /** Safe defaults when no operation/span start was observed; never infer identity. */
431
+ snapshotExplicit(properties, eventId) {
432
+ try {
433
+ const base = { ...this.explicit };
434
+ const operation = eventId ? this.operations.get(eventId) : void 0;
435
+ if (operation) {
436
+ for (const key of operation.explicitKeys) base[key] = operation.snapshot[key];
437
+ }
438
+ return this.mergeSnapshot(base, properties, new Set(Object.keys(base)));
439
+ } catch (e) {
440
+ return mergeAppGitSnapshot({}, properties);
441
+ }
442
+ }
443
+ /** Existing replay branch reporting stays separate from opt-in event properties. */
444
+ snapshotForReplay() {
445
+ const snapshot = this.snapshot();
446
+ if (this.replayBranch && !own(snapshot, APP_BRANCH) && snapshot[APP_COMMIT_SHA] === this.defaults[APP_COMMIT_SHA]) {
447
+ return Object.freeze({ ...snapshot, [APP_BRANCH]: this.replayBranch });
448
+ }
449
+ return snapshot;
450
+ }
451
+ /** Merge a frozen span snapshot without losing client-authored provenance. */
452
+ mergeSnapshot(base, properties, explicitKeys = this.explicitKeys) {
453
+ return mergeAppGitSnapshot(base, properties, /* @__PURE__ */ new Set([...this.explicitKeys, ...explicitKeys]));
454
+ }
455
+ /** Shippers belonging to one client share this bounded operation registry. */
456
+ forOperation(eventId, properties, owner) {
457
+ if (!eventId) return this.snapshot(properties);
458
+ let operation = this.operations.get(eventId);
459
+ if (!operation) {
460
+ if (this.operations.size >= 1e4) this.operationOverflow = true;
461
+ if (this.operationOverflow) return this.mergeSnapshot({}, properties);
462
+ operation = {
463
+ snapshot: this.snapshot(properties),
464
+ owners: /* @__PURE__ */ new Set(),
465
+ unowned: owner === void 0,
466
+ explicitKeys: appGitExplicitKeys(properties)
467
+ };
468
+ this.operations.set(eventId, operation);
469
+ } else if (properties && APP_GIT_KEYS.some((key) => own(properties, key))) {
470
+ const explicitKeys = /* @__PURE__ */ new Set([...this.explicitKeys, ...operation.explicitKeys]);
471
+ operation.snapshot = this.mergeSnapshot(operation.snapshot, properties, explicitKeys);
472
+ for (const key of appGitExplicitKeys(properties)) operation.explicitKeys.add(key);
473
+ }
474
+ if (!owner) operation.unowned = true;
475
+ if (owner) operation.owners.add(owner);
476
+ return operation.snapshot;
477
+ }
478
+ releaseOperation(eventId, owner) {
479
+ const operation = this.operations.get(eventId);
480
+ if (!operation) return;
481
+ operation.owners.delete(owner);
482
+ if (operation.owners.size === 0 && !operation.unowned) this.operations.delete(eventId);
483
+ }
484
+ finishOperation(eventId) {
485
+ this.operations.delete(eventId);
486
+ }
487
+ dispose() {
488
+ var _a;
489
+ this.disposed = true;
490
+ try {
491
+ (_a = this.cancel) == null ? void 0 : _a.call(this);
492
+ } catch (e) {
493
+ }
494
+ }
495
+ };
496
+ var AppGitContext = AppGitContextImpl;
497
+ function createAppGitContext(options, runtime = {}) {
498
+ return new AppGitContext(options, runtime);
499
+ }
500
+ function withAppGitProperties(properties, snapshot) {
501
+ try {
502
+ const merged = { ...properties };
503
+ for (const key of APP_GIT_KEYS)
504
+ if (!own(merged, key) && own(snapshot, key)) merged[key] = snapshot[key];
505
+ return merged;
506
+ } catch (e) {
507
+ return properties != null ? properties : {};
508
+ }
509
+ }
510
+ function withAppGitAttributes(attributes, snapshot) {
511
+ try {
512
+ const present = new Set(attributes.flatMap((attr) => attr ? [attr.key] : []));
513
+ const added = [];
514
+ for (const key of APP_GIT_KEYS) {
515
+ if (present.has(key) || present.has(`ai.telemetry.metadata.${key}`) || present.has(`traceloop.association.properties.${key}`))
516
+ continue;
517
+ const value = snapshot[key];
518
+ if (typeof value === "string") added.push({ key, value: { stringValue: value } });
519
+ else if (typeof value === "boolean") added.push({ key, value: { boolValue: value } });
520
+ }
521
+ return added.length ? [...attributes, ...added] : attributes;
522
+ } catch (e) {
523
+ return attributes;
524
+ }
525
+ }
526
+ function appGitPropertiesFromAttributes(attributes) {
527
+ var _a, _b, _c;
528
+ const properties = {};
529
+ try {
530
+ for (const attr of attributes) {
531
+ if (!attr) continue;
532
+ for (const key of APP_GIT_KEYS) {
533
+ if (attr.key === key || attr.key === `ai.telemetry.metadata.${key}` || attr.key === `traceloop.association.properties.${key}`) {
534
+ properties[key] = (_c = (_b = (_a = attr.value.stringValue) != null ? _a : attr.value.boolValue) != null ? _b : attr.value.intValue) != null ? _c : null;
535
+ }
536
+ }
537
+ }
538
+ } catch (e) {
539
+ }
540
+ return properties;
541
+ }
28
542
  function getCrypto() {
29
543
  const c = globalThis.crypto;
30
544
  return c;
@@ -79,6 +593,25 @@ function base64Encode(bytes) {
79
593
  }
80
594
  return out;
81
595
  }
596
+ function base64ToHex(value) {
597
+ if (!value) return "";
598
+ const maybeBuffer = globalThis.Buffer;
599
+ if (maybeBuffer) {
600
+ return maybeBuffer.from(value, "base64").toString("hex");
601
+ }
602
+ const atobFn = globalThis.atob;
603
+ if (typeof atobFn !== "function") return "";
604
+ try {
605
+ const binary = atobFn(value);
606
+ let hex = "";
607
+ for (let i = 0; i < binary.length; i++) {
608
+ hex += (binary.charCodeAt(i) & 255).toString(16).padStart(2, "0");
609
+ }
610
+ return hex;
611
+ } catch (e) {
612
+ return "";
613
+ }
614
+ }
82
615
  function generateId() {
83
616
  return randomUUID();
84
617
  }
@@ -113,7 +646,7 @@ function runWithTracingSuppressed(fn) {
113
646
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
114
647
  var MAX_RETRY_DELAY_MS = 3e4;
115
648
  function wait(ms) {
116
- return new Promise((resolve) => setTimeout(resolve, ms));
649
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
117
650
  }
118
651
  function formatEndpoint(endpoint) {
119
652
  if (!endpoint) return void 0;
@@ -150,9 +683,9 @@ async function raceWithTimeout(promise, timeoutMs) {
150
683
  () => true,
151
684
  () => true
152
685
  ),
153
- new Promise((resolve) => {
686
+ new Promise((resolve2) => {
154
687
  var _a;
155
- timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
688
+ timer = setTimeout(() => resolve2(false), Math.max(0, timeoutMs));
156
689
  (_a = timer.unref) == null ? void 0 : _a.call(timer);
157
690
  })
158
691
  ]);
@@ -223,6 +756,7 @@ async function postJson(url, body, headers, opts) {
223
756
  const resp = await runWithTracingSuppressed(
224
757
  () => fetch(url, {
225
758
  method: "POST",
759
+ redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
226
760
  headers: {
227
761
  "Content-Type": "application/json",
228
762
  ...headers
@@ -293,6 +827,11 @@ function attrInt(key, value) {
293
827
  if (!Number.isFinite(value)) return void 0;
294
828
  return { key, value: { intValue: String(Math.trunc(value)) } };
295
829
  }
830
+ function attrDouble(key, value) {
831
+ if (value === void 0) return void 0;
832
+ if (!Number.isFinite(value)) return void 0;
833
+ return { key, value: { doubleValue: value } };
834
+ }
296
835
  function buildOtlpSpan(args) {
297
836
  const attrs = args.attributes.filter((x) => x !== void 0);
298
837
  const span = {
@@ -386,26 +925,29 @@ function resolveLocalDebuggerBaseUrl(baseUrl) {
386
925
  if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
387
926
  return null;
388
927
  }
928
+ var LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS = 2e3;
389
929
  function mirrorTraceExportToLocalDebugger(body, options = {}) {
390
930
  var _a;
391
931
  const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
392
- if (!baseUrl) return;
393
- void postJson(`${baseUrl}traces`, body, {}, {
932
+ if (!baseUrl) return Promise.resolve();
933
+ return postJson(`${baseUrl}traces`, body, {}, {
394
934
  maxAttempts: 1,
395
935
  debug: (_a = options.debug) != null ? _a : false,
396
- sdkName: options.sdkName
936
+ sdkName: options.sdkName,
937
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
397
938
  }).catch(() => {
398
939
  });
399
940
  }
400
941
  function mirrorPartialEventToLocalDebugger(event, options = {}) {
401
942
  var _a;
402
943
  const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
403
- if (!baseUrl) return;
944
+ if (!baseUrl) return Promise.resolve();
404
945
  const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
405
- void postJson(`${baseUrl}events/track_partial`, event, headers, {
946
+ return postJson(`${baseUrl}events/track_partial`, event, headers, {
406
947
  maxAttempts: 1,
407
948
  debug: (_a = options.debug) != null ? _a : false,
408
- sdkName: options.sdkName
949
+ sdkName: options.sdkName,
950
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
409
951
  }).catch(() => {
410
952
  });
411
953
  }
@@ -430,6 +972,13 @@ function projectIdHeaders(projectId) {
430
972
  }
431
973
  var SHUTDOWN_DEADLINE_MS = 1e4;
432
974
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
975
+ function appGitOwnerId(prefix) {
976
+ try {
977
+ return `${prefix}:${randomUUID()}`;
978
+ } catch (e) {
979
+ return `${prefix}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
980
+ }
981
+ }
433
982
  function mergePatches(target, source) {
434
983
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
435
984
  const out = { ...target, ...source };
@@ -479,22 +1028,25 @@ function eventTelemetryProperties(patch) {
479
1028
  }
480
1029
  var EventShipper = class {
481
1030
  constructor(opts) {
1031
+ this.appGitOwner = appGitOwnerId("event");
482
1032
  this.buffers = /* @__PURE__ */ new Map();
483
1033
  this.sticky = /* @__PURE__ */ new Map();
484
1034
  this.timers = /* @__PURE__ */ new Map();
485
1035
  this.inFlight = /* @__PURE__ */ new Set();
486
1036
  this.hasShutdown = false;
487
- var _a, _b, _c, _d, _e, _f, _g, _h;
488
- this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
489
- this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
1037
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1038
+ this.appGitContext = (_a = opts.appGitContext) != null ? _a : createAppGitContext(opts.appGit);
1039
+ this.ownsAppGitContext = opts.appGitContext === void 0;
1040
+ this.writeKey = (_b = opts.writeKey) == null ? void 0 : _b.trim();
1041
+ this.baseUrl = (_c = formatEndpoint(opts.endpoint)) != null ? _c : "https://api.raindrop.ai/v1/";
490
1042
  this.enabled = opts.enabled !== false;
491
1043
  this.debug = opts.debug;
492
- this.partialFlushMs = (_c = opts.partialFlushMs) != null ? _c : 1e3;
493
- this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
1044
+ this.partialFlushMs = (_d = opts.partialFlushMs) != null ? _d : 1e3;
1045
+ this.sdkName = (_e = opts.sdkName) != null ? _e : "core";
494
1046
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
495
- this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
1047
+ this.defaultEventName = (_f = opts.defaultEventName) != null ? _f : "ai_generation";
496
1048
  this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
497
- this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
1049
+ this.localDebuggerUrl = (_g = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _g : void 0;
498
1050
  if (this.debug && this.localDebuggerUrl) {
499
1051
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
500
1052
  }
@@ -505,8 +1057,8 @@ var EventShipper = class {
505
1057
  const isNode = typeof process !== "undefined" && typeof process.version === "string";
506
1058
  this.context = {
507
1059
  library: {
508
- name: (_g = opts.libraryName) != null ? _g : "@raindrop-ai/core",
509
- version: (_h = opts.libraryVersion) != null ? _h : "0.0.0"
1060
+ name: (_h = opts.libraryName) != null ? _h : "@raindrop-ai/core",
1061
+ version: (_i = opts.libraryVersion) != null ? _i : "0.0.0"
510
1062
  },
511
1063
  metadata: {
512
1064
  jsRuntime: isNode ? "node" : "web",
@@ -514,6 +1066,9 @@ var EventShipper = class {
514
1066
  }
515
1067
  };
516
1068
  }
1069
+ ownAppGitContextForShutdown() {
1070
+ this.ownsAppGitContext = true;
1071
+ }
517
1072
  isDebugEnabled() {
518
1073
  return this.debug;
519
1074
  }
@@ -562,6 +1117,15 @@ var EventShipper = class {
562
1117
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
563
1118
  if (!this.enabled) return;
564
1119
  if (!eventId || !eventId.trim()) return;
1120
+ const capturedSticky = this.sticky.get(eventId);
1121
+ const eventAlreadyKnown = capturedSticky !== void 0 || this.buffers.has(eventId);
1122
+ const destination = currentReplayTraceDestination();
1123
+ const cloudReplay = destination !== void 0 && destination.kind !== "workshop";
1124
+ if ((capturedSticky == null ? void 0 : capturedSticky.replayOnly) === true || !eventAlreadyKnown && cloudReplay) {
1125
+ if (patch.isPending === false) this.sticky.delete(eventId);
1126
+ else this.sticky.set(eventId, { ...capturedSticky, replayOnly: true });
1127
+ return;
1128
+ }
565
1129
  const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
566
1130
  if (typeof patch.input === "string" && patch.input.length > maxChars) {
567
1131
  patch = { ...patch, input: capText(patch.input, maxChars) };
@@ -583,6 +1147,13 @@ var EventShipper = class {
583
1147
  }
584
1148
  const sticky = (_c = this.sticky.get(eventId)) != null ? _c : {};
585
1149
  const existing = (_d = this.buffers.get(eventId)) != null ? _d : {};
1150
+ patch = {
1151
+ ...patch,
1152
+ properties: withAppGitProperties(
1153
+ patch.properties,
1154
+ this.appGitContext.forOperation(eventId, patch.properties, this.appGitOwner)
1155
+ )
1156
+ };
586
1157
  const merged = mergePatches(existing, patch);
587
1158
  merged.isPending = (_g = (_f = (_e = patch.isPending) != null ? _e : existing.isPending) != null ? _f : sticky.isPending) != null ? _g : true;
588
1159
  this.buffers.set(eventId, merged);
@@ -596,6 +1167,8 @@ var EventShipper = class {
596
1167
  if (t) clearTimeout(t);
597
1168
  if (merged.isPending === false) {
598
1169
  await this.flushOne(eventId);
1170
+ if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(eventId, this.appGitOwner);
1171
+ else this.appGitContext.finishOperation(eventId);
599
1172
  return;
600
1173
  }
601
1174
  const timeout = setTimeout(() => {
@@ -620,15 +1193,21 @@ var EventShipper = class {
620
1193
  try {
621
1194
  for (const t of this.timers.values()) clearTimeout(t);
622
1195
  this.timers.clear();
1196
+ for (const [eventId, sticky] of this.sticky) {
1197
+ if (sticky.replayOnly) this.sticky.delete(eventId);
1198
+ }
623
1199
  const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
624
1200
  if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
625
1201
  } finally {
626
1202
  this.shutdownDeadlineAt = void 0;
1203
+ if (this.ownsAppGitContext) this.appGitContext.dispose();
627
1204
  }
628
1205
  }
629
1206
  async trackSignal(signal) {
630
1207
  var _a, _b;
631
1208
  if (!this.enabled) return;
1209
+ const destination = currentReplayTraceDestination();
1210
+ if (destination && destination.kind !== "workshop") return;
632
1211
  const body = [
633
1212
  {
634
1213
  event_id: signal.eventId,
@@ -663,6 +1242,8 @@ var EventShipper = class {
663
1242
  }
664
1243
  async identify(users) {
665
1244
  if (!this.enabled) return;
1245
+ const destination = currentReplayTraceDestination();
1246
+ if (destination && destination.kind !== "workshop") return;
666
1247
  const list = Array.isArray(users) ? users : [users];
667
1248
  const body = list.filter((user) => {
668
1249
  if (!(user == null ? void 0 : user.userId) || !user.userId.trim()) {
@@ -715,8 +1296,9 @@ var EventShipper = class {
715
1296
  this.buffers.delete(eventId);
716
1297
  if (!accumulated) return;
717
1298
  const sticky = (_a = this.sticky.get(eventId)) != null ? _a : {};
718
- const eventName = (_c = (_b = accumulated.eventName) != null ? _b : sticky.eventName) != null ? _c : this.defaultEventName;
719
- const userId = (_d = accumulated.userId) != null ? _d : sticky.userId;
1299
+ const isPending = (_c = (_b = accumulated.isPending) != null ? _b : sticky.isPending) != null ? _c : true;
1300
+ const eventName = (_e = (_d = accumulated.eventName) != null ? _d : sticky.eventName) != null ? _e : this.defaultEventName;
1301
+ const userId = (_f = accumulated.userId) != null ? _f : sticky.userId;
720
1302
  if (!userId) {
721
1303
  if (this.debug) {
722
1304
  console.warn(`${this.prefix} skipping track_partial for ${eventId}: missing userId`);
@@ -724,14 +1306,13 @@ var EventShipper = class {
724
1306
  this.sticky.delete(eventId);
725
1307
  return;
726
1308
  }
727
- const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
1309
+ const { wizardSession, ...restProperties } = (_g = accumulated.properties) != null ? _g : {};
728
1310
  let telemetryProperties = {};
729
1311
  try {
730
1312
  telemetryProperties = eventTelemetryProperties(accumulated);
731
1313
  } catch (e) {
732
1314
  }
733
- const convoId = (_f = accumulated.convoId) != null ? _f : sticky.convoId;
734
- const isPending = (_h = (_g = accumulated.isPending) != null ? _g : sticky.isPending) != null ? _h : true;
1315
+ const convoId = (_h = accumulated.convoId) != null ? _h : sticky.convoId;
735
1316
  const payload = {
736
1317
  event_id: eventId,
737
1318
  user_id: userId,
@@ -773,29 +1354,30 @@ var EventShipper = class {
773
1354
  endpoint: url
774
1355
  });
775
1356
  }
776
- if (this.localDebuggerUrl) {
777
- mirrorPartialEventToLocalDebugger(payload, {
778
- baseUrl: this.localDebuggerUrl,
779
- writeKey: this.writeKey,
780
- debug: this.debug,
781
- sdkName: this.sdkName
782
- });
783
- }
1357
+ const mirror = this.localDebuggerUrl ? mirrorPartialEventToLocalDebugger(payload, {
1358
+ baseUrl: this.localDebuggerUrl,
1359
+ writeKey: this.writeKey,
1360
+ debug: this.debug,
1361
+ sdkName: this.sdkName
1362
+ }) : void 0;
784
1363
  if (!this.writeKey) {
785
1364
  if (!isPending) this.sticky.delete(eventId);
1365
+ if (mirror) await mirror;
786
1366
  return;
787
1367
  }
788
1368
  const opts = this.requestOpts();
789
1369
  if (!opts) {
790
1370
  this.warnShutdownDrop(`track_partial ${eventId}`);
791
1371
  if (!isPending) this.sticky.delete(eventId);
1372
+ if (mirror) await mirror;
792
1373
  return;
793
1374
  }
794
1375
  const p = postJson(url, payload, this.requestHeaders(), opts);
795
- this.inFlight.add(p);
1376
+ const combined = mirror ? Promise.all([mirror, p]).then(() => void 0) : p;
1377
+ this.inFlight.add(combined);
796
1378
  try {
797
1379
  try {
798
- await p;
1380
+ await combined;
799
1381
  if (this.debug) {
800
1382
  console.log(`${this.prefix} sent track_partial ${eventId} (${eventName})`);
801
1383
  }
@@ -807,7 +1389,7 @@ var EventShipper = class {
807
1389
  );
808
1390
  }
809
1391
  } finally {
810
- this.inFlight.delete(p);
1392
+ this.inFlight.delete(combined);
811
1393
  }
812
1394
  if (!isPending) {
813
1395
  this.sticky.delete(eventId);
@@ -815,6 +1397,7 @@ var EventShipper = class {
815
1397
  }
816
1398
  };
817
1399
  var MODEL_USAGE_ATTRIBUTES = {
1400
+ usageSource: "raindrop.usage.source",
818
1401
  providerName: "gen_ai.provider.name",
819
1402
  requestModel: "gen_ai.request.model",
820
1403
  responseModel: "gen_ai.response.model",
@@ -823,6 +1406,7 @@ var MODEL_USAGE_ATTRIBUTES = {
823
1406
  reasoningTokens: "gen_ai.usage.reasoning_tokens",
824
1407
  cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
825
1408
  cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
1409
+ providerReportedCost: "gen_ai.usage.provider_reported_cost",
826
1410
  nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
827
1411
  nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
828
1412
  totalOutputTokens: "raindrop.usage.output_tokens.total",
@@ -831,13 +1415,20 @@ var MODEL_USAGE_ATTRIBUTES = {
831
1415
  cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
832
1416
  };
833
1417
  var MODEL_PROVIDER_NAMES = [
1418
+ ["google.vertex.anthropic", "google-vertex"],
1419
+ ["google.vertex_ai", "google-vertex"],
834
1420
  ["google.vertex", "google-vertex"],
1421
+ ["gcp.vertex_ai", "google-vertex"],
1422
+ ["gcp.gemini", "google"],
1423
+ ["google_vertexai", "google-vertex"],
835
1424
  ["vertex.anthropic", "google-vertex"],
836
1425
  ["amazon-bedrock", "amazon-bedrock"],
1426
+ ["aws.bedrock", "amazon-bedrock"],
837
1427
  ["bedrock", "amazon-bedrock"],
838
1428
  ["anthropic", "anthropic"],
839
1429
  ["openai", "openai"],
840
1430
  ["google", "google"],
1431
+ ["az.ai.openai", "azure"],
841
1432
  ["azure", "azure"],
842
1433
  ["openrouter", "openrouter"]
843
1434
  ];
@@ -848,60 +1439,20 @@ function canonicalModelProvider(provider) {
848
1439
  const match = MODEL_PROVIDER_NAMES.find(
849
1440
  ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
850
1441
  );
851
- return (_a = match == null ? void 0 : match[1]) != null ? _a : provider;
1442
+ return (_a = match == null ? void 0 : match[1]) != null ? _a : normalizedProvider;
852
1443
  }
853
1444
  function tokenCount(value) {
854
1445
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
855
1446
  }
856
- function exclusiveTokens({
857
- explicit,
858
- total,
859
- parts,
860
- semantics
861
- }) {
862
- if (explicit !== void 0) return explicit;
863
- if (total === void 0) return void 0;
864
- if (semantics === "exclusive") return total;
865
- const observedParts = parts.filter((part) => part !== void 0 && part > 0);
866
- if (semantics === void 0 && observedParts.length > 0) return void 0;
867
- const exclusive = total - observedParts.reduce((sum, part) => sum + part, 0);
868
- return exclusive >= 0 ? exclusive : void 0;
869
- }
870
- function buildModelUsageAttributes(usage) {
871
- const inputTokens = tokenCount(usage.inputTokens);
872
- const outputTokens = tokenCount(usage.outputTokens);
873
- const explicitNonCachedInputTokens = tokenCount(usage.nonCachedInputTokens);
874
- const explicitNonReasoningOutputTokens = tokenCount(usage.nonReasoningOutputTokens);
875
- const reasoningTokens = tokenCount(usage.reasoningTokens);
876
- const cacheReadInputTokens = tokenCount(usage.cacheReadInputTokens);
877
- const cacheWriteInputTokens = tokenCount(usage.cacheWriteInputTokens);
878
- const nonCachedInputTokens = exclusiveTokens({
879
- explicit: explicitNonCachedInputTokens,
880
- total: inputTokens,
881
- parts: [cacheReadInputTokens, cacheWriteInputTokens],
882
- semantics: usage.inputTokenSemantics
883
- });
884
- const nonReasoningOutputTokens = exclusiveTokens({
885
- explicit: explicitNonReasoningOutputTokens,
886
- total: outputTokens,
887
- parts: [reasoningTokens],
888
- semantics: usage.outputTokenSemantics
889
- });
890
- const exclusivePoolAttributes = nonCachedInputTokens !== void 0 && nonReasoningOutputTokens !== void 0 ? [
891
- // backend consumes this complete raindrop.* set as one atomic billing-pool contract.
892
- attrInt(MODEL_USAGE_ATTRIBUTES.nonCachedInputTokens, nonCachedInputTokens),
893
- attrInt(MODEL_USAGE_ATTRIBUTES.nonReasoningOutputTokens, nonReasoningOutputTokens),
894
- attrInt(MODEL_USAGE_ATTRIBUTES.reasoningOutputTokens, reasoningTokens),
895
- attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadTokens, cacheReadInputTokens),
896
- attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteTokens, cacheWriteInputTokens)
897
- ] : [];
1447
+ function buildRawModelUsageAttributes(usage) {
1448
+ const providerReportedCost = typeof usage.providerReportedCost === "number" && Number.isFinite(usage.providerReportedCost) && usage.providerReportedCost >= 0 ? usage.providerReportedCost : void 0;
898
1449
  return [
899
- attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, inputTokens),
900
- attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, outputTokens),
901
- attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, reasoningTokens),
902
- attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, cacheReadInputTokens),
903
- attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, cacheWriteInputTokens),
904
- ...exclusivePoolAttributes
1450
+ attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, tokenCount(usage.inputTokens)),
1451
+ attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, tokenCount(usage.outputTokens)),
1452
+ attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, tokenCount(usage.reasoningTokens)),
1453
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, tokenCount(usage.cacheReadInputTokens)),
1454
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, tokenCount(usage.cacheWriteInputTokens)),
1455
+ attrDouble(MODEL_USAGE_ATTRIBUTES.providerReportedCost, providerReportedCost)
905
1456
  ].filter((attribute) => attribute !== void 0);
906
1457
  }
907
1458
  var STRING_ALIASES = {
@@ -1091,6 +1642,13 @@ function redactJsonAttributeValue(key, value) {
1091
1642
  if (scrubbedJson === json) return void 0;
1092
1643
  return { stringValue: scrubbedJson };
1093
1644
  }
1645
+ function appGitOwnerId2(prefix) {
1646
+ try {
1647
+ return `${prefix}:${randomUUID()}`;
1648
+ } catch (e) {
1649
+ return `${prefix}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
1650
+ }
1651
+ }
1094
1652
  function identityAttrs(args) {
1095
1653
  return [
1096
1654
  attrString("ai.telemetry.metadata.raindrop.ai.userId", args.userId),
@@ -1113,23 +1671,30 @@ function applyOtelSpanAttributeLimit(limit) {
1113
1671
  }
1114
1672
  var TraceShipper = class {
1115
1673
  constructor(opts) {
1674
+ this.appGitOwner = appGitOwnerId2("trace");
1675
+ this.appGitEventIds = /* @__PURE__ */ new WeakMap();
1676
+ this.appGitActiveSpans = /* @__PURE__ */ new Map();
1677
+ this.appGitCaptured = /* @__PURE__ */ new WeakSet();
1678
+ this.appGitAdded = /* @__PURE__ */ new WeakMap();
1116
1679
  this.queue = [];
1117
1680
  this.inFlight = /* @__PURE__ */ new Set();
1118
1681
  this.hasShutdown = false;
1119
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1120
- this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
1121
- this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
1682
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1683
+ this.appGitContext = (_a = opts.appGitContext) != null ? _a : createAppGitContext(opts.appGit);
1684
+ this.ownsAppGitContext = opts.appGitContext === void 0;
1685
+ this.writeKey = (_b = opts.writeKey) == null ? void 0 : _b.trim();
1686
+ this.baseUrl = (_c = formatEndpoint(opts.endpoint)) != null ? _c : "https://api.raindrop.ai/v1/";
1122
1687
  this.enabled = opts.enabled !== false;
1123
1688
  this.debug = opts.debug;
1124
1689
  this.debugSpans = opts.debugSpans === true;
1125
- this.flushIntervalMs = (_c = opts.flushIntervalMs) != null ? _c : 1e3;
1126
- this.maxBatchSize = (_d = opts.maxBatchSize) != null ? _d : 50;
1127
- this.maxQueueSize = (_e = opts.maxQueueSize) != null ? _e : 5e3;
1128
- this.sdkName = (_f = opts.sdkName) != null ? _f : "core";
1690
+ this.flushIntervalMs = (_d = opts.flushIntervalMs) != null ? _d : 1e3;
1691
+ this.maxBatchSize = (_e = opts.maxBatchSize) != null ? _e : 50;
1692
+ this.maxQueueSize = (_f = opts.maxQueueSize) != null ? _f : 5e3;
1693
+ this.sdkName = (_g = opts.sdkName) != null ? _g : "core";
1129
1694
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
1130
- this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
1131
- this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
1132
- this.localDebuggerUrl = (_i = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _i : void 0;
1695
+ this.serviceName = (_h = opts.serviceName) != null ? _h : "raindrop.core";
1696
+ this.serviceVersion = (_i = opts.serviceVersion) != null ? _i : "0.0.0";
1697
+ this.localDebuggerUrl = (_j = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _j : void 0;
1133
1698
  if (this.debug && this.localDebuggerUrl) {
1134
1699
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
1135
1700
  }
@@ -1193,7 +1758,10 @@ var TraceShipper = class {
1193
1758
  try {
1194
1759
  const result = this.transformSpanHook(current);
1195
1760
  if (result === null) return null;
1196
- if (result !== void 0) current = result;
1761
+ if (result !== void 0) {
1762
+ copyReplayTraceDestination(current, result);
1763
+ current = result;
1764
+ }
1197
1765
  } catch (err) {
1198
1766
  if (this.debug) {
1199
1767
  const msg = err instanceof Error ? err.message : String(err);
@@ -1203,13 +1771,19 @@ var TraceShipper = class {
1203
1771
  }
1204
1772
  }
1205
1773
  try {
1206
- current = normalizeModelUsageSpan(current);
1774
+ const normalized = normalizeModelUsageSpan(current);
1775
+ copyReplayTraceDestination(current, normalized);
1776
+ current = normalized;
1207
1777
  } catch (e) {
1208
1778
  }
1209
1779
  if (!this.disableDefaultRedaction) {
1210
- current = defaultTransformSpan(current);
1780
+ const redacted = defaultTransformSpan(current);
1781
+ copyReplayTraceDestination(current, redacted);
1782
+ current = redacted;
1211
1783
  }
1212
- return this.capSpanAttributes(current);
1784
+ const capped = this.capSpanAttributes(current);
1785
+ copyReplayTraceDestination(current, capped);
1786
+ return capped;
1213
1787
  }
1214
1788
  isDebugEnabled() {
1215
1789
  return this.debug;
@@ -1221,46 +1795,84 @@ var TraceShipper = class {
1221
1795
  return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
1222
1796
  }
1223
1797
  startSpan(args) {
1224
- var _a, _b;
1798
+ var _a, _b, _c;
1225
1799
  const ids = createSpanIds(args.parent);
1226
1800
  const started = (_a = args.startTimeUnixNano) != null ? _a : nowUnixNanoString();
1227
1801
  const attrs = [
1228
1802
  attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
1229
1803
  ...identityAttrs(args),
1230
- attrString("ai.operationId", args.operationId)
1804
+ attrString("ai.operationId", args.operationId),
1805
+ ...Object.entries(evalScopeAttributes()).map(([key, value]) => attrString(key, value))
1231
1806
  ];
1232
1807
  if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
1233
- const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
1234
- this.mirrorToLocalDebugger(
1235
- buildOtlpSpan({
1236
- ids: span.ids,
1237
- name: span.name,
1238
- startTimeUnixNano: span.startTimeUnixNano,
1239
- endTimeUnixNano: span.startTimeUnixNano,
1240
- // placeholder — will be updated on endSpan
1241
- attributes: span.attributes,
1242
- status: { code: SpanStatusCode.UNSET }
1243
- })
1244
- );
1808
+ const tracksAppGit = this.appGitActiveSpans.has(args.eventId) || this.appGitActiveSpans.size < 1e4;
1809
+ const appGitOverrides = appGitPropertiesFromAttributes(attrs);
1810
+ const span = {
1811
+ ids,
1812
+ name: args.name,
1813
+ startTimeUnixNano: started,
1814
+ attributes: withAppGitAttributes(
1815
+ attrs,
1816
+ this.appGitContext.forOperation(args.eventId, appGitOverrides, this.appGitOwner)
1817
+ )
1818
+ };
1819
+ rememberReplayTraceDestination(span);
1820
+ this.appGitCaptured.add(span);
1821
+ this.appGitEventIds.set(span, args.eventId);
1822
+ if (tracksAppGit) {
1823
+ this.appGitActiveSpans.set(args.eventId, ((_c = this.appGitActiveSpans.get(args.eventId)) != null ? _c : 0) + 1);
1824
+ }
1825
+ this.appGitAdded.set(span, new Set(span.attributes.filter((attr) => !attrs.includes(attr))));
1826
+ const openSpan = buildOtlpSpan({
1827
+ ids: span.ids,
1828
+ name: span.name,
1829
+ startTimeUnixNano: span.startTimeUnixNano,
1830
+ endTimeUnixNano: span.startTimeUnixNano,
1831
+ // placeholder — will be updated on endSpan
1832
+ attributes: span.attributes,
1833
+ status: { code: SpanStatusCode.UNSET }
1834
+ });
1835
+ copyReplayTraceDestination(span, openSpan);
1836
+ this.mirrorToLocalDebugger(openSpan);
1245
1837
  return span;
1246
1838
  }
1247
- mirrorToLocalDebugger(span) {
1248
- if (!this.localDebuggerUrl) return;
1839
+ mirrorToLocalDebugger(span, completed = false) {
1840
+ const destination = replayTraceDestinationFor(span);
1841
+ if (destination && destination.kind !== "workshop") return;
1842
+ const localDebuggerUrl = (destination == null ? void 0 : destination.kind) === "workshop" ? destination.url : this.localDebuggerUrl;
1843
+ if (!localDebuggerUrl) return;
1249
1844
  const redacted = this.redactSpan(span);
1250
1845
  if (redacted === null) return;
1846
+ if (completed && (destination == null ? void 0 : destination.kind) === "workshop") {
1847
+ recordReplayExportedSpan(
1848
+ destination,
1849
+ base64ToHex(redacted.spanId),
1850
+ base64ToHex(redacted.traceId)
1851
+ );
1852
+ }
1251
1853
  const body = buildExportTraceServiceRequest([redacted], this.serviceName, this.serviceVersion);
1252
- mirrorTraceExportToLocalDebugger(body, {
1253
- baseUrl: this.localDebuggerUrl,
1854
+ const p = mirrorTraceExportToLocalDebugger(body, {
1855
+ baseUrl: localDebuggerUrl,
1254
1856
  debug: false,
1255
1857
  sdkName: this.sdkName
1256
1858
  });
1859
+ this.inFlight.add(p);
1860
+ void p.finally(() => {
1861
+ this.inFlight.delete(p);
1862
+ });
1257
1863
  }
1258
1864
  endSpan(span, extra) {
1259
- var _a, _b;
1865
+ var _a, _b, _c;
1260
1866
  if (span.endTimeUnixNano) return;
1261
1867
  span.endTimeUnixNano = (_a = extra == null ? void 0 : extra.endTimeUnixNano) != null ? _a : nowUnixNanoString();
1262
1868
  if ((_b = extra == null ? void 0 : extra.attributes) == null ? void 0 : _b.length) {
1869
+ const overrides = appGitPropertiesFromAttributes(extra.attributes);
1870
+ const eventId2 = this.appGitEventIds.get(span);
1871
+ const snapshot = eventId2 ? this.appGitContext.forOperation(eventId2, overrides, this.appGitOwner) : overrides;
1872
+ const added = this.appGitAdded.get(span);
1873
+ 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));
1263
1874
  span.attributes.push(...extra.attributes);
1875
+ span.attributes = withAppGitAttributes(span.attributes, snapshot);
1264
1876
  }
1265
1877
  let status = extra == null ? void 0 : extra.status;
1266
1878
  if (!status && (extra == null ? void 0 : extra.error) !== void 0) {
@@ -1275,30 +1887,65 @@ var TraceShipper = class {
1275
1887
  attributes: span.attributes,
1276
1888
  status
1277
1889
  });
1890
+ copyReplayTraceDestination(span, otlp);
1891
+ this.appGitCaptured.add(otlp);
1278
1892
  this.enqueue(otlp);
1279
- this.mirrorToLocalDebugger(otlp);
1893
+ this.mirrorToLocalDebugger(otlp, true);
1894
+ const eventId = this.appGitEventIds.get(span);
1895
+ if (eventId && this.appGitActiveSpans.has(eventId)) {
1896
+ const remaining = ((_c = this.appGitActiveSpans.get(eventId)) != null ? _c : 1) - 1;
1897
+ if (remaining > 0) this.appGitActiveSpans.set(eventId, remaining);
1898
+ else {
1899
+ this.appGitActiveSpans.delete(eventId);
1900
+ if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(eventId, this.appGitOwner);
1901
+ else if (this.ownsAppGitContext) this.appGitContext.finishOperation(eventId);
1902
+ }
1903
+ }
1280
1904
  }
1281
1905
  createSpan(args) {
1282
1906
  var _a;
1283
1907
  const ids = createSpanIds(args.parent);
1284
1908
  const attrs = [
1285
1909
  attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
1286
- ...identityAttrs(args)
1910
+ ...identityAttrs(args),
1911
+ ...Object.entries(evalScopeAttributes()).map(([key, value]) => attrString(key, value))
1287
1912
  ];
1288
1913
  if ((_a = args.attributes) == null ? void 0 : _a.length) attrs.push(...args.attributes);
1914
+ const tracksAppGit = !this.appGitActiveSpans.has(args.eventId);
1289
1915
  const otlp = buildOtlpSpan({
1290
1916
  ids,
1291
1917
  name: args.name,
1292
1918
  startTimeUnixNano: args.startTimeUnixNano,
1293
1919
  endTimeUnixNano: args.endTimeUnixNano,
1294
- attributes: attrs,
1920
+ attributes: withAppGitAttributes(
1921
+ attrs,
1922
+ this.appGitContext.forOperation(
1923
+ args.eventId,
1924
+ appGitPropertiesFromAttributes(attrs),
1925
+ this.appGitOwner
1926
+ )
1927
+ ),
1295
1928
  status: args.status
1296
1929
  });
1930
+ rememberReplayTraceDestination(otlp);
1931
+ this.appGitCaptured.add(otlp);
1932
+ if (tracksAppGit) {
1933
+ if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(args.eventId, this.appGitOwner);
1934
+ else if (this.ownsAppGitContext) this.appGitContext.finishOperation(args.eventId);
1935
+ }
1297
1936
  this.enqueue(otlp);
1298
- this.mirrorToLocalDebugger(otlp);
1937
+ this.mirrorToLocalDebugger(otlp, true);
1299
1938
  }
1300
1939
  enqueue(span) {
1940
+ var _a;
1301
1941
  if (!this.enabled) return;
1942
+ const attributes = (_a = span.attributes) != null ? _a : [];
1943
+ const enriched = this.appGitCaptured.has(span) ? attributes : withAppGitAttributes(attributes, this.appGitContext.snapshot(appGitPropertiesFromAttributes(attributes)));
1944
+ if (enriched !== attributes) {
1945
+ const original = span;
1946
+ span = { ...span, attributes: enriched.filter((attr) => attr !== void 0) };
1947
+ copyReplayTraceDestination(original, span);
1948
+ }
1302
1949
  if (this.debugSpans) {
1303
1950
  const short = (s) => s ? s.slice(-8) : "none";
1304
1951
  console.log(
@@ -1309,6 +1956,13 @@ var TraceShipper = class {
1309
1956
  }
1310
1957
  const redacted = this.redactSpan(span);
1311
1958
  if (redacted === null) return;
1959
+ const destination = replayTraceDestinationFor(redacted);
1960
+ recordReplayExportedSpan(
1961
+ destination,
1962
+ base64ToHex(redacted.spanId),
1963
+ base64ToHex(redacted.traceId)
1964
+ );
1965
+ if ((destination == null ? void 0 : destination.kind) === "workshop") return;
1312
1966
  if (this.queue.length >= this.maxQueueSize) {
1313
1967
  this.queue.shift();
1314
1968
  }
@@ -1327,47 +1981,67 @@ var TraceShipper = class {
1327
1981
  }
1328
1982
  }
1329
1983
  async flush() {
1330
- if (!this.enabled) return;
1984
+ var _a;
1985
+ if (!this.enabled) {
1986
+ await Promise.all([...this.inFlight].map((p) => p.catch(() => {
1987
+ })));
1988
+ return;
1989
+ }
1331
1990
  if (this.timer) {
1332
1991
  clearTimeout(this.timer);
1333
1992
  this.timer = void 0;
1334
1993
  }
1335
1994
  while (this.queue.length > 0) {
1336
1995
  const batch = this.queue.splice(0, this.maxBatchSize);
1337
- if (!this.writeKey) continue;
1338
- const opts = this.requestOpts();
1339
- if (!opts) {
1340
- rateLimitedLog(
1341
- `${this.prefix}.shutdown_deadline`,
1342
- () => console.warn(
1343
- `${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
1344
- )
1345
- );
1346
- continue;
1347
- }
1348
- const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
1349
- const url = `${this.baseUrl}traces`;
1350
- if (this.debug) {
1351
- console.log(`${this.prefix} sending traces batch`, {
1352
- spans: batch.length,
1353
- endpoint: url
1354
- });
1996
+ const groups = /* @__PURE__ */ new Map();
1997
+ for (const span of batch) {
1998
+ const destination = replayTraceDestinationFor(span);
1999
+ const spans = groups.get(destination);
2000
+ if (spans) spans.push(span);
2001
+ else groups.set(destination, [span]);
1355
2002
  }
1356
- const p = postJson(url, body, this.requestHeaders(), opts);
1357
- this.inFlight.add(p);
1358
- try {
1359
- try {
1360
- await p;
1361
- if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
1362
- } catch (err) {
1363
- const msg = err instanceof Error ? err.message : String(err);
2003
+ for (const [destination, spans] of groups) {
2004
+ if (!destination && !this.writeKey) continue;
2005
+ if ((destination == null ? void 0 : destination.kind) === "workshop") continue;
2006
+ const opts = this.requestOpts();
2007
+ if (!opts) {
1364
2008
  rateLimitedLog(
1365
- `${this.prefix}.send_spans_failed`,
1366
- () => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
2009
+ `${this.prefix}.shutdown_deadline`,
2010
+ () => console.warn(
2011
+ `${this.prefix} shutdown flush deadline exceeded; dropping ${spans.length} spans`
2012
+ )
1367
2013
  );
2014
+ continue;
2015
+ }
2016
+ const body = buildExportTraceServiceRequest(spans, this.serviceName, this.serviceVersion);
2017
+ const url = destination ? replayTraceUrl(destination.url) : `${this.baseUrl}traces`;
2018
+ const headers = destination ? {
2019
+ Authorization: `Bearer ${destination.apiKey}`,
2020
+ "X-Raindrop-Replay-Id": destination.replayId,
2021
+ "X-Raindrop-Project-Id": (_a = destination.projectId) != null ? _a : "default"
2022
+ } : this.requestHeaders();
2023
+ if (this.debug) {
2024
+ console.log(`${this.prefix} sending traces batch`, {
2025
+ spans: spans.length,
2026
+ endpoint: url
2027
+ });
2028
+ }
2029
+ const p = postJson(url, body, headers, opts);
2030
+ this.inFlight.add(p);
2031
+ try {
2032
+ try {
2033
+ await p;
2034
+ if (this.debug) console.log(`${this.prefix} sent ${spans.length} spans`);
2035
+ } catch (err) {
2036
+ const msg = err instanceof Error ? err.message : String(err);
2037
+ rateLimitedLog(
2038
+ `${this.prefix}.send_spans_failed`,
2039
+ () => console.warn(`${this.prefix} failed to send ${spans.length} spans: ${msg}`)
2040
+ );
2041
+ }
2042
+ } finally {
2043
+ this.inFlight.delete(p);
1368
2044
  }
1369
- } finally {
1370
- this.inFlight.delete(p);
1371
2045
  }
1372
2046
  }
1373
2047
  }
@@ -1410,17 +2084,165 @@ var TraceShipper = class {
1410
2084
  if (!settled) {
1411
2085
  rateLimitedLog(
1412
2086
  `${this.prefix}.shutdown_deadline`,
1413
- () => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
2087
+ () => console.warn(
2088
+ `${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`
2089
+ )
1414
2090
  );
1415
2091
  }
1416
2092
  } finally {
1417
2093
  this.shutdownDeadlineAt = void 0;
2094
+ if (this.ownsAppGitContext) this.appGitContext.dispose();
1418
2095
  }
1419
2096
  }
1420
2097
  };
1421
2098
 
1422
2099
  // ../core/dist/index.node.js
1423
2100
  var import_async_hooks = require("async_hooks");
2101
+ var import_child_process = require("child_process");
2102
+ var import_path = require("path");
2103
+ var resolveLocal = (sourceDirectory, detectBranch, completed) => {
2104
+ const cwd = sourceDirectory ? (0, import_path.resolve)(sourceDirectory) : process.cwd();
2105
+ let cancelled = false;
2106
+ let active;
2107
+ let deadlineTimer;
2108
+ const deadline = Date.now() + 1e3;
2109
+ const run = (args, done) => {
2110
+ if (cancelled) return;
2111
+ const remaining = deadline - Date.now();
2112
+ if (remaining <= 0) {
2113
+ done(void 0);
2114
+ return;
2115
+ }
2116
+ try {
2117
+ active = (0, import_child_process.execFile)(
2118
+ "git",
2119
+ args,
2120
+ {
2121
+ cwd,
2122
+ encoding: "utf8",
2123
+ maxBuffer: 4096,
2124
+ windowsHide: true,
2125
+ env: {
2126
+ ...process.env,
2127
+ // The selected application cwd, not the invoking shell's Git plumbing, owns identity.
2128
+ GIT_DIR: void 0,
2129
+ GIT_WORK_TREE: void 0,
2130
+ GIT_COMMON_DIR: void 0,
2131
+ GIT_NAMESPACE: void 0,
2132
+ GIT_INDEX_FILE: void 0,
2133
+ GIT_OBJECT_DIRECTORY: void 0,
2134
+ GIT_ALTERNATE_OBJECT_DIRECTORIES: void 0,
2135
+ GIT_CONFIG_PARAMETERS: void 0,
2136
+ GIT_CONFIG_COUNT: "0",
2137
+ GIT_TERMINAL_PROMPT: "0",
2138
+ GIT_OPTIONAL_LOCKS: "0"
2139
+ }
2140
+ },
2141
+ (error, stdout) => {
2142
+ clearTimeout(deadlineTimer);
2143
+ deadlineTimer = void 0;
2144
+ active = void 0;
2145
+ if (!cancelled) {
2146
+ try {
2147
+ done(error ? void 0 : stdout.trim());
2148
+ } catch (e) {
2149
+ }
2150
+ }
2151
+ }
2152
+ );
2153
+ active.unref();
2154
+ if (active.stdout && "unref" in active.stdout && typeof active.stdout.unref === "function")
2155
+ active.stdout.unref();
2156
+ if (active.stderr && "unref" in active.stderr && typeof active.stderr.unref === "function")
2157
+ active.stderr.unref();
2158
+ deadlineTimer = setTimeout(() => {
2159
+ try {
2160
+ active == null ? void 0 : active.kill("SIGKILL");
2161
+ } catch (e) {
2162
+ }
2163
+ }, remaining);
2164
+ deadlineTimer.unref();
2165
+ } catch (e) {
2166
+ try {
2167
+ done(void 0);
2168
+ } catch (e2) {
2169
+ }
2170
+ }
2171
+ };
2172
+ const scheduled = setTimeout(() => {
2173
+ try {
2174
+ run(["rev-parse", "HEAD"], (rawSha) => {
2175
+ const commitSha = validAppCommitSha(rawSha);
2176
+ if (!commitSha) {
2177
+ completed({});
2178
+ return;
2179
+ }
2180
+ run(["-c", "core.fsmonitor=false", "status", "--porcelain"], (status) => {
2181
+ const finish = (branch) => {
2182
+ run(["rev-parse", "HEAD"], (finalSha) => {
2183
+ const coherent = validAppCommitSha(finalSha) === commitSha;
2184
+ completed({
2185
+ commitSha,
2186
+ ...coherent && status !== void 0 ? { commitDirty: status !== "" } : {},
2187
+ ...coherent && branch && branch !== "HEAD" ? { branch } : {}
2188
+ });
2189
+ });
2190
+ };
2191
+ if (detectBranch) run(["symbolic-ref", "--quiet", "--short", "HEAD"], finish);
2192
+ else finish();
2193
+ });
2194
+ });
2195
+ } catch (e) {
2196
+ completed({});
2197
+ }
2198
+ }, 0);
2199
+ scheduled.unref();
2200
+ return () => {
2201
+ cancelled = true;
2202
+ clearTimeout(scheduled);
2203
+ clearTimeout(deadlineTimer);
2204
+ try {
2205
+ active == null ? void 0 : active.kill("SIGKILL");
2206
+ } catch (e) {
2207
+ }
2208
+ };
2209
+ };
2210
+ function createAppGitContext2(options, runtime = {}) {
2211
+ return new AppGitContext(options, { ...runtime, resolveLocal });
2212
+ }
2213
+ function hasDestination(options) {
2214
+ var _a;
2215
+ if (options.enabled === false) return false;
2216
+ return Boolean(((_a = options.writeKey) == null ? void 0 : _a.trim()) || resolveLocalDebuggerBaseUrl(options.localDebuggerUrl));
2217
+ }
2218
+ var EventShipper2 = class extends EventShipper {
2219
+ constructor(options) {
2220
+ var _a;
2221
+ const ownsAppGitContext = options.appGitContext === void 0;
2222
+ const appGitContext = (_a = options.appGitContext) != null ? _a : createAppGitContext2(options.appGit, { allowDiscovery: hasDestination(options) });
2223
+ try {
2224
+ super({ ...options, appGitContext });
2225
+ } catch (error) {
2226
+ if (ownsAppGitContext) appGitContext.dispose();
2227
+ throw error;
2228
+ }
2229
+ if (ownsAppGitContext) this.ownAppGitContextForShutdown();
2230
+ }
2231
+ };
2232
+ var TraceShipper2 = class extends TraceShipper {
2233
+ constructor(options) {
2234
+ var _a;
2235
+ const ownsAppGitContext = options.appGitContext === void 0;
2236
+ const appGitContext = (_a = options.appGitContext) != null ? _a : createAppGitContext2(options.appGit, { allowDiscovery: hasDestination(options) });
2237
+ try {
2238
+ super({ ...options, appGitContext });
2239
+ } catch (error) {
2240
+ if (ownsAppGitContext) appGitContext.dispose();
2241
+ throw error;
2242
+ }
2243
+ this.ownsAppGitContext = ownsAppGitContext;
2244
+ }
2245
+ };
1424
2246
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
1425
2247
  var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
1426
2248
  "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
@@ -1485,7 +2307,20 @@ function loadConfig(projectDirectory) {
1485
2307
  debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_g = merged.debug) != null ? _g : false,
1486
2308
  captureSystemPrompt: process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] !== void 0 ? process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] === "true" : (_h = merged.capture_system_prompt) != null ? _h : false,
1487
2309
  eventMetadata,
1488
- localWorkshopUrl: resolveLocalWorkshopUrl(merged.local_workshop_url)
2310
+ localWorkshopUrl: resolveLocalWorkshopUrl(merged.local_workshop_url),
2311
+ appGit: mapAppGit(merged.app_git)
2312
+ };
2313
+ }
2314
+ function mapAppGit(value) {
2315
+ if (value === false) return false;
2316
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
2317
+ return {
2318
+ commitSha: "commit_sha" in value && (typeof value.commit_sha === "string" || value.commit_sha === null) ? value.commit_sha : void 0,
2319
+ commitDirty: "commit_dirty" in value && (typeof value.commit_dirty === "boolean" || value.commit_dirty === null) ? value.commit_dirty : void 0,
2320
+ branch: "branch" in value && (typeof value.branch === "string" || value.branch === null) ? value.branch : void 0,
2321
+ detectBranch: "detect_branch" in value && typeof value.detect_branch === "boolean" ? value.detect_branch : void 0,
2322
+ sourceDirectory: "source_directory" in value && typeof value.source_directory === "string" ? value.source_directory : void 0,
2323
+ autoDetect: "auto_detect" in value && typeof value.auto_detect === "boolean" ? value.auto_detect : void 0
1489
2324
  };
1490
2325
  }
1491
2326
  function resolveLocalWorkshopUrl(fileValue) {
@@ -1502,7 +2337,7 @@ function resolveLocalWorkshopUrl(fileValue) {
1502
2337
  // package.json
1503
2338
  var package_default = {
1504
2339
  name: "@raindrop-ai/opencode-plugin",
1505
- version: "0.1.2",
2340
+ version: "0.1.4",
1506
2341
  description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
1507
2342
  type: "module",
1508
2343
  main: "dist/index.js",
@@ -1573,7 +2408,7 @@ var PLUGIN_NAME = package_default.name;
1573
2408
  var PLUGIN_VERSION = package_default.version;
1574
2409
 
1575
2410
  // src/shipper.ts
1576
- var EventShipper2 = class extends EventShipper {
2411
+ var EventShipper3 = class extends EventShipper2 {
1577
2412
  constructor(opts) {
1578
2413
  var _a, _b, _c, _d;
1579
2414
  super({
@@ -1585,7 +2420,7 @@ var EventShipper2 = class extends EventShipper {
1585
2420
  });
1586
2421
  }
1587
2422
  };
1588
- var TraceShipper2 = class extends TraceShipper {
2423
+ var TraceShipper3 = class extends TraceShipper2 {
1589
2424
  constructor(opts) {
1590
2425
  var _a, _b, _c;
1591
2426
  super({
@@ -1707,18 +2542,13 @@ function parseOpenCodeAssistantTokens(tokens) {
1707
2542
  };
1708
2543
  }
1709
2544
  function modelSpendSpanAttributes(input) {
1710
- const providerAttr = attrString(
1711
- "gen_ai.provider.name",
1712
- canonicalModelProvider(input.providerID)
1713
- );
1714
- const usageAttrs = buildModelUsageAttributes({
2545
+ const providerAttr = attrString("gen_ai.provider.name", canonicalModelProvider(input.providerID));
2546
+ const usageAttrs = buildRawModelUsageAttributes({
1715
2547
  inputTokens: input.tokens.input,
1716
2548
  outputTokens: input.tokens.output,
1717
2549
  reasoningTokens: input.tokens.reasoning,
1718
2550
  cacheReadInputTokens: input.tokens.cacheRead,
1719
- cacheWriteInputTokens: input.tokens.cacheWrite,
1720
- inputTokenSemantics: "exclusive",
1721
- outputTokenSemantics: "exclusive"
2551
+ cacheWriteInputTokens: input.tokens.cacheWrite
1722
2552
  });
1723
2553
  return providerAttr ? [providerAttr, ...usageAttrs] : usageAttrs;
1724
2554
  }
@@ -1726,6 +2556,9 @@ function buildLlmGenAiAttributes(input) {
1726
2556
  const parsed = parseOpenCodeAssistantTokens(input.tokens);
1727
2557
  if (!parsed) return [];
1728
2558
  return [
2559
+ // OpenCode has already normalized both token axes. Provenance lets Dawn
2560
+ // select that contract without the SDK or customer declaring semantics.
2561
+ attrString(MODEL_USAGE_ATTRIBUTES.usageSource, "opencode"),
1729
2562
  attrString("gen_ai.system", input.providerID),
1730
2563
  attrString("gen_ai.request.model", input.modelID),
1731
2564
  attrString("gen_ai.response.model", input.modelID),
@@ -1745,11 +2578,11 @@ function createSessionParentMapHelpers({
1745
2578
  }) {
1746
2579
  function applyChildSessionParentToState(childSessionId) {
1747
2580
  const parentInfo = mapChildSessionToParent2.get(childSessionId);
1748
- const state = sessions2.get(childSessionId);
1749
- if (!parentInfo || !state) return;
1750
- state.parentId = parentInfo.parentId;
1751
- state.parentTaskSpanIds = parentInfo.parentTaskSpanIds;
1752
- state.parentEventContext = parentInfo.eventContext;
2581
+ const state2 = sessions2.get(childSessionId);
2582
+ if (!parentInfo || !state2) return;
2583
+ state2.parentId = parentInfo.parentId;
2584
+ state2.parentTaskSpanIds = parentInfo.parentTaskSpanIds;
2585
+ state2.parentEventContext = parentInfo.eventContext;
1753
2586
  }
1754
2587
  function queuePendingChildByCallKey(key, childSessionId) {
1755
2588
  var _a;
@@ -1845,10 +2678,10 @@ function markTaskCallFinished(sessionID, callID) {
1845
2678
  running.delete(callID);
1846
2679
  if (running.size === 0) runningTaskCallsBySession.delete(sessionID);
1847
2680
  }
1848
- function endPendingToolSpanWithError(state, callID, tool, toolCallArgs, errorMessage, traceShipper) {
1849
- const startInfo = state.toolSpanStarts.get(callID);
2681
+ function endPendingToolSpanWithError(state2, callID, tool, toolCallArgs, errorMessage, traceShipper) {
2682
+ const startInfo = state2.toolSpanStarts.get(callID);
1850
2683
  if (!startInfo) return false;
1851
- state.toolSpanStarts.delete(callID);
2684
+ state2.toolSpanStarts.delete(callID);
1852
2685
  const error = new Error(errorMessage);
1853
2686
  const endAttrs = [
1854
2687
  attrString("ai.toolCall.args", toolCallArgs),
@@ -1948,17 +2781,17 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
1948
2781
  if (info == null || typeof info !== "object")
1949
2782
  throw new Error("session.created: props.info is required");
1950
2783
  const parentID = info["parentID"];
1951
- let state = sessions.get(sessionID);
1952
- if (!state) {
1953
- state = createSessionState(sessionID);
1954
- sessions.set(sessionID, state);
2784
+ let state2 = sessions.get(sessionID);
2785
+ if (!state2) {
2786
+ state2 = createSessionState(sessionID);
2787
+ sessions.set(sessionID, state2);
1955
2788
  }
1956
- state.parentId = parentID;
1957
- if (state.parentId) mapChildSessionToParent.set(sessionID, { parentId: state.parentId });
2789
+ state2.parentId = parentID;
2790
+ if (state2.parentId) mapChildSessionToParent.set(sessionID, { parentId: state2.parentId });
1958
2791
  } else if (event.type === "session.compacted") {
1959
- const state = sessions.get(String(sessionID));
1960
- if (!state) return;
1961
- state.isCompacting = true;
2792
+ const state2 = sessions.get(String(sessionID));
2793
+ if (!state2) return;
2794
+ state2.isCompacting = true;
1962
2795
  } else if (event.type === "message.part.updated") {
1963
2796
  const part = props["part"];
1964
2797
  if (part == null || typeof part !== "object")
@@ -1970,10 +2803,10 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
1970
2803
  throw new Error("message.part.updated: part.sessionID is required");
1971
2804
  if (messageId == null || messageId === "")
1972
2805
  throw new Error("message.part.updated: part.messageID is required");
1973
- const state = sessions.get(partSessionID);
1974
- if (!state) return;
2806
+ const state2 = sessions.get(partSessionID);
2807
+ if (!state2) return;
1975
2808
  if (partObj["type"] === "text" && typeof partObj["text"] === "string") {
1976
- state.outputParts.set(messageId, capText2(partObj["text"]));
2809
+ state2.outputParts.set(messageId, capText2(partObj["text"]));
1977
2810
  } else if (partObj["type"] === "tool" && messageId) {
1978
2811
  const tool = partObj["tool"];
1979
2812
  const callID = partObj["callID"];
@@ -2005,7 +2838,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
2005
2838
  const input = toolState["input"];
2006
2839
  const toolCallArgs = input === void 0 ? void 0 : typeof input === "string" ? capText2(input) : boundedStringify(input);
2007
2840
  if (endPendingToolSpanWithError(
2008
- state,
2841
+ state2,
2009
2842
  callID,
2010
2843
  tool,
2011
2844
  toolCallArgs,
@@ -2016,7 +2849,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
2016
2849
  }
2017
2850
  }
2018
2851
  } else if (partObj["type"] === "reasoning" && typeof partObj["text"] === "string") {
2019
- state.reasoningParts.set(messageId, capText2(partObj["text"]));
2852
+ state2.reasoningParts.set(messageId, capText2(partObj["text"]));
2020
2853
  }
2021
2854
  } else if (event.type === "message.updated") {
2022
2855
  const msgInfo = props["info"];
@@ -2036,10 +2869,10 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
2036
2869
  throw new Error("message.updated: info.time is required");
2037
2870
  const timeObj = time;
2038
2871
  if (timeObj["completed"] == null) return;
2039
- const state = sessions.get(msgSessionID);
2040
- if (!state || !state.currentEventId || !state.currentRootSpan) return;
2041
- if (state.processedMessages.has(messageId)) return;
2042
- state.processedMessages.add(messageId);
2872
+ const state2 = sessions.get(msgSessionID);
2873
+ if (!state2 || !state2.currentEventId || !state2.currentRootSpan) return;
2874
+ if (state2.processedMessages.has(messageId)) return;
2875
+ state2.processedMessages.add(messageId);
2043
2876
  const tokens = info2["tokens"];
2044
2877
  if (tokens == null || typeof tokens !== "object")
2045
2878
  throw new Error("message.updated: info.tokens is required");
@@ -2056,13 +2889,13 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
2056
2889
  );
2057
2890
  const modelName = `${providerID}/${modelID}`;
2058
2891
  const finishReason = info2["finish"];
2059
- const outputText = state.outputParts.get(messageId);
2060
- const reasoningText = state.reasoningParts.get(messageId);
2892
+ const outputText = state2.outputParts.get(messageId);
2893
+ const reasoningText = state2.reasoningParts.get(messageId);
2061
2894
  const isToolCallsCompletion = finishReason === "tool-calls";
2062
2895
  if (outputText !== void 0 && outputText.trim().length > 0) {
2063
- state.turnOutputParts.push(outputText);
2896
+ state2.turnOutputParts.push(outputText);
2064
2897
  }
2065
- state.turnOutputTokens += outputTokens;
2898
+ state2.turnOutputTokens += outputTokens;
2066
2899
  if (outputText === void 0 && !isToolCallsCompletion) return;
2067
2900
  const msgError = info2["error"];
2068
2901
  const errorForSpan = msgError ? (() => {
@@ -2082,17 +2915,17 @@ type: ${name != null ? name : "UnknownError"}`;
2082
2915
  attrString("ai.response.text", responseText),
2083
2916
  ...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
2084
2917
  ];
2085
- if (state.currentSystemPrompt) {
2918
+ if (state2.currentSystemPrompt) {
2086
2919
  llmAttrs.push(
2087
2920
  attrString("gen_ai.prompt.0.role", "system"),
2088
- attrString("gen_ai.prompt.0.content", state.currentSystemPrompt)
2921
+ attrString("gen_ai.prompt.0.content", state2.currentSystemPrompt)
2089
2922
  );
2090
2923
  }
2091
- const llmParent = state.parentId && state.parentTaskSpanIds ? state.parentTaskSpanIds : state.currentRootSpan.ids;
2924
+ const llmParent = state2.parentId && state2.parentTaskSpanIds ? state2.parentTaskSpanIds : state2.currentRootSpan.ids;
2092
2925
  const llmSpan = traceShipper.startSpan({
2093
2926
  name: modelName,
2094
2927
  parent: llmParent,
2095
- eventId: state.currentEventId,
2928
+ eventId: state2.currentEventId,
2096
2929
  attributes: llmAttrs
2097
2930
  });
2098
2931
  if (typeof timeObj["created"] === "number") {
@@ -2103,40 +2936,40 @@ type: ${name != null ? name : "UnknownError"}`;
2103
2936
  return;
2104
2937
  }
2105
2938
  if (outputText === void 0) return;
2106
- const turnOutputText = capText2(state.turnOutputParts.join("\n\n"));
2107
- const rootSpan = state.currentRootSpan;
2108
- const isCompaction = state.isCompacting === true;
2109
- if (state.parentId) {
2110
- const userInput = unwrapQuotes(state.currentInput);
2939
+ const turnOutputText = capText2(state2.turnOutputParts.join("\n\n"));
2940
+ const rootSpan = state2.currentRootSpan;
2941
+ const isCompaction = state2.isCompacting === true;
2942
+ if (state2.parentId) {
2943
+ const userInput = unwrapQuotes(state2.currentInput);
2111
2944
  const llmAttrs = [
2112
2945
  attrString("ai.operationId", "generateText"),
2113
2946
  attrString("ai.prompt", userInput),
2114
2947
  attrString(
2115
2948
  "ai.prompt.messages",
2116
- buildPromptMessages(state.currentSystemPrompt, state.currentInput)
2949
+ buildPromptMessages(state2.currentSystemPrompt, state2.currentInput)
2117
2950
  ),
2118
2951
  attrString("ai.response.text", turnOutputText),
2119
2952
  ...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
2120
2953
  ];
2121
- if (state.currentSystemPrompt) {
2954
+ if (state2.currentSystemPrompt) {
2122
2955
  llmAttrs.push(
2123
2956
  attrString("gen_ai.prompt.0.role", "system"),
2124
- attrString("gen_ai.prompt.0.content", state.currentSystemPrompt)
2957
+ attrString("gen_ai.prompt.0.content", state2.currentSystemPrompt)
2125
2958
  );
2126
2959
  }
2127
- if (!state.parentTaskSpanIds) {
2960
+ if (!state2.parentTaskSpanIds) {
2128
2961
  traceShipper.endSpan(rootSpan, {
2129
2962
  error: "Missing strict parent task for child session"
2130
2963
  });
2131
- state.currentRootSpan = void 0;
2132
- state.currentEventId = void 0;
2964
+ state2.currentRootSpan = void 0;
2965
+ state2.currentEventId = void 0;
2133
2966
  await traceShipper.flush();
2134
2967
  return;
2135
2968
  }
2136
2969
  const finalLlmSpan = traceShipper.startSpan({
2137
2970
  name: modelName,
2138
- parent: state.parentTaskSpanIds,
2139
- eventId: state.currentEventId,
2971
+ parent: state2.parentTaskSpanIds,
2972
+ eventId: state2.currentEventId,
2140
2973
  attributes: llmAttrs
2141
2974
  });
2142
2975
  if (typeof timeObj["created"] === "number") {
@@ -2146,19 +2979,19 @@ type: ${name != null ? name : "UnknownError"}`;
2146
2979
  traceShipper.endSpan(rootSpan, {
2147
2980
  attributes: [
2148
2981
  attrString("is_subagent", "true"),
2149
- attrString("parent_session_id", state.parentId)
2982
+ attrString("parent_session_id", state2.parentId)
2150
2983
  ],
2151
2984
  error: errorForSpan
2152
2985
  });
2153
2986
  } else {
2154
2987
  rootSpan.name = isCompaction ? "ai.compaction" : modelName;
2155
- const userInput = unwrapQuotes(state.currentInput);
2988
+ const userInput = unwrapQuotes(state2.currentInput);
2156
2989
  const rootAttrs = [
2157
2990
  attrString("ai.operationId", "generateText"),
2158
2991
  attrString("ai.prompt", userInput),
2159
2992
  attrString(
2160
2993
  "ai.prompt.messages",
2161
- buildPromptMessages(state.currentSystemPrompt, state.currentInput)
2994
+ buildPromptMessages(state2.currentSystemPrompt, state2.currentInput)
2162
2995
  ),
2163
2996
  attrString("ai.response.text", turnOutputText),
2164
2997
  ...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
@@ -2167,10 +3000,10 @@ type: ${name != null ? name : "UnknownError"}`;
2167
3000
  rootAttrs.push(attrString("is_compaction", "true"));
2168
3001
  }
2169
3002
  rootAttrs.push(attrString("message_id", messageId));
2170
- if (state.currentSystemPrompt) {
3003
+ if (state2.currentSystemPrompt) {
2171
3004
  rootAttrs.push(
2172
3005
  attrString("gen_ai.prompt.0.role", "system"),
2173
- attrString("gen_ai.prompt.0.content", state.currentSystemPrompt)
3006
+ attrString("gen_ai.prompt.0.content", state2.currentSystemPrompt)
2174
3007
  );
2175
3008
  }
2176
3009
  traceShipper.endSpan(rootSpan, {
@@ -2178,24 +3011,24 @@ type: ${name != null ? name : "UnknownError"}`;
2178
3011
  error: errorForSpan
2179
3012
  });
2180
3013
  if (isCompaction) {
2181
- state.isCompacting = false;
3014
+ state2.isCompacting = false;
2182
3015
  }
2183
3016
  }
2184
- state.currentRootSpan = void 0;
3017
+ state2.currentRootSpan = void 0;
2185
3018
  const hasAssistantResponse = turnOutputText.trim().length > 0;
2186
3019
  if (!hasAssistantResponse && !msgError) {
2187
3020
  await traceShipper.flush();
2188
- state.currentEventId = void 0;
3021
+ state2.currentEventId = void 0;
2189
3022
  return;
2190
3023
  }
2191
- if (!state.parentId) {
2192
- await eventShipper.finish(state.currentEventId, {
2193
- userId: (_d = (_c = state.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state.sessionId,
3024
+ if (!state2.parentId) {
3025
+ await eventShipper.finish(state2.currentEventId, {
3026
+ userId: (_d = (_c = state2.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state2.sessionId,
2194
3027
  model: modelID,
2195
3028
  output: msgError ? void 0 : turnOutputText,
2196
3029
  usage: {
2197
3030
  promptTokens: inputTokens,
2198
- completionTokens: state.turnOutputTokens
3031
+ completionTokens: state2.turnOutputTokens
2199
3032
  },
2200
3033
  error: msgError ? {
2201
3034
  name: msgError.name,
@@ -2210,35 +3043,35 @@ type: ${name != null ? name : "UnknownError"}`;
2210
3043
  });
2211
3044
  }
2212
3045
  await traceShipper.flush();
2213
- state.currentEventId = void 0;
3046
+ state2.currentEventId = void 0;
2214
3047
  } else if (event.type === "session.idle") {
2215
3048
  if (!sessionID) return;
2216
- const state = sessions.get(String(sessionID));
2217
- if (!state) return;
2218
- if (state.currentRootSpan) {
2219
- traceShipper.endSpan(state.currentRootSpan);
2220
- state.currentRootSpan = void 0;
3049
+ const state2 = sessions.get(String(sessionID));
3050
+ if (!state2) return;
3051
+ if (state2.currentRootSpan) {
3052
+ traceShipper.endSpan(state2.currentRootSpan);
3053
+ state2.currentRootSpan = void 0;
2221
3054
  }
2222
- if (state.currentEventId) {
2223
- state.currentEventId = void 0;
3055
+ if (state2.currentEventId) {
3056
+ state2.currentEventId = void 0;
2224
3057
  }
2225
- state.isCompacting = false;
3058
+ state2.isCompacting = false;
2226
3059
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
2227
- if (state.parentId) {
3060
+ if (state2.parentId) {
2228
3061
  cleanupSessionParentMap(String(sessionID));
2229
3062
  sessions.delete(String(sessionID));
2230
3063
  } else {
2231
3064
  }
2232
3065
  } else if (event.type === "session.deleted") {
2233
3066
  if (!sessionID) return;
2234
- const state = sessions.get(String(sessionID));
2235
- if (!state) return;
2236
- if (state.currentRootSpan) {
2237
- traceShipper.endSpan(state.currentRootSpan);
2238
- state.currentRootSpan = void 0;
3067
+ const state2 = sessions.get(String(sessionID));
3068
+ if (!state2) return;
3069
+ if (state2.currentRootSpan) {
3070
+ traceShipper.endSpan(state2.currentRootSpan);
3071
+ state2.currentRootSpan = void 0;
2239
3072
  }
2240
- if (state.currentEventId) {
2241
- state.currentEventId = void 0;
3073
+ if (state2.currentEventId) {
3074
+ state2.currentEventId = void 0;
2242
3075
  }
2243
3076
  cleanupSessionParentMap(String(sessionID));
2244
3077
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
@@ -2247,8 +3080,8 @@ type: ${name != null ? name : "UnknownError"}`;
2247
3080
  const errorSessionID = (_h = props["sessionID"]) != null ? _h : sessionID;
2248
3081
  if (errorSessionID === void 0 || errorSessionID === "")
2249
3082
  throw new Error("session.error: missing sessionID");
2250
- const state = sessions.get(String(errorSessionID));
2251
- if (!state) return;
3083
+ const state2 = sessions.get(String(errorSessionID));
3084
+ if (!state2) return;
2252
3085
  const errorObj = props["error"];
2253
3086
  if (errorObj == null) throw new Error("session.error: missing error object");
2254
3087
  const errorName = errorObj.name;
@@ -2258,12 +3091,12 @@ type: ${name != null ? name : "UnknownError"}`;
2258
3091
  const errorStr = `${errorMessage != null ? errorMessage : errorName}
2259
3092
 
2260
3093
  type: ${errorName != null ? errorName : "UnknownError"}`;
2261
- if (state.currentRootSpan) {
2262
- traceShipper.endSpan(state.currentRootSpan, { error: errorStr });
2263
- state.currentRootSpan = void 0;
3094
+ if (state2.currentRootSpan) {
3095
+ traceShipper.endSpan(state2.currentRootSpan, { error: errorStr });
3096
+ state2.currentRootSpan = void 0;
2264
3097
  }
2265
- if (state.currentEventId) {
2266
- state.currentEventId = void 0;
3098
+ if (state2.currentEventId) {
3099
+ state2.currentEventId = void 0;
2267
3100
  }
2268
3101
  cleanupSessionParentMap(String(errorSessionID));
2269
3102
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
@@ -2283,15 +3116,15 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2283
3116
  var _a, _b, _c, _d, _e, _f, _g;
2284
3117
  try {
2285
3118
  const { sessionID } = messageInput;
2286
- let state = sessions.get(sessionID);
2287
- if (!state) {
2288
- state = createSessionState(sessionID);
2289
- sessions.set(sessionID, state);
3119
+ let state2 = sessions.get(sessionID);
3120
+ if (!state2) {
3121
+ state2 = createSessionState(sessionID);
3122
+ sessions.set(sessionID, state2);
2290
3123
  }
2291
3124
  applyChildSessionParentToState(sessionID);
2292
- if (state.currentRootSpan) {
2293
- traceShipper.endSpan(state.currentRootSpan);
2294
- state.currentRootSpan = void 0;
3125
+ if (state2.currentRootSpan) {
3126
+ traceShipper.endSpan(state2.currentRootSpan);
3127
+ state2.currentRootSpan = void 0;
2295
3128
  }
2296
3129
  if (output == null) throw new Error("chat.message: output is required");
2297
3130
  const parts = output.parts;
@@ -2322,39 +3155,39 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2322
3155
  }
2323
3156
  }
2324
3157
  }
2325
- state.eventMetadata = {
3158
+ state2.eventMetadata = {
2326
3159
  ...baseMeta,
2327
3160
  ...promptMeta,
2328
3161
  properties: baseMeta.properties || promptMeta.properties ? { ...(_a = baseMeta.properties) != null ? _a : {}, ...(_b = promptMeta.properties) != null ? _b : {} } : void 0
2329
3162
  };
2330
3163
  const userText = capText2(textParts.join("\n"));
2331
- state.currentInput = userText;
2332
- state.turnOutputParts = [];
2333
- state.turnOutputTokens = 0;
2334
- if (state.parentId) {
2335
- if (!state.parentTaskSpanIds || !state.parentEventContext) {
3164
+ state2.currentInput = userText;
3165
+ state2.turnOutputParts = [];
3166
+ state2.turnOutputTokens = 0;
3167
+ if (state2.parentId) {
3168
+ if (!state2.parentTaskSpanIds || !state2.parentEventContext) {
2336
3169
  return;
2337
3170
  }
2338
- state.currentEventId = state.parentEventContext.eventId;
2339
- state.currentRootSpan = traceShipper.startSpan({
3171
+ state2.currentEventId = state2.parentEventContext.eventId;
3172
+ state2.currentRootSpan = traceShipper.startSpan({
2340
3173
  name: "Subagent",
2341
- parent: state.parentTaskSpanIds,
2342
- eventId: state.currentEventId,
3174
+ parent: state2.parentTaskSpanIds,
3175
+ eventId: state2.currentEventId,
2343
3176
  attributes: [
2344
3177
  attrString("workspace", worktree),
2345
3178
  attrString("directory", directory),
2346
3179
  attrString("hostname", hostname),
2347
3180
  attrString("os", os),
2348
3181
  attrString("is_subagent", "true"),
2349
- attrString("parent_session_id", state.parentId)
3182
+ attrString("parent_session_id", state2.parentId)
2350
3183
  ]
2351
3184
  });
2352
3185
  return;
2353
3186
  }
2354
- state.currentEventId = generateId();
2355
- state.currentRootSpan = traceShipper.startSpan({
2356
- name: state.isCompacting ? "ai.compaction" : "ai.event",
2357
- eventId: state.currentEventId,
3187
+ state2.currentEventId = generateId();
3188
+ state2.currentRootSpan = traceShipper.startSpan({
3189
+ name: state2.isCompacting ? "ai.compaction" : "ai.event",
3190
+ eventId: state2.currentEventId,
2358
3191
  attributes: [
2359
3192
  attrString("workspace", worktree),
2360
3193
  attrString("directory", directory),
@@ -2362,11 +3195,11 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2362
3195
  attrString("os", os)
2363
3196
  ]
2364
3197
  });
2365
- await eventShipper.patch(state.currentEventId, {
3198
+ await eventShipper.patch(state2.currentEventId, {
2366
3199
  isPending: true,
2367
- userId: (_d = (_c = state.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state.sessionId,
2368
- convoId: state.sessionId,
2369
- eventName: (_f = (_e = state.eventMetadata) == null ? void 0 : _e.eventName) != null ? _f : config.eventName,
3200
+ userId: (_d = (_c = state2.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state2.sessionId,
3201
+ convoId: state2.sessionId,
3202
+ eventName: (_f = (_e = state2.eventMetadata) == null ? void 0 : _e.eventName) != null ? _f : config.eventName,
2370
3203
  input: userText,
2371
3204
  ...newAttachments.length > 0 ? { attachments: newAttachments } : {},
2372
3205
  properties: {
@@ -2375,7 +3208,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2375
3208
  hostname,
2376
3209
  os,
2377
3210
  plugin_version: PLUGIN_VERSION,
2378
- ...(_g = state.eventMetadata) == null ? void 0 : _g.properties
3211
+ ...(_g = state2.eventMetadata) == null ? void 0 : _g.properties
2379
3212
  }
2380
3213
  });
2381
3214
  } catch (err) {
@@ -2389,16 +3222,16 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2389
3222
  var _a, _b, _c;
2390
3223
  try {
2391
3224
  const { tool, sessionID, callID } = toolInput;
2392
- const state = sessions.get(sessionID);
2393
- if (!state || !state.currentEventId) return;
2394
- const spanParent = state.parentId && state.parentTaskSpanIds ? state.parentTaskSpanIds : (_a = state.currentRootSpan) == null ? void 0 : _a.ids;
3225
+ const state2 = sessions.get(sessionID);
3226
+ if (!state2 || !state2.currentEventId) return;
3227
+ const spanParent = state2.parentId && state2.parentTaskSpanIds ? state2.parentTaskSpanIds : (_a = state2.currentRootSpan) == null ? void 0 : _a.ids;
2395
3228
  if (!spanParent) return;
2396
3229
  const startTimeUnixNano = nowUnixNanoString();
2397
3230
  if (tool === "task") {
2398
3231
  const liveTaskSpan = traceShipper.startSpan({
2399
3232
  name: "ai.toolCall",
2400
3233
  parent: spanParent,
2401
- eventId: state.currentEventId,
3234
+ eventId: state2.currentEventId,
2402
3235
  attributes: [
2403
3236
  attrString("ai.operationId", "ai.toolCall"),
2404
3237
  attrString("ai.toolCall.name", tool),
@@ -2409,9 +3242,9 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2409
3242
  const ctx = {
2410
3243
  ids: liveTaskSpan.ids,
2411
3244
  eventContext: {
2412
- eventId: state.currentEventId,
2413
- userId: (_c = (_b = state.eventMetadata) == null ? void 0 : _b.userId) != null ? _c : state.sessionId,
2414
- convoId: state.sessionId
3245
+ eventId: state2.currentEventId,
3246
+ userId: (_c = (_b = state2.eventMetadata) == null ? void 0 : _b.userId) != null ? _c : state2.sessionId,
3247
+ convoId: state2.sessionId
2415
3248
  }
2416
3249
  };
2417
3250
  taskContexts.set(callKey(sessionID, callID), ctx);
@@ -2422,18 +3255,18 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2422
3255
  }
2423
3256
  running.add(callID);
2424
3257
  resolvePendingChildrenForCall(sessionID, callID, "tool.execute.before");
2425
- state.toolSpanStarts.set(callID, {
3258
+ state2.toolSpanStarts.set(callID, {
2426
3259
  startTimeUnixNano,
2427
3260
  parent: spanParent,
2428
- eventId: state.currentEventId,
3261
+ eventId: state2.currentEventId,
2429
3262
  name: tool,
2430
3263
  liveSpan: liveTaskSpan
2431
3264
  });
2432
3265
  } else {
2433
- state.toolSpanStarts.set(callID, {
3266
+ state2.toolSpanStarts.set(callID, {
2434
3267
  startTimeUnixNano,
2435
3268
  parent: spanParent,
2436
- eventId: state.currentEventId,
3269
+ eventId: state2.currentEventId,
2437
3270
  name: tool
2438
3271
  });
2439
3272
  }
@@ -2450,13 +3283,13 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2450
3283
  "tool.execute.after": async (toolInput, result) => {
2451
3284
  try {
2452
3285
  const { tool, sessionID, callID, args } = toolInput;
2453
- const state = sessions.get(sessionID);
2454
- if (!state || !state.currentEventId) return;
3286
+ const state2 = sessions.get(sessionID);
3287
+ if (!state2 || !state2.currentEventId) return;
2455
3288
  if (args === void 0 || args === null)
2456
3289
  throw new Error("tool.execute.after: args is required");
2457
3290
  const toolCallArgs = typeof args === "string" ? capText2(args) : boundedStringify(args);
2458
- const startInfo = state.toolSpanStarts.get(callID);
2459
- state.toolSpanStarts.delete(callID);
3291
+ const startInfo = state2.toolSpanStarts.get(callID);
3292
+ state2.toolSpanStarts.delete(callID);
2460
3293
  const resultMetadata = result.metadata;
2461
3294
  const childSessionId = resultMetadata == null ? void 0 : resultMetadata["sessionId"];
2462
3295
  if (tool === "task" && childSessionId) {
@@ -2509,9 +3342,9 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2509
3342
  try {
2510
3343
  const sessionID = input.sessionID;
2511
3344
  if (!sessionID) return;
2512
- const state = sessions.get(sessionID);
2513
- if (state) {
2514
- state.isCompacting = true;
3345
+ const state2 = sessions.get(sessionID);
3346
+ if (state2) {
3347
+ state2.isCompacting = true;
2515
3348
  }
2516
3349
  } catch (err) {
2517
3350
  rateLimitedErrorLog(
@@ -2528,15 +3361,15 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2528
3361
  if (!config.captureSystemPrompt) return;
2529
3362
  const sessionID = input.sessionID;
2530
3363
  if (!sessionID) return;
2531
- let state = sessions.get(sessionID);
2532
- if (!state) {
2533
- state = createSessionState(sessionID);
2534
- sessions.set(sessionID, state);
3364
+ let state2 = sessions.get(sessionID);
3365
+ if (!state2) {
3366
+ state2 = createSessionState(sessionID);
3367
+ sessions.set(sessionID, state2);
2535
3368
  }
2536
3369
  const MAX_SYSTEM_PROMPT_LENGTH = 32768;
2537
3370
  const TRUNCATION_MARKER3 = "\n...[truncated]";
2538
3371
  const joined = output.system.join("\n\n");
2539
- state.currentSystemPrompt = joined.length > MAX_SYSTEM_PROMPT_LENGTH ? joined.slice(0, MAX_SYSTEM_PROMPT_LENGTH - TRUNCATION_MARKER3.length) + TRUNCATION_MARKER3 : joined;
3372
+ state2.currentSystemPrompt = joined.length > MAX_SYSTEM_PROMPT_LENGTH ? joined.slice(0, MAX_SYSTEM_PROMPT_LENGTH - TRUNCATION_MARKER3.length) + TRUNCATION_MARKER3 : joined;
2540
3373
  } catch (err) {
2541
3374
  rateLimitedErrorLog(
2542
3375
  "experimental.chat.system.transform",
@@ -2548,7 +3381,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2548
3381
  }
2549
3382
 
2550
3383
  // src/index.ts
2551
- function createRaindropHandle(eventShipper, traceShipper) {
3384
+ function createRaindropHandle(eventShipper, traceShipper, appGitContext) {
2552
3385
  return {
2553
3386
  users: {
2554
3387
  async identify(users) {
@@ -2559,12 +3392,19 @@ function createRaindropHandle(eventShipper, traceShipper) {
2559
3392
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
2560
3393
  },
2561
3394
  async shutdown() {
2562
- await Promise.all([eventShipper.shutdown(), traceShipper.shutdown()]);
3395
+ try {
3396
+ await Promise.all([eventShipper.shutdown(), traceShipper.shutdown()]);
3397
+ } finally {
3398
+ try {
3399
+ appGitContext.dispose();
3400
+ } catch (e) {
3401
+ }
3402
+ }
2563
3403
  }
2564
3404
  };
2565
3405
  }
2566
- async function plugin(input) {
2567
- var _a;
3406
+ async function plugin(input, opts = {}) {
3407
+ var _a, _b, _c;
2568
3408
  const config = loadConfig(input.directory);
2569
3409
  function appLog(level, message) {
2570
3410
  console.log(`[raindrop-ai/opencode-plugin] [${level}] ${message}`);
@@ -2586,21 +3426,28 @@ async function plugin(input) {
2586
3426
  ].filter(Boolean);
2587
3427
  appLog("info", `Raindrop tracing enabled \u2014 destinations: ${destinations.join(", ")}`);
2588
3428
  }
2589
- const eventShipper = new EventShipper2({
3429
+ const configuredAppGit = (_a = opts.appGit) != null ? _a : config.appGit;
3430
+ const appGitContext = createAppGitContext2(
3431
+ ((_b = config.writeKey) == null ? void 0 : _b.trim()) || hasLocalDestination ? configuredAppGit : false,
3432
+ { allowLocalGit: false }
3433
+ );
3434
+ const eventShipper = new EventShipper3({
2590
3435
  writeKey: config.writeKey,
2591
3436
  endpoint: config.endpoint,
2592
3437
  debug: config.debug,
2593
3438
  projectId: config.projectId,
2594
- localDebuggerUrl: config.localWorkshopUrl
3439
+ localDebuggerUrl: config.localWorkshopUrl,
3440
+ appGitContext
2595
3441
  });
2596
- const traceShipper = new TraceShipper2({
3442
+ const traceShipper = new TraceShipper3({
2597
3443
  writeKey: config.writeKey,
2598
3444
  endpoint: config.endpoint,
2599
3445
  debug: config.debug,
2600
3446
  projectId: config.projectId,
2601
- localDebuggerUrl: config.localWorkshopUrl
3447
+ localDebuggerUrl: config.localWorkshopUrl,
3448
+ appGitContext
2602
3449
  });
2603
- const worktree = (_a = input.worktree) != null ? _a : input.directory;
3450
+ const worktree = (_c = input.worktree) != null ? _c : input.directory;
2604
3451
  const hooks = createHooks(
2605
3452
  config,
2606
3453
  worktree,
@@ -2608,5 +3455,5 @@ async function plugin(input) {
2608
3455
  eventShipper,
2609
3456
  traceShipper
2610
3457
  );
2611
- return { ...hooks, raindrop: createRaindropHandle(eventShipper, traceShipper) };
3458
+ return { ...hooks, raindrop: createRaindropHandle(eventShipper, traceShipper, appGitContext) };
2612
3459
  }