@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.js CHANGED
@@ -1,4 +1,518 @@
1
- // ../core/dist/chunk-YUIXRQWN.js
1
+ // ../core/dist/chunk-BYBMRLZM.js
2
+ var EVAL_CORRELATION_ID_ATTRIBUTE = "raindrop.eval_correlation_id";
3
+ var PROBE_SCOPE = { correlationId: "" };
4
+ var EvalScopeStorage = class {
5
+ constructor() {
6
+ this._als = null;
7
+ this._probed = false;
8
+ }
9
+ maybeAdoptAsyncLocalStorage() {
10
+ if (this._probed) return;
11
+ this._probed = true;
12
+ try {
13
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
14
+ if (!Ctor) return;
15
+ const als = new Ctor();
16
+ als.run(PROBE_SCOPE, () => {
17
+ });
18
+ this._als = als;
19
+ } catch (e) {
20
+ this._als = null;
21
+ }
22
+ }
23
+ run(scope, callback) {
24
+ this.maybeAdoptAsyncLocalStorage();
25
+ if (this._als) return this._als.run(scope, callback);
26
+ const previous = this._syncScope;
27
+ this._syncScope = scope;
28
+ try {
29
+ return callback();
30
+ } finally {
31
+ this._syncScope = previous;
32
+ }
33
+ }
34
+ current() {
35
+ this.maybeAdoptAsyncLocalStorage();
36
+ if (this._als) return this._als.getStore();
37
+ return this._syncScope;
38
+ }
39
+ };
40
+ var EVAL_SCOPE_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.evalScopeStorage");
41
+ function evalScopeStorage() {
42
+ const holder = globalThis;
43
+ let storage = holder[EVAL_SCOPE_STORAGE_KEY];
44
+ if (!storage) {
45
+ storage = new EvalScopeStorage();
46
+ holder[EVAL_SCOPE_STORAGE_KEY] = storage;
47
+ }
48
+ return storage;
49
+ }
50
+ function currentEvalScope() {
51
+ try {
52
+ return evalScopeStorage().current();
53
+ } catch (e) {
54
+ return void 0;
55
+ }
56
+ }
57
+ function evalScopeAttributes() {
58
+ const scope = currentEvalScope();
59
+ if (!scope) return {};
60
+ return { [EVAL_CORRELATION_ID_ATTRIBUTE]: scope.correlationId };
61
+ }
62
+
63
+ // ../core/dist/chunk-WJOR6M7H.js
64
+ var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
65
+ function state() {
66
+ const holder = globalThis;
67
+ let current = holder[STATE_KEY];
68
+ if (!current) {
69
+ let storage = null;
70
+ try {
71
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
72
+ if (Ctor) storage = new Ctor();
73
+ } catch (e) {
74
+ storage = null;
75
+ }
76
+ current = {
77
+ storage,
78
+ syncDestination: void 0,
79
+ spanDestinations: /* @__PURE__ */ new WeakMap()
80
+ };
81
+ holder[STATE_KEY] = current;
82
+ }
83
+ return current;
84
+ }
85
+ function currentReplayTraceDestination() {
86
+ var _a, _b;
87
+ const routing = state();
88
+ return (_b = (_a = routing.storage) == null ? void 0 : _a.getStore()) != null ? _b : routing.syncDestination;
89
+ }
90
+ function rememberReplayTraceDestination(span) {
91
+ const destination = currentReplayTraceDestination();
92
+ if (destination) state().spanDestinations.set(span, destination);
93
+ }
94
+ function copyReplayTraceDestination(source, target) {
95
+ const destination = state().spanDestinations.get(source);
96
+ if (destination) state().spanDestinations.set(target, destination);
97
+ }
98
+ function replayTraceDestinationFor(value) {
99
+ if (typeof value !== "object" && typeof value !== "function" || value === null) {
100
+ return void 0;
101
+ }
102
+ return state().spanDestinations.get(value);
103
+ }
104
+ function recordReplayExportedSpan(destination, spanId, traceId) {
105
+ var _a, _b;
106
+ if (!destination || !spanId || !traceId) return;
107
+ (_a = destination.exportedSpanIds) == null ? void 0 : _a.add(spanId);
108
+ (_b = destination.exportedTraceIds) == null ? void 0 : _b.add(traceId);
109
+ }
110
+ function replayTraceUrl(baseUrl) {
111
+ return `${baseUrl.replace(/\/+$/, "")}/v1/traces`;
112
+ }
113
+ function getAsyncLocalStorageCtor() {
114
+ return globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
115
+ }
116
+ var MAX_BINDING_STACK = 128;
117
+ var EMPTY_STACK = [];
118
+ var ContextStackStorage = class {
119
+ /** Emitted once when this runtime cannot support an ambient push. */
120
+ constructor(noAmbientBindWarning) {
121
+ this.noAmbientBindWarning = noAmbientBindWarning;
122
+ this._als = null;
123
+ this._enterWithUsable = false;
124
+ this._syncStack = EMPTY_STACK;
125
+ this._adoptionChecked = false;
126
+ this._warnedNoAmbientBind = false;
127
+ }
128
+ maybeAdoptAsyncLocalStorage() {
129
+ if (this._adoptionChecked) return;
130
+ this._adoptionChecked = true;
131
+ const Ctor = getAsyncLocalStorageCtor();
132
+ if (!Ctor) return;
133
+ const als = new Ctor();
134
+ try {
135
+ als.run(EMPTY_STACK, () => {
136
+ });
137
+ } catch (e) {
138
+ return;
139
+ }
140
+ this._als = als;
141
+ if (typeof als.enterWith === "function") {
142
+ try {
143
+ als.enterWith(EMPTY_STACK);
144
+ this._enterWithUsable = true;
145
+ } catch (e) {
146
+ this._enterWithUsable = false;
147
+ }
148
+ }
149
+ }
150
+ getStack() {
151
+ var _a;
152
+ this.maybeAdoptAsyncLocalStorage();
153
+ if (this._als) return (_a = this._als.getStore()) != null ? _a : this._syncStack;
154
+ return this._syncStack;
155
+ }
156
+ /**
157
+ * Persist an ambient stack mutation. Returns false when an ALS is adopted but
158
+ * `enterWith` is unusable (workerd): callers must NOT fall back to the
159
+ * process-shared sync stack, since concurrent isolates would cross-stamp.
160
+ */
161
+ setAmbientStack(stack) {
162
+ if (this._als) {
163
+ if (!this._enterWithUsable) return false;
164
+ this._als.enterWith(stack);
165
+ return true;
166
+ }
167
+ this._syncStack = stack;
168
+ return true;
169
+ }
170
+ push(binding) {
171
+ this.maybeAdoptAsyncLocalStorage();
172
+ if (this._als && !this._enterWithUsable) {
173
+ this.warnNoAmbientBindOnce();
174
+ return;
175
+ }
176
+ let stack = this.getStack();
177
+ if (stack.length >= MAX_BINDING_STACK) {
178
+ stack = stack.filter((b) => b.isLive());
179
+ if (stack.length >= MAX_BINDING_STACK) {
180
+ stack = stack.slice(stack.length - (MAX_BINDING_STACK - 1));
181
+ }
182
+ }
183
+ this.setAmbientStack([...stack, binding]);
184
+ }
185
+ remove(binding) {
186
+ binding.markDead();
187
+ const stack = this.getStack();
188
+ const pruned = stack.filter((b) => b !== binding && b.isLive());
189
+ if (pruned.length !== stack.length) {
190
+ this.setAmbientStack(pruned);
191
+ }
192
+ }
193
+ runWith(binding, callback) {
194
+ this.maybeAdoptAsyncLocalStorage();
195
+ if (this._als) {
196
+ return this._als.run([...this.getStack(), binding], callback);
197
+ }
198
+ const previous = this._syncStack;
199
+ this._syncStack = [...previous, binding];
200
+ try {
201
+ return callback();
202
+ } finally {
203
+ this._syncStack = previous;
204
+ }
205
+ }
206
+ current() {
207
+ const stack = this.getStack();
208
+ if (stack.length === 0) return void 0;
209
+ const top = stack[stack.length - 1];
210
+ if (top.isLive()) return top;
211
+ const pruned = stack.filter((b) => b.isLive());
212
+ this.setAmbientStack(pruned);
213
+ return pruned.length > 0 ? pruned[pruned.length - 1] : void 0;
214
+ }
215
+ warnNoAmbientBindOnce() {
216
+ if (this._warnedNoAmbientBind) return;
217
+ this._warnedNoAmbientBind = true;
218
+ console.warn(this.noAmbientBindWarning);
219
+ }
220
+ };
221
+ 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.";
222
+ var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
223
+ function routingStorage() {
224
+ const holder = globalThis;
225
+ let storage = holder[ROUTING_STORAGE_KEY];
226
+ if (!storage) {
227
+ storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
228
+ holder[ROUTING_STORAGE_KEY] = storage;
229
+ }
230
+ return storage;
231
+ }
232
+ function currentRoutingBinding() {
233
+ const bound = routingStorage().current();
234
+ if (!bound) return void 0;
235
+ return {
236
+ projectId: bound.projectId,
237
+ authHint: bound.authHint,
238
+ ...bound.appGit ? { appGit: bound.appGit } : {},
239
+ ...bound.appGitSource ? { appGitSource: bound.appGitSource } : {}
240
+ };
241
+ }
242
+ var APP_COMMIT_SHA = "raindrop.app.commit_sha";
243
+ var APP_COMMIT_DIRTY = "raindrop.app.commit_dirty";
244
+ var APP_BRANCH = "raindrop.app.branch";
245
+ var APP_GIT_KEYS = [APP_COMMIT_SHA, APP_COMMIT_DIRTY, APP_BRANCH];
246
+ var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
247
+ function appGitExplicitKeys(properties) {
248
+ const keys = /* @__PURE__ */ new Set();
249
+ if (!properties) return keys;
250
+ for (const key of APP_GIT_KEYS) {
251
+ if (own(properties, key)) keys.add(key);
252
+ }
253
+ return keys;
254
+ }
255
+ function mergeAppGitSnapshot(base, properties, explicitKeys = /* @__PURE__ */ new Set()) {
256
+ try {
257
+ const result = { ...base };
258
+ if (properties && own(properties, APP_COMMIT_SHA) && properties[APP_COMMIT_SHA] !== result[APP_COMMIT_SHA]) {
259
+ if (!explicitKeys.has(APP_COMMIT_DIRTY) && !own(properties, APP_COMMIT_DIRTY)) {
260
+ delete result[APP_COMMIT_DIRTY];
261
+ }
262
+ if (!explicitKeys.has(APP_BRANCH) && !own(properties, APP_BRANCH)) {
263
+ delete result[APP_BRANCH];
264
+ }
265
+ }
266
+ for (const key of APP_GIT_KEYS) {
267
+ if (properties && own(properties, key)) result[key] = properties[key];
268
+ }
269
+ return Object.freeze(result);
270
+ } catch (e) {
271
+ return Object.freeze({});
272
+ }
273
+ }
274
+ function validAppCommitSha(value) {
275
+ if (typeof value !== "string" || value.length > 128) return void 0;
276
+ const cleaned = value.trim();
277
+ return /^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/.test(cleaned) ? cleaned.toLowerCase() : void 0;
278
+ }
279
+ function shortString(value) {
280
+ if (typeof value !== "string" || value.length > 1024) return void 0;
281
+ const cleaned = value.trim();
282
+ return cleaned.length > 0 ? cleaned : void 0;
283
+ }
284
+ function flag(value) {
285
+ if (value === true || value === "true" || value === "1") return true;
286
+ if (value === false || value === "false" || value === "0") return false;
287
+ return void 0;
288
+ }
289
+ function environment() {
290
+ try {
291
+ return typeof process === "undefined" ? {} : process.env;
292
+ } catch (e) {
293
+ return {};
294
+ }
295
+ }
296
+ var DEPLOYMENTS = [
297
+ ["VERCEL", "VERCEL_GIT_COMMIT_SHA", "VERCEL_GIT_COMMIT_REF"],
298
+ ["NETLIFY", "COMMIT_REF", "BRANCH"],
299
+ ["RAILWAY_ENVIRONMENT_NAME", "RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH"],
300
+ ["RAILWAY_ENVIRONMENT", "RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH"],
301
+ ["RENDER", "RENDER_GIT_COMMIT", "RENDER_GIT_BRANCH"],
302
+ ["DYNO", "HEROKU_BUILD_COMMIT", ""],
303
+ ["DYNO", "HEROKU_SLUG_COMMIT", ""],
304
+ ["AWS_APP_ID", "AWS_COMMIT_ID", "AWS_BRANCH"],
305
+ ["WORKERS_CI", "WORKERS_CI_COMMIT_SHA", "WORKERS_CI_BRANCH"]
306
+ ];
307
+ var CI_SOURCES = [
308
+ ["GITHUB_ACTIONS", "GITHUB_SHA", "GITHUB_REF_NAME"],
309
+ ["GITLAB_CI", "CI_COMMIT_SHA", "CI_COMMIT_REF_NAME"],
310
+ ["CIRCLECI", "CIRCLE_SHA1", "CIRCLE_BRANCH"]
311
+ ];
312
+ function detectedSource(env, table, detectBranch) {
313
+ for (const row of table) {
314
+ const [marker, shaKey, branchKey] = row;
315
+ if (!marker || !shaKey || !shortString(env[marker]) || flag(env[marker]) === false) continue;
316
+ const commitSha = validAppCommitSha(env[shaKey]);
317
+ if (commitSha)
318
+ return {
319
+ commitSha,
320
+ ...detectBranch && branchKey ? { branch: shortString(env[branchKey]) } : {}
321
+ };
322
+ }
323
+ return {};
324
+ }
325
+ var AppGitContextImpl = class {
326
+ constructor(options, runtime = {}) {
327
+ this.defaults = {};
328
+ this.explicit = {};
329
+ this.explicitKeys = /* @__PURE__ */ new Set();
330
+ this.disposed = false;
331
+ this.operations = /* @__PURE__ */ new Map();
332
+ this.operationOverflow = false;
333
+ this.detectBranch = false;
334
+ this.disabled = false;
335
+ var _a, _b, _c, _d, _e, _f;
336
+ try {
337
+ if (options === false) {
338
+ this.disabled = true;
339
+ return;
340
+ }
341
+ const opts = options != null ? options : {};
342
+ const env = (_a = runtime.env) != null ? _a : environment();
343
+ const detectBranch = (_c = (_b = flag(opts.detectBranch)) != null ? _b : flag(env.RAINDROP_GIT_DETECT_BRANCH)) != null ? _c : false;
344
+ this.detectBranch = detectBranch;
345
+ const collectBranch = detectBranch || runtime.captureLegacyReplayBranch === true;
346
+ const autoDetect = runtime.allowDiscovery === false ? false : (_e = (_d = flag(opts.autoDetect)) != null ? _d : flag(env.RAINDROP_GIT_AUTO_DETECT)) != null ? _e : true;
347
+ const configuredSha = opts.commitSha;
348
+ const explicitSha = configuredSha === void 0 ? env.RAINDROP_COMMIT_SHA : configuredSha;
349
+ if (explicitSha === null) this.explicit[APP_COMMIT_SHA] = null;
350
+ else if (typeof explicitSha === "string" && validAppCommitSha(explicitSha))
351
+ this.explicit[APP_COMMIT_SHA] = explicitSha;
352
+ const configuredDirty = opts.commitDirty;
353
+ const explicitDirty = configuredDirty === void 0 ? flag(env.RAINDROP_COMMIT_DIRTY) : configuredDirty;
354
+ if (explicitDirty === null || typeof explicitDirty === "boolean")
355
+ this.explicit[APP_COMMIT_DIRTY] = explicitDirty;
356
+ const configuredBranch = opts.branch;
357
+ const explicitBranch = configuredBranch === void 0 ? env.RAINDROP_BRANCH : configuredBranch;
358
+ if (explicitBranch === null) this.explicit[APP_BRANCH] = null;
359
+ else if (typeof explicitBranch === "string" && explicitBranch.length <= 1024)
360
+ this.explicit[APP_BRANCH] = explicitBranch;
361
+ this.explicitKeys = appGitExplicitKeys(this.explicit);
362
+ this.defaults = { ...this.explicit };
363
+ if (!autoDetect || explicitSha !== void 0) return;
364
+ const sourceDirectory = (_f = shortString(opts.sourceDirectory)) != null ? _f : shortString(env.RAINDROP_GIT_SOURCE_DIRECTORY);
365
+ if (runtime.allowLocalGit === false && !sourceDirectory) return;
366
+ const deploy = sourceDirectory ? {} : detectedSource(env, DEPLOYMENTS, collectBranch);
367
+ if (deploy.commitSha) {
368
+ this.accept(deploy);
369
+ return;
370
+ }
371
+ const fallback = sourceDirectory ? {} : detectedSource(env, CI_SOURCES, collectBranch);
372
+ if (runtime.resolveLocal && (runtime.allowLocalGit !== false || sourceDirectory)) {
373
+ this.cancel = runtime.resolveLocal(sourceDirectory, collectBranch, (result) => {
374
+ try {
375
+ if (!this.disposed) this.accept(result.commitSha ? result : fallback);
376
+ } catch (e) {
377
+ }
378
+ });
379
+ } else this.accept(fallback);
380
+ } catch (e) {
381
+ }
382
+ }
383
+ accept(result) {
384
+ const sha = validAppCommitSha(result.commitSha);
385
+ if (!sha) return;
386
+ this.replayBranch = shortString(result.branch);
387
+ this.defaults = {
388
+ [APP_COMMIT_SHA]: sha,
389
+ ...typeof result.commitDirty === "boolean" ? { [APP_COMMIT_DIRTY]: result.commitDirty } : {},
390
+ ...this.detectBranch && this.replayBranch ? { [APP_BRANCH]: this.replayBranch } : {},
391
+ ...this.explicit
392
+ };
393
+ }
394
+ snapshot(properties) {
395
+ var _a;
396
+ try {
397
+ const binding = currentRoutingBinding();
398
+ const base = this.disabled && (binding == null ? void 0 : binding.appGitSource) !== "replay-override" ? {} : (_a = binding == null ? void 0 : binding.appGit) != null ? _a : this.defaults;
399
+ return this.mergeSnapshot(base, properties);
400
+ } catch (e) {
401
+ return Object.freeze({});
402
+ }
403
+ }
404
+ /** Safe defaults when no operation/span start was observed; never infer identity. */
405
+ snapshotExplicit(properties, eventId) {
406
+ try {
407
+ const base = { ...this.explicit };
408
+ const operation = eventId ? this.operations.get(eventId) : void 0;
409
+ if (operation) {
410
+ for (const key of operation.explicitKeys) base[key] = operation.snapshot[key];
411
+ }
412
+ return this.mergeSnapshot(base, properties, new Set(Object.keys(base)));
413
+ } catch (e) {
414
+ return mergeAppGitSnapshot({}, properties);
415
+ }
416
+ }
417
+ /** Existing replay branch reporting stays separate from opt-in event properties. */
418
+ snapshotForReplay() {
419
+ const snapshot = this.snapshot();
420
+ if (this.replayBranch && !own(snapshot, APP_BRANCH) && snapshot[APP_COMMIT_SHA] === this.defaults[APP_COMMIT_SHA]) {
421
+ return Object.freeze({ ...snapshot, [APP_BRANCH]: this.replayBranch });
422
+ }
423
+ return snapshot;
424
+ }
425
+ /** Merge a frozen span snapshot without losing client-authored provenance. */
426
+ mergeSnapshot(base, properties, explicitKeys = this.explicitKeys) {
427
+ return mergeAppGitSnapshot(base, properties, /* @__PURE__ */ new Set([...this.explicitKeys, ...explicitKeys]));
428
+ }
429
+ /** Shippers belonging to one client share this bounded operation registry. */
430
+ forOperation(eventId, properties, owner) {
431
+ if (!eventId) return this.snapshot(properties);
432
+ let operation = this.operations.get(eventId);
433
+ if (!operation) {
434
+ if (this.operations.size >= 1e4) this.operationOverflow = true;
435
+ if (this.operationOverflow) return this.mergeSnapshot({}, properties);
436
+ operation = {
437
+ snapshot: this.snapshot(properties),
438
+ owners: /* @__PURE__ */ new Set(),
439
+ unowned: owner === void 0,
440
+ explicitKeys: appGitExplicitKeys(properties)
441
+ };
442
+ this.operations.set(eventId, operation);
443
+ } else if (properties && APP_GIT_KEYS.some((key) => own(properties, key))) {
444
+ const explicitKeys = /* @__PURE__ */ new Set([...this.explicitKeys, ...operation.explicitKeys]);
445
+ operation.snapshot = this.mergeSnapshot(operation.snapshot, properties, explicitKeys);
446
+ for (const key of appGitExplicitKeys(properties)) operation.explicitKeys.add(key);
447
+ }
448
+ if (!owner) operation.unowned = true;
449
+ if (owner) operation.owners.add(owner);
450
+ return operation.snapshot;
451
+ }
452
+ releaseOperation(eventId, owner) {
453
+ const operation = this.operations.get(eventId);
454
+ if (!operation) return;
455
+ operation.owners.delete(owner);
456
+ if (operation.owners.size === 0 && !operation.unowned) this.operations.delete(eventId);
457
+ }
458
+ finishOperation(eventId) {
459
+ this.operations.delete(eventId);
460
+ }
461
+ dispose() {
462
+ var _a;
463
+ this.disposed = true;
464
+ try {
465
+ (_a = this.cancel) == null ? void 0 : _a.call(this);
466
+ } catch (e) {
467
+ }
468
+ }
469
+ };
470
+ var AppGitContext = AppGitContextImpl;
471
+ function createAppGitContext(options, runtime = {}) {
472
+ return new AppGitContext(options, runtime);
473
+ }
474
+ function withAppGitProperties(properties, snapshot) {
475
+ try {
476
+ const merged = { ...properties };
477
+ for (const key of APP_GIT_KEYS)
478
+ if (!own(merged, key) && own(snapshot, key)) merged[key] = snapshot[key];
479
+ return merged;
480
+ } catch (e) {
481
+ return properties != null ? properties : {};
482
+ }
483
+ }
484
+ function withAppGitAttributes(attributes, snapshot) {
485
+ try {
486
+ const present = new Set(attributes.flatMap((attr) => attr ? [attr.key] : []));
487
+ const added = [];
488
+ for (const key of APP_GIT_KEYS) {
489
+ if (present.has(key) || present.has(`ai.telemetry.metadata.${key}`) || present.has(`traceloop.association.properties.${key}`))
490
+ continue;
491
+ const value = snapshot[key];
492
+ if (typeof value === "string") added.push({ key, value: { stringValue: value } });
493
+ else if (typeof value === "boolean") added.push({ key, value: { boolValue: value } });
494
+ }
495
+ return added.length ? [...attributes, ...added] : attributes;
496
+ } catch (e) {
497
+ return attributes;
498
+ }
499
+ }
500
+ function appGitPropertiesFromAttributes(attributes) {
501
+ var _a, _b, _c;
502
+ const properties = {};
503
+ try {
504
+ for (const attr of attributes) {
505
+ if (!attr) continue;
506
+ for (const key of APP_GIT_KEYS) {
507
+ if (attr.key === key || attr.key === `ai.telemetry.metadata.${key}` || attr.key === `traceloop.association.properties.${key}`) {
508
+ properties[key] = (_c = (_b = (_a = attr.value.stringValue) != null ? _a : attr.value.boolValue) != null ? _b : attr.value.intValue) != null ? _c : null;
509
+ }
510
+ }
511
+ }
512
+ } catch (e) {
513
+ }
514
+ return properties;
515
+ }
2
516
  function getCrypto() {
3
517
  const c = globalThis.crypto;
4
518
  return c;
@@ -53,6 +567,25 @@ function base64Encode(bytes) {
53
567
  }
54
568
  return out;
55
569
  }
570
+ function base64ToHex(value) {
571
+ if (!value) return "";
572
+ const maybeBuffer = globalThis.Buffer;
573
+ if (maybeBuffer) {
574
+ return maybeBuffer.from(value, "base64").toString("hex");
575
+ }
576
+ const atobFn = globalThis.atob;
577
+ if (typeof atobFn !== "function") return "";
578
+ try {
579
+ const binary = atobFn(value);
580
+ let hex = "";
581
+ for (let i = 0; i < binary.length; i++) {
582
+ hex += (binary.charCodeAt(i) & 255).toString(16).padStart(2, "0");
583
+ }
584
+ return hex;
585
+ } catch (e) {
586
+ return "";
587
+ }
588
+ }
56
589
  function generateId() {
57
590
  return randomUUID();
58
591
  }
@@ -87,7 +620,7 @@ function runWithTracingSuppressed(fn) {
87
620
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
88
621
  var MAX_RETRY_DELAY_MS = 3e4;
89
622
  function wait(ms) {
90
- return new Promise((resolve) => setTimeout(resolve, ms));
623
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
91
624
  }
92
625
  function formatEndpoint(endpoint) {
93
626
  if (!endpoint) return void 0;
@@ -124,9 +657,9 @@ async function raceWithTimeout(promise, timeoutMs) {
124
657
  () => true,
125
658
  () => true
126
659
  ),
127
- new Promise((resolve) => {
660
+ new Promise((resolve2) => {
128
661
  var _a;
129
- timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
662
+ timer = setTimeout(() => resolve2(false), Math.max(0, timeoutMs));
130
663
  (_a = timer.unref) == null ? void 0 : _a.call(timer);
131
664
  })
132
665
  ]);
@@ -197,6 +730,7 @@ async function postJson(url, body, headers, opts) {
197
730
  const resp = await runWithTracingSuppressed(
198
731
  () => fetch(url, {
199
732
  method: "POST",
733
+ redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
200
734
  headers: {
201
735
  "Content-Type": "application/json",
202
736
  ...headers
@@ -267,6 +801,11 @@ function attrInt(key, value) {
267
801
  if (!Number.isFinite(value)) return void 0;
268
802
  return { key, value: { intValue: String(Math.trunc(value)) } };
269
803
  }
804
+ function attrDouble(key, value) {
805
+ if (value === void 0) return void 0;
806
+ if (!Number.isFinite(value)) return void 0;
807
+ return { key, value: { doubleValue: value } };
808
+ }
270
809
  function buildOtlpSpan(args) {
271
810
  const attrs = args.attributes.filter((x) => x !== void 0);
272
811
  const span = {
@@ -360,26 +899,29 @@ function resolveLocalDebuggerBaseUrl(baseUrl) {
360
899
  if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
361
900
  return null;
362
901
  }
902
+ var LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS = 2e3;
363
903
  function mirrorTraceExportToLocalDebugger(body, options = {}) {
364
904
  var _a;
365
905
  const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
366
- if (!baseUrl) return;
367
- void postJson(`${baseUrl}traces`, body, {}, {
906
+ if (!baseUrl) return Promise.resolve();
907
+ return postJson(`${baseUrl}traces`, body, {}, {
368
908
  maxAttempts: 1,
369
909
  debug: (_a = options.debug) != null ? _a : false,
370
- sdkName: options.sdkName
910
+ sdkName: options.sdkName,
911
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
371
912
  }).catch(() => {
372
913
  });
373
914
  }
374
915
  function mirrorPartialEventToLocalDebugger(event, options = {}) {
375
916
  var _a;
376
917
  const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
377
- if (!baseUrl) return;
918
+ if (!baseUrl) return Promise.resolve();
378
919
  const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
379
- void postJson(`${baseUrl}events/track_partial`, event, headers, {
920
+ return postJson(`${baseUrl}events/track_partial`, event, headers, {
380
921
  maxAttempts: 1,
381
922
  debug: (_a = options.debug) != null ? _a : false,
382
- sdkName: options.sdkName
923
+ sdkName: options.sdkName,
924
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
383
925
  }).catch(() => {
384
926
  });
385
927
  }
@@ -404,6 +946,13 @@ function projectIdHeaders(projectId) {
404
946
  }
405
947
  var SHUTDOWN_DEADLINE_MS = 1e4;
406
948
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
949
+ function appGitOwnerId(prefix) {
950
+ try {
951
+ return `${prefix}:${randomUUID()}`;
952
+ } catch (e) {
953
+ return `${prefix}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
954
+ }
955
+ }
407
956
  function mergePatches(target, source) {
408
957
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
409
958
  const out = { ...target, ...source };
@@ -453,22 +1002,25 @@ function eventTelemetryProperties(patch) {
453
1002
  }
454
1003
  var EventShipper = class {
455
1004
  constructor(opts) {
1005
+ this.appGitOwner = appGitOwnerId("event");
456
1006
  this.buffers = /* @__PURE__ */ new Map();
457
1007
  this.sticky = /* @__PURE__ */ new Map();
458
1008
  this.timers = /* @__PURE__ */ new Map();
459
1009
  this.inFlight = /* @__PURE__ */ new Set();
460
1010
  this.hasShutdown = false;
461
- var _a, _b, _c, _d, _e, _f, _g, _h;
462
- this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
463
- this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
1011
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1012
+ this.appGitContext = (_a = opts.appGitContext) != null ? _a : createAppGitContext(opts.appGit);
1013
+ this.ownsAppGitContext = opts.appGitContext === void 0;
1014
+ this.writeKey = (_b = opts.writeKey) == null ? void 0 : _b.trim();
1015
+ this.baseUrl = (_c = formatEndpoint(opts.endpoint)) != null ? _c : "https://api.raindrop.ai/v1/";
464
1016
  this.enabled = opts.enabled !== false;
465
1017
  this.debug = opts.debug;
466
- this.partialFlushMs = (_c = opts.partialFlushMs) != null ? _c : 1e3;
467
- this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
1018
+ this.partialFlushMs = (_d = opts.partialFlushMs) != null ? _d : 1e3;
1019
+ this.sdkName = (_e = opts.sdkName) != null ? _e : "core";
468
1020
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
469
- this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
1021
+ this.defaultEventName = (_f = opts.defaultEventName) != null ? _f : "ai_generation";
470
1022
  this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
471
- this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
1023
+ this.localDebuggerUrl = (_g = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _g : void 0;
472
1024
  if (this.debug && this.localDebuggerUrl) {
473
1025
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
474
1026
  }
@@ -479,8 +1031,8 @@ var EventShipper = class {
479
1031
  const isNode = typeof process !== "undefined" && typeof process.version === "string";
480
1032
  this.context = {
481
1033
  library: {
482
- name: (_g = opts.libraryName) != null ? _g : "@raindrop-ai/core",
483
- version: (_h = opts.libraryVersion) != null ? _h : "0.0.0"
1034
+ name: (_h = opts.libraryName) != null ? _h : "@raindrop-ai/core",
1035
+ version: (_i = opts.libraryVersion) != null ? _i : "0.0.0"
484
1036
  },
485
1037
  metadata: {
486
1038
  jsRuntime: isNode ? "node" : "web",
@@ -488,6 +1040,9 @@ var EventShipper = class {
488
1040
  }
489
1041
  };
490
1042
  }
1043
+ ownAppGitContextForShutdown() {
1044
+ this.ownsAppGitContext = true;
1045
+ }
491
1046
  isDebugEnabled() {
492
1047
  return this.debug;
493
1048
  }
@@ -536,6 +1091,15 @@ var EventShipper = class {
536
1091
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
537
1092
  if (!this.enabled) return;
538
1093
  if (!eventId || !eventId.trim()) return;
1094
+ const capturedSticky = this.sticky.get(eventId);
1095
+ const eventAlreadyKnown = capturedSticky !== void 0 || this.buffers.has(eventId);
1096
+ const destination = currentReplayTraceDestination();
1097
+ const cloudReplay = destination !== void 0 && destination.kind !== "workshop";
1098
+ if ((capturedSticky == null ? void 0 : capturedSticky.replayOnly) === true || !eventAlreadyKnown && cloudReplay) {
1099
+ if (patch.isPending === false) this.sticky.delete(eventId);
1100
+ else this.sticky.set(eventId, { ...capturedSticky, replayOnly: true });
1101
+ return;
1102
+ }
539
1103
  const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
540
1104
  if (typeof patch.input === "string" && patch.input.length > maxChars) {
541
1105
  patch = { ...patch, input: capText(patch.input, maxChars) };
@@ -557,6 +1121,13 @@ var EventShipper = class {
557
1121
  }
558
1122
  const sticky = (_c = this.sticky.get(eventId)) != null ? _c : {};
559
1123
  const existing = (_d = this.buffers.get(eventId)) != null ? _d : {};
1124
+ patch = {
1125
+ ...patch,
1126
+ properties: withAppGitProperties(
1127
+ patch.properties,
1128
+ this.appGitContext.forOperation(eventId, patch.properties, this.appGitOwner)
1129
+ )
1130
+ };
560
1131
  const merged = mergePatches(existing, patch);
561
1132
  merged.isPending = (_g = (_f = (_e = patch.isPending) != null ? _e : existing.isPending) != null ? _f : sticky.isPending) != null ? _g : true;
562
1133
  this.buffers.set(eventId, merged);
@@ -570,6 +1141,8 @@ var EventShipper = class {
570
1141
  if (t) clearTimeout(t);
571
1142
  if (merged.isPending === false) {
572
1143
  await this.flushOne(eventId);
1144
+ if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(eventId, this.appGitOwner);
1145
+ else this.appGitContext.finishOperation(eventId);
573
1146
  return;
574
1147
  }
575
1148
  const timeout = setTimeout(() => {
@@ -594,15 +1167,21 @@ var EventShipper = class {
594
1167
  try {
595
1168
  for (const t of this.timers.values()) clearTimeout(t);
596
1169
  this.timers.clear();
1170
+ for (const [eventId, sticky] of this.sticky) {
1171
+ if (sticky.replayOnly) this.sticky.delete(eventId);
1172
+ }
597
1173
  const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
598
1174
  if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
599
1175
  } finally {
600
1176
  this.shutdownDeadlineAt = void 0;
1177
+ if (this.ownsAppGitContext) this.appGitContext.dispose();
601
1178
  }
602
1179
  }
603
1180
  async trackSignal(signal) {
604
1181
  var _a, _b;
605
1182
  if (!this.enabled) return;
1183
+ const destination = currentReplayTraceDestination();
1184
+ if (destination && destination.kind !== "workshop") return;
606
1185
  const body = [
607
1186
  {
608
1187
  event_id: signal.eventId,
@@ -637,6 +1216,8 @@ var EventShipper = class {
637
1216
  }
638
1217
  async identify(users) {
639
1218
  if (!this.enabled) return;
1219
+ const destination = currentReplayTraceDestination();
1220
+ if (destination && destination.kind !== "workshop") return;
640
1221
  const list = Array.isArray(users) ? users : [users];
641
1222
  const body = list.filter((user) => {
642
1223
  if (!(user == null ? void 0 : user.userId) || !user.userId.trim()) {
@@ -689,8 +1270,9 @@ var EventShipper = class {
689
1270
  this.buffers.delete(eventId);
690
1271
  if (!accumulated) return;
691
1272
  const sticky = (_a = this.sticky.get(eventId)) != null ? _a : {};
692
- const eventName = (_c = (_b = accumulated.eventName) != null ? _b : sticky.eventName) != null ? _c : this.defaultEventName;
693
- const userId = (_d = accumulated.userId) != null ? _d : sticky.userId;
1273
+ const isPending = (_c = (_b = accumulated.isPending) != null ? _b : sticky.isPending) != null ? _c : true;
1274
+ const eventName = (_e = (_d = accumulated.eventName) != null ? _d : sticky.eventName) != null ? _e : this.defaultEventName;
1275
+ const userId = (_f = accumulated.userId) != null ? _f : sticky.userId;
694
1276
  if (!userId) {
695
1277
  if (this.debug) {
696
1278
  console.warn(`${this.prefix} skipping track_partial for ${eventId}: missing userId`);
@@ -698,14 +1280,13 @@ var EventShipper = class {
698
1280
  this.sticky.delete(eventId);
699
1281
  return;
700
1282
  }
701
- const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
1283
+ const { wizardSession, ...restProperties } = (_g = accumulated.properties) != null ? _g : {};
702
1284
  let telemetryProperties = {};
703
1285
  try {
704
1286
  telemetryProperties = eventTelemetryProperties(accumulated);
705
1287
  } catch (e) {
706
1288
  }
707
- const convoId = (_f = accumulated.convoId) != null ? _f : sticky.convoId;
708
- const isPending = (_h = (_g = accumulated.isPending) != null ? _g : sticky.isPending) != null ? _h : true;
1289
+ const convoId = (_h = accumulated.convoId) != null ? _h : sticky.convoId;
709
1290
  const payload = {
710
1291
  event_id: eventId,
711
1292
  user_id: userId,
@@ -747,29 +1328,30 @@ var EventShipper = class {
747
1328
  endpoint: url
748
1329
  });
749
1330
  }
750
- if (this.localDebuggerUrl) {
751
- mirrorPartialEventToLocalDebugger(payload, {
752
- baseUrl: this.localDebuggerUrl,
753
- writeKey: this.writeKey,
754
- debug: this.debug,
755
- sdkName: this.sdkName
756
- });
757
- }
1331
+ const mirror = this.localDebuggerUrl ? mirrorPartialEventToLocalDebugger(payload, {
1332
+ baseUrl: this.localDebuggerUrl,
1333
+ writeKey: this.writeKey,
1334
+ debug: this.debug,
1335
+ sdkName: this.sdkName
1336
+ }) : void 0;
758
1337
  if (!this.writeKey) {
759
1338
  if (!isPending) this.sticky.delete(eventId);
1339
+ if (mirror) await mirror;
760
1340
  return;
761
1341
  }
762
1342
  const opts = this.requestOpts();
763
1343
  if (!opts) {
764
1344
  this.warnShutdownDrop(`track_partial ${eventId}`);
765
1345
  if (!isPending) this.sticky.delete(eventId);
1346
+ if (mirror) await mirror;
766
1347
  return;
767
1348
  }
768
1349
  const p = postJson(url, payload, this.requestHeaders(), opts);
769
- this.inFlight.add(p);
1350
+ const combined = mirror ? Promise.all([mirror, p]).then(() => void 0) : p;
1351
+ this.inFlight.add(combined);
770
1352
  try {
771
1353
  try {
772
- await p;
1354
+ await combined;
773
1355
  if (this.debug) {
774
1356
  console.log(`${this.prefix} sent track_partial ${eventId} (${eventName})`);
775
1357
  }
@@ -781,7 +1363,7 @@ var EventShipper = class {
781
1363
  );
782
1364
  }
783
1365
  } finally {
784
- this.inFlight.delete(p);
1366
+ this.inFlight.delete(combined);
785
1367
  }
786
1368
  if (!isPending) {
787
1369
  this.sticky.delete(eventId);
@@ -789,6 +1371,7 @@ var EventShipper = class {
789
1371
  }
790
1372
  };
791
1373
  var MODEL_USAGE_ATTRIBUTES = {
1374
+ usageSource: "raindrop.usage.source",
792
1375
  providerName: "gen_ai.provider.name",
793
1376
  requestModel: "gen_ai.request.model",
794
1377
  responseModel: "gen_ai.response.model",
@@ -797,6 +1380,7 @@ var MODEL_USAGE_ATTRIBUTES = {
797
1380
  reasoningTokens: "gen_ai.usage.reasoning_tokens",
798
1381
  cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
799
1382
  cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
1383
+ providerReportedCost: "gen_ai.usage.provider_reported_cost",
800
1384
  nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
801
1385
  nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
802
1386
  totalOutputTokens: "raindrop.usage.output_tokens.total",
@@ -805,13 +1389,20 @@ var MODEL_USAGE_ATTRIBUTES = {
805
1389
  cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
806
1390
  };
807
1391
  var MODEL_PROVIDER_NAMES = [
1392
+ ["google.vertex.anthropic", "google-vertex"],
1393
+ ["google.vertex_ai", "google-vertex"],
808
1394
  ["google.vertex", "google-vertex"],
1395
+ ["gcp.vertex_ai", "google-vertex"],
1396
+ ["gcp.gemini", "google"],
1397
+ ["google_vertexai", "google-vertex"],
809
1398
  ["vertex.anthropic", "google-vertex"],
810
1399
  ["amazon-bedrock", "amazon-bedrock"],
1400
+ ["aws.bedrock", "amazon-bedrock"],
811
1401
  ["bedrock", "amazon-bedrock"],
812
1402
  ["anthropic", "anthropic"],
813
1403
  ["openai", "openai"],
814
1404
  ["google", "google"],
1405
+ ["az.ai.openai", "azure"],
815
1406
  ["azure", "azure"],
816
1407
  ["openrouter", "openrouter"]
817
1408
  ];
@@ -822,60 +1413,20 @@ function canonicalModelProvider(provider) {
822
1413
  const match = MODEL_PROVIDER_NAMES.find(
823
1414
  ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
824
1415
  );
825
- return (_a = match == null ? void 0 : match[1]) != null ? _a : provider;
1416
+ return (_a = match == null ? void 0 : match[1]) != null ? _a : normalizedProvider;
826
1417
  }
827
1418
  function tokenCount(value) {
828
1419
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
829
1420
  }
830
- function exclusiveTokens({
831
- explicit,
832
- total,
833
- parts,
834
- semantics
835
- }) {
836
- if (explicit !== void 0) return explicit;
837
- if (total === void 0) return void 0;
838
- if (semantics === "exclusive") return total;
839
- const observedParts = parts.filter((part) => part !== void 0 && part > 0);
840
- if (semantics === void 0 && observedParts.length > 0) return void 0;
841
- const exclusive = total - observedParts.reduce((sum, part) => sum + part, 0);
842
- return exclusive >= 0 ? exclusive : void 0;
843
- }
844
- function buildModelUsageAttributes(usage) {
845
- const inputTokens = tokenCount(usage.inputTokens);
846
- const outputTokens = tokenCount(usage.outputTokens);
847
- const explicitNonCachedInputTokens = tokenCount(usage.nonCachedInputTokens);
848
- const explicitNonReasoningOutputTokens = tokenCount(usage.nonReasoningOutputTokens);
849
- const reasoningTokens = tokenCount(usage.reasoningTokens);
850
- const cacheReadInputTokens = tokenCount(usage.cacheReadInputTokens);
851
- const cacheWriteInputTokens = tokenCount(usage.cacheWriteInputTokens);
852
- const nonCachedInputTokens = exclusiveTokens({
853
- explicit: explicitNonCachedInputTokens,
854
- total: inputTokens,
855
- parts: [cacheReadInputTokens, cacheWriteInputTokens],
856
- semantics: usage.inputTokenSemantics
857
- });
858
- const nonReasoningOutputTokens = exclusiveTokens({
859
- explicit: explicitNonReasoningOutputTokens,
860
- total: outputTokens,
861
- parts: [reasoningTokens],
862
- semantics: usage.outputTokenSemantics
863
- });
864
- const exclusivePoolAttributes = nonCachedInputTokens !== void 0 && nonReasoningOutputTokens !== void 0 ? [
865
- // backend consumes this complete raindrop.* set as one atomic billing-pool contract.
866
- attrInt(MODEL_USAGE_ATTRIBUTES.nonCachedInputTokens, nonCachedInputTokens),
867
- attrInt(MODEL_USAGE_ATTRIBUTES.nonReasoningOutputTokens, nonReasoningOutputTokens),
868
- attrInt(MODEL_USAGE_ATTRIBUTES.reasoningOutputTokens, reasoningTokens),
869
- attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadTokens, cacheReadInputTokens),
870
- attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteTokens, cacheWriteInputTokens)
871
- ] : [];
1421
+ function buildRawModelUsageAttributes(usage) {
1422
+ const providerReportedCost = typeof usage.providerReportedCost === "number" && Number.isFinite(usage.providerReportedCost) && usage.providerReportedCost >= 0 ? usage.providerReportedCost : void 0;
872
1423
  return [
873
- attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, inputTokens),
874
- attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, outputTokens),
875
- attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, reasoningTokens),
876
- attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, cacheReadInputTokens),
877
- attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, cacheWriteInputTokens),
878
- ...exclusivePoolAttributes
1424
+ attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, tokenCount(usage.inputTokens)),
1425
+ attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, tokenCount(usage.outputTokens)),
1426
+ attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, tokenCount(usage.reasoningTokens)),
1427
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, tokenCount(usage.cacheReadInputTokens)),
1428
+ attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, tokenCount(usage.cacheWriteInputTokens)),
1429
+ attrDouble(MODEL_USAGE_ATTRIBUTES.providerReportedCost, providerReportedCost)
879
1430
  ].filter((attribute) => attribute !== void 0);
880
1431
  }
881
1432
  var STRING_ALIASES = {
@@ -1065,6 +1616,13 @@ function redactJsonAttributeValue(key, value) {
1065
1616
  if (scrubbedJson === json) return void 0;
1066
1617
  return { stringValue: scrubbedJson };
1067
1618
  }
1619
+ function appGitOwnerId2(prefix) {
1620
+ try {
1621
+ return `${prefix}:${randomUUID()}`;
1622
+ } catch (e) {
1623
+ return `${prefix}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
1624
+ }
1625
+ }
1068
1626
  function identityAttrs(args) {
1069
1627
  return [
1070
1628
  attrString("ai.telemetry.metadata.raindrop.ai.userId", args.userId),
@@ -1087,23 +1645,30 @@ function applyOtelSpanAttributeLimit(limit) {
1087
1645
  }
1088
1646
  var TraceShipper = class {
1089
1647
  constructor(opts) {
1648
+ this.appGitOwner = appGitOwnerId2("trace");
1649
+ this.appGitEventIds = /* @__PURE__ */ new WeakMap();
1650
+ this.appGitActiveSpans = /* @__PURE__ */ new Map();
1651
+ this.appGitCaptured = /* @__PURE__ */ new WeakSet();
1652
+ this.appGitAdded = /* @__PURE__ */ new WeakMap();
1090
1653
  this.queue = [];
1091
1654
  this.inFlight = /* @__PURE__ */ new Set();
1092
1655
  this.hasShutdown = false;
1093
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1094
- this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
1095
- this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
1656
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1657
+ this.appGitContext = (_a = opts.appGitContext) != null ? _a : createAppGitContext(opts.appGit);
1658
+ this.ownsAppGitContext = opts.appGitContext === void 0;
1659
+ this.writeKey = (_b = opts.writeKey) == null ? void 0 : _b.trim();
1660
+ this.baseUrl = (_c = formatEndpoint(opts.endpoint)) != null ? _c : "https://api.raindrop.ai/v1/";
1096
1661
  this.enabled = opts.enabled !== false;
1097
1662
  this.debug = opts.debug;
1098
1663
  this.debugSpans = opts.debugSpans === true;
1099
- this.flushIntervalMs = (_c = opts.flushIntervalMs) != null ? _c : 1e3;
1100
- this.maxBatchSize = (_d = opts.maxBatchSize) != null ? _d : 50;
1101
- this.maxQueueSize = (_e = opts.maxQueueSize) != null ? _e : 5e3;
1102
- this.sdkName = (_f = opts.sdkName) != null ? _f : "core";
1664
+ this.flushIntervalMs = (_d = opts.flushIntervalMs) != null ? _d : 1e3;
1665
+ this.maxBatchSize = (_e = opts.maxBatchSize) != null ? _e : 50;
1666
+ this.maxQueueSize = (_f = opts.maxQueueSize) != null ? _f : 5e3;
1667
+ this.sdkName = (_g = opts.sdkName) != null ? _g : "core";
1103
1668
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
1104
- this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
1105
- this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
1106
- this.localDebuggerUrl = (_i = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _i : void 0;
1669
+ this.serviceName = (_h = opts.serviceName) != null ? _h : "raindrop.core";
1670
+ this.serviceVersion = (_i = opts.serviceVersion) != null ? _i : "0.0.0";
1671
+ this.localDebuggerUrl = (_j = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _j : void 0;
1107
1672
  if (this.debug && this.localDebuggerUrl) {
1108
1673
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
1109
1674
  }
@@ -1167,7 +1732,10 @@ var TraceShipper = class {
1167
1732
  try {
1168
1733
  const result = this.transformSpanHook(current);
1169
1734
  if (result === null) return null;
1170
- if (result !== void 0) current = result;
1735
+ if (result !== void 0) {
1736
+ copyReplayTraceDestination(current, result);
1737
+ current = result;
1738
+ }
1171
1739
  } catch (err) {
1172
1740
  if (this.debug) {
1173
1741
  const msg = err instanceof Error ? err.message : String(err);
@@ -1177,13 +1745,19 @@ var TraceShipper = class {
1177
1745
  }
1178
1746
  }
1179
1747
  try {
1180
- current = normalizeModelUsageSpan(current);
1748
+ const normalized = normalizeModelUsageSpan(current);
1749
+ copyReplayTraceDestination(current, normalized);
1750
+ current = normalized;
1181
1751
  } catch (e) {
1182
1752
  }
1183
1753
  if (!this.disableDefaultRedaction) {
1184
- current = defaultTransformSpan(current);
1754
+ const redacted = defaultTransformSpan(current);
1755
+ copyReplayTraceDestination(current, redacted);
1756
+ current = redacted;
1185
1757
  }
1186
- return this.capSpanAttributes(current);
1758
+ const capped = this.capSpanAttributes(current);
1759
+ copyReplayTraceDestination(current, capped);
1760
+ return capped;
1187
1761
  }
1188
1762
  isDebugEnabled() {
1189
1763
  return this.debug;
@@ -1195,46 +1769,84 @@ var TraceShipper = class {
1195
1769
  return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
1196
1770
  }
1197
1771
  startSpan(args) {
1198
- var _a, _b;
1772
+ var _a, _b, _c;
1199
1773
  const ids = createSpanIds(args.parent);
1200
1774
  const started = (_a = args.startTimeUnixNano) != null ? _a : nowUnixNanoString();
1201
1775
  const attrs = [
1202
1776
  attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
1203
1777
  ...identityAttrs(args),
1204
- attrString("ai.operationId", args.operationId)
1778
+ attrString("ai.operationId", args.operationId),
1779
+ ...Object.entries(evalScopeAttributes()).map(([key, value]) => attrString(key, value))
1205
1780
  ];
1206
1781
  if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
1207
- const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
1208
- this.mirrorToLocalDebugger(
1209
- buildOtlpSpan({
1210
- ids: span.ids,
1211
- name: span.name,
1212
- startTimeUnixNano: span.startTimeUnixNano,
1213
- endTimeUnixNano: span.startTimeUnixNano,
1214
- // placeholder — will be updated on endSpan
1215
- attributes: span.attributes,
1216
- status: { code: SpanStatusCode.UNSET }
1217
- })
1218
- );
1782
+ const tracksAppGit = this.appGitActiveSpans.has(args.eventId) || this.appGitActiveSpans.size < 1e4;
1783
+ const appGitOverrides = appGitPropertiesFromAttributes(attrs);
1784
+ const span = {
1785
+ ids,
1786
+ name: args.name,
1787
+ startTimeUnixNano: started,
1788
+ attributes: withAppGitAttributes(
1789
+ attrs,
1790
+ this.appGitContext.forOperation(args.eventId, appGitOverrides, this.appGitOwner)
1791
+ )
1792
+ };
1793
+ rememberReplayTraceDestination(span);
1794
+ this.appGitCaptured.add(span);
1795
+ this.appGitEventIds.set(span, args.eventId);
1796
+ if (tracksAppGit) {
1797
+ this.appGitActiveSpans.set(args.eventId, ((_c = this.appGitActiveSpans.get(args.eventId)) != null ? _c : 0) + 1);
1798
+ }
1799
+ this.appGitAdded.set(span, new Set(span.attributes.filter((attr) => !attrs.includes(attr))));
1800
+ const openSpan = buildOtlpSpan({
1801
+ ids: span.ids,
1802
+ name: span.name,
1803
+ startTimeUnixNano: span.startTimeUnixNano,
1804
+ endTimeUnixNano: span.startTimeUnixNano,
1805
+ // placeholder — will be updated on endSpan
1806
+ attributes: span.attributes,
1807
+ status: { code: SpanStatusCode.UNSET }
1808
+ });
1809
+ copyReplayTraceDestination(span, openSpan);
1810
+ this.mirrorToLocalDebugger(openSpan);
1219
1811
  return span;
1220
1812
  }
1221
- mirrorToLocalDebugger(span) {
1222
- if (!this.localDebuggerUrl) return;
1813
+ mirrorToLocalDebugger(span, completed = false) {
1814
+ const destination = replayTraceDestinationFor(span);
1815
+ if (destination && destination.kind !== "workshop") return;
1816
+ const localDebuggerUrl = (destination == null ? void 0 : destination.kind) === "workshop" ? destination.url : this.localDebuggerUrl;
1817
+ if (!localDebuggerUrl) return;
1223
1818
  const redacted = this.redactSpan(span);
1224
1819
  if (redacted === null) return;
1820
+ if (completed && (destination == null ? void 0 : destination.kind) === "workshop") {
1821
+ recordReplayExportedSpan(
1822
+ destination,
1823
+ base64ToHex(redacted.spanId),
1824
+ base64ToHex(redacted.traceId)
1825
+ );
1826
+ }
1225
1827
  const body = buildExportTraceServiceRequest([redacted], this.serviceName, this.serviceVersion);
1226
- mirrorTraceExportToLocalDebugger(body, {
1227
- baseUrl: this.localDebuggerUrl,
1828
+ const p = mirrorTraceExportToLocalDebugger(body, {
1829
+ baseUrl: localDebuggerUrl,
1228
1830
  debug: false,
1229
1831
  sdkName: this.sdkName
1230
1832
  });
1833
+ this.inFlight.add(p);
1834
+ void p.finally(() => {
1835
+ this.inFlight.delete(p);
1836
+ });
1231
1837
  }
1232
1838
  endSpan(span, extra) {
1233
- var _a, _b;
1839
+ var _a, _b, _c;
1234
1840
  if (span.endTimeUnixNano) return;
1235
1841
  span.endTimeUnixNano = (_a = extra == null ? void 0 : extra.endTimeUnixNano) != null ? _a : nowUnixNanoString();
1236
1842
  if ((_b = extra == null ? void 0 : extra.attributes) == null ? void 0 : _b.length) {
1843
+ const overrides = appGitPropertiesFromAttributes(extra.attributes);
1844
+ const eventId2 = this.appGitEventIds.get(span);
1845
+ const snapshot = eventId2 ? this.appGitContext.forOperation(eventId2, overrides, this.appGitOwner) : overrides;
1846
+ const added = this.appGitAdded.get(span);
1847
+ 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));
1237
1848
  span.attributes.push(...extra.attributes);
1849
+ span.attributes = withAppGitAttributes(span.attributes, snapshot);
1238
1850
  }
1239
1851
  let status = extra == null ? void 0 : extra.status;
1240
1852
  if (!status && (extra == null ? void 0 : extra.error) !== void 0) {
@@ -1249,30 +1861,65 @@ var TraceShipper = class {
1249
1861
  attributes: span.attributes,
1250
1862
  status
1251
1863
  });
1864
+ copyReplayTraceDestination(span, otlp);
1865
+ this.appGitCaptured.add(otlp);
1252
1866
  this.enqueue(otlp);
1253
- this.mirrorToLocalDebugger(otlp);
1867
+ this.mirrorToLocalDebugger(otlp, true);
1868
+ const eventId = this.appGitEventIds.get(span);
1869
+ if (eventId && this.appGitActiveSpans.has(eventId)) {
1870
+ const remaining = ((_c = this.appGitActiveSpans.get(eventId)) != null ? _c : 1) - 1;
1871
+ if (remaining > 0) this.appGitActiveSpans.set(eventId, remaining);
1872
+ else {
1873
+ this.appGitActiveSpans.delete(eventId);
1874
+ if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(eventId, this.appGitOwner);
1875
+ else if (this.ownsAppGitContext) this.appGitContext.finishOperation(eventId);
1876
+ }
1877
+ }
1254
1878
  }
1255
1879
  createSpan(args) {
1256
1880
  var _a;
1257
1881
  const ids = createSpanIds(args.parent);
1258
1882
  const attrs = [
1259
1883
  attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
1260
- ...identityAttrs(args)
1884
+ ...identityAttrs(args),
1885
+ ...Object.entries(evalScopeAttributes()).map(([key, value]) => attrString(key, value))
1261
1886
  ];
1262
1887
  if ((_a = args.attributes) == null ? void 0 : _a.length) attrs.push(...args.attributes);
1888
+ const tracksAppGit = !this.appGitActiveSpans.has(args.eventId);
1263
1889
  const otlp = buildOtlpSpan({
1264
1890
  ids,
1265
1891
  name: args.name,
1266
1892
  startTimeUnixNano: args.startTimeUnixNano,
1267
1893
  endTimeUnixNano: args.endTimeUnixNano,
1268
- attributes: attrs,
1894
+ attributes: withAppGitAttributes(
1895
+ attrs,
1896
+ this.appGitContext.forOperation(
1897
+ args.eventId,
1898
+ appGitPropertiesFromAttributes(attrs),
1899
+ this.appGitOwner
1900
+ )
1901
+ ),
1269
1902
  status: args.status
1270
1903
  });
1904
+ rememberReplayTraceDestination(otlp);
1905
+ this.appGitCaptured.add(otlp);
1906
+ if (tracksAppGit) {
1907
+ if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(args.eventId, this.appGitOwner);
1908
+ else if (this.ownsAppGitContext) this.appGitContext.finishOperation(args.eventId);
1909
+ }
1271
1910
  this.enqueue(otlp);
1272
- this.mirrorToLocalDebugger(otlp);
1911
+ this.mirrorToLocalDebugger(otlp, true);
1273
1912
  }
1274
1913
  enqueue(span) {
1914
+ var _a;
1275
1915
  if (!this.enabled) return;
1916
+ const attributes = (_a = span.attributes) != null ? _a : [];
1917
+ const enriched = this.appGitCaptured.has(span) ? attributes : withAppGitAttributes(attributes, this.appGitContext.snapshot(appGitPropertiesFromAttributes(attributes)));
1918
+ if (enriched !== attributes) {
1919
+ const original = span;
1920
+ span = { ...span, attributes: enriched.filter((attr) => attr !== void 0) };
1921
+ copyReplayTraceDestination(original, span);
1922
+ }
1276
1923
  if (this.debugSpans) {
1277
1924
  const short = (s) => s ? s.slice(-8) : "none";
1278
1925
  console.log(
@@ -1283,6 +1930,13 @@ var TraceShipper = class {
1283
1930
  }
1284
1931
  const redacted = this.redactSpan(span);
1285
1932
  if (redacted === null) return;
1933
+ const destination = replayTraceDestinationFor(redacted);
1934
+ recordReplayExportedSpan(
1935
+ destination,
1936
+ base64ToHex(redacted.spanId),
1937
+ base64ToHex(redacted.traceId)
1938
+ );
1939
+ if ((destination == null ? void 0 : destination.kind) === "workshop") return;
1286
1940
  if (this.queue.length >= this.maxQueueSize) {
1287
1941
  this.queue.shift();
1288
1942
  }
@@ -1301,47 +1955,67 @@ var TraceShipper = class {
1301
1955
  }
1302
1956
  }
1303
1957
  async flush() {
1304
- if (!this.enabled) return;
1958
+ var _a;
1959
+ if (!this.enabled) {
1960
+ await Promise.all([...this.inFlight].map((p) => p.catch(() => {
1961
+ })));
1962
+ return;
1963
+ }
1305
1964
  if (this.timer) {
1306
1965
  clearTimeout(this.timer);
1307
1966
  this.timer = void 0;
1308
1967
  }
1309
1968
  while (this.queue.length > 0) {
1310
1969
  const batch = this.queue.splice(0, this.maxBatchSize);
1311
- if (!this.writeKey) continue;
1312
- const opts = this.requestOpts();
1313
- if (!opts) {
1314
- rateLimitedLog(
1315
- `${this.prefix}.shutdown_deadline`,
1316
- () => console.warn(
1317
- `${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
1318
- )
1319
- );
1320
- continue;
1321
- }
1322
- const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
1323
- const url = `${this.baseUrl}traces`;
1324
- if (this.debug) {
1325
- console.log(`${this.prefix} sending traces batch`, {
1326
- spans: batch.length,
1327
- endpoint: url
1328
- });
1970
+ const groups = /* @__PURE__ */ new Map();
1971
+ for (const span of batch) {
1972
+ const destination = replayTraceDestinationFor(span);
1973
+ const spans = groups.get(destination);
1974
+ if (spans) spans.push(span);
1975
+ else groups.set(destination, [span]);
1329
1976
  }
1330
- const p = postJson(url, body, this.requestHeaders(), opts);
1331
- this.inFlight.add(p);
1332
- try {
1333
- try {
1334
- await p;
1335
- if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
1336
- } catch (err) {
1337
- const msg = err instanceof Error ? err.message : String(err);
1977
+ for (const [destination, spans] of groups) {
1978
+ if (!destination && !this.writeKey) continue;
1979
+ if ((destination == null ? void 0 : destination.kind) === "workshop") continue;
1980
+ const opts = this.requestOpts();
1981
+ if (!opts) {
1338
1982
  rateLimitedLog(
1339
- `${this.prefix}.send_spans_failed`,
1340
- () => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
1983
+ `${this.prefix}.shutdown_deadline`,
1984
+ () => console.warn(
1985
+ `${this.prefix} shutdown flush deadline exceeded; dropping ${spans.length} spans`
1986
+ )
1341
1987
  );
1988
+ continue;
1989
+ }
1990
+ const body = buildExportTraceServiceRequest(spans, this.serviceName, this.serviceVersion);
1991
+ const url = destination ? replayTraceUrl(destination.url) : `${this.baseUrl}traces`;
1992
+ const headers = destination ? {
1993
+ Authorization: `Bearer ${destination.apiKey}`,
1994
+ "X-Raindrop-Replay-Id": destination.replayId,
1995
+ "X-Raindrop-Project-Id": (_a = destination.projectId) != null ? _a : "default"
1996
+ } : this.requestHeaders();
1997
+ if (this.debug) {
1998
+ console.log(`${this.prefix} sending traces batch`, {
1999
+ spans: spans.length,
2000
+ endpoint: url
2001
+ });
2002
+ }
2003
+ const p = postJson(url, body, headers, opts);
2004
+ this.inFlight.add(p);
2005
+ try {
2006
+ try {
2007
+ await p;
2008
+ if (this.debug) console.log(`${this.prefix} sent ${spans.length} spans`);
2009
+ } catch (err) {
2010
+ const msg = err instanceof Error ? err.message : String(err);
2011
+ rateLimitedLog(
2012
+ `${this.prefix}.send_spans_failed`,
2013
+ () => console.warn(`${this.prefix} failed to send ${spans.length} spans: ${msg}`)
2014
+ );
2015
+ }
2016
+ } finally {
2017
+ this.inFlight.delete(p);
1342
2018
  }
1343
- } finally {
1344
- this.inFlight.delete(p);
1345
2019
  }
1346
2020
  }
1347
2021
  }
@@ -1384,17 +2058,165 @@ var TraceShipper = class {
1384
2058
  if (!settled) {
1385
2059
  rateLimitedLog(
1386
2060
  `${this.prefix}.shutdown_deadline`,
1387
- () => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
2061
+ () => console.warn(
2062
+ `${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`
2063
+ )
1388
2064
  );
1389
2065
  }
1390
2066
  } finally {
1391
2067
  this.shutdownDeadlineAt = void 0;
2068
+ if (this.ownsAppGitContext) this.appGitContext.dispose();
1392
2069
  }
1393
2070
  }
1394
2071
  };
1395
2072
 
1396
2073
  // ../core/dist/index.node.js
1397
2074
  import { AsyncLocalStorage } from "async_hooks";
2075
+ import { execFile } from "child_process";
2076
+ import { resolve } from "path";
2077
+ var resolveLocal = (sourceDirectory, detectBranch, completed) => {
2078
+ const cwd = sourceDirectory ? resolve(sourceDirectory) : process.cwd();
2079
+ let cancelled = false;
2080
+ let active;
2081
+ let deadlineTimer;
2082
+ const deadline = Date.now() + 1e3;
2083
+ const run = (args, done) => {
2084
+ if (cancelled) return;
2085
+ const remaining = deadline - Date.now();
2086
+ if (remaining <= 0) {
2087
+ done(void 0);
2088
+ return;
2089
+ }
2090
+ try {
2091
+ active = execFile(
2092
+ "git",
2093
+ args,
2094
+ {
2095
+ cwd,
2096
+ encoding: "utf8",
2097
+ maxBuffer: 4096,
2098
+ windowsHide: true,
2099
+ env: {
2100
+ ...process.env,
2101
+ // The selected application cwd, not the invoking shell's Git plumbing, owns identity.
2102
+ GIT_DIR: void 0,
2103
+ GIT_WORK_TREE: void 0,
2104
+ GIT_COMMON_DIR: void 0,
2105
+ GIT_NAMESPACE: void 0,
2106
+ GIT_INDEX_FILE: void 0,
2107
+ GIT_OBJECT_DIRECTORY: void 0,
2108
+ GIT_ALTERNATE_OBJECT_DIRECTORIES: void 0,
2109
+ GIT_CONFIG_PARAMETERS: void 0,
2110
+ GIT_CONFIG_COUNT: "0",
2111
+ GIT_TERMINAL_PROMPT: "0",
2112
+ GIT_OPTIONAL_LOCKS: "0"
2113
+ }
2114
+ },
2115
+ (error, stdout) => {
2116
+ clearTimeout(deadlineTimer);
2117
+ deadlineTimer = void 0;
2118
+ active = void 0;
2119
+ if (!cancelled) {
2120
+ try {
2121
+ done(error ? void 0 : stdout.trim());
2122
+ } catch (e) {
2123
+ }
2124
+ }
2125
+ }
2126
+ );
2127
+ active.unref();
2128
+ if (active.stdout && "unref" in active.stdout && typeof active.stdout.unref === "function")
2129
+ active.stdout.unref();
2130
+ if (active.stderr && "unref" in active.stderr && typeof active.stderr.unref === "function")
2131
+ active.stderr.unref();
2132
+ deadlineTimer = setTimeout(() => {
2133
+ try {
2134
+ active == null ? void 0 : active.kill("SIGKILL");
2135
+ } catch (e) {
2136
+ }
2137
+ }, remaining);
2138
+ deadlineTimer.unref();
2139
+ } catch (e) {
2140
+ try {
2141
+ done(void 0);
2142
+ } catch (e2) {
2143
+ }
2144
+ }
2145
+ };
2146
+ const scheduled = setTimeout(() => {
2147
+ try {
2148
+ run(["rev-parse", "HEAD"], (rawSha) => {
2149
+ const commitSha = validAppCommitSha(rawSha);
2150
+ if (!commitSha) {
2151
+ completed({});
2152
+ return;
2153
+ }
2154
+ run(["-c", "core.fsmonitor=false", "status", "--porcelain"], (status) => {
2155
+ const finish = (branch) => {
2156
+ run(["rev-parse", "HEAD"], (finalSha) => {
2157
+ const coherent = validAppCommitSha(finalSha) === commitSha;
2158
+ completed({
2159
+ commitSha,
2160
+ ...coherent && status !== void 0 ? { commitDirty: status !== "" } : {},
2161
+ ...coherent && branch && branch !== "HEAD" ? { branch } : {}
2162
+ });
2163
+ });
2164
+ };
2165
+ if (detectBranch) run(["symbolic-ref", "--quiet", "--short", "HEAD"], finish);
2166
+ else finish();
2167
+ });
2168
+ });
2169
+ } catch (e) {
2170
+ completed({});
2171
+ }
2172
+ }, 0);
2173
+ scheduled.unref();
2174
+ return () => {
2175
+ cancelled = true;
2176
+ clearTimeout(scheduled);
2177
+ clearTimeout(deadlineTimer);
2178
+ try {
2179
+ active == null ? void 0 : active.kill("SIGKILL");
2180
+ } catch (e) {
2181
+ }
2182
+ };
2183
+ };
2184
+ function createAppGitContext2(options, runtime = {}) {
2185
+ return new AppGitContext(options, { ...runtime, resolveLocal });
2186
+ }
2187
+ function hasDestination(options) {
2188
+ var _a;
2189
+ if (options.enabled === false) return false;
2190
+ return Boolean(((_a = options.writeKey) == null ? void 0 : _a.trim()) || resolveLocalDebuggerBaseUrl(options.localDebuggerUrl));
2191
+ }
2192
+ var EventShipper2 = class extends EventShipper {
2193
+ constructor(options) {
2194
+ var _a;
2195
+ const ownsAppGitContext = options.appGitContext === void 0;
2196
+ const appGitContext = (_a = options.appGitContext) != null ? _a : createAppGitContext2(options.appGit, { allowDiscovery: hasDestination(options) });
2197
+ try {
2198
+ super({ ...options, appGitContext });
2199
+ } catch (error) {
2200
+ if (ownsAppGitContext) appGitContext.dispose();
2201
+ throw error;
2202
+ }
2203
+ if (ownsAppGitContext) this.ownAppGitContextForShutdown();
2204
+ }
2205
+ };
2206
+ var TraceShipper2 = class extends TraceShipper {
2207
+ constructor(options) {
2208
+ var _a;
2209
+ const ownsAppGitContext = options.appGitContext === void 0;
2210
+ const appGitContext = (_a = options.appGitContext) != null ? _a : createAppGitContext2(options.appGit, { allowDiscovery: hasDestination(options) });
2211
+ try {
2212
+ super({ ...options, appGitContext });
2213
+ } catch (error) {
2214
+ if (ownsAppGitContext) appGitContext.dispose();
2215
+ throw error;
2216
+ }
2217
+ this.ownsAppGitContext = ownsAppGitContext;
2218
+ }
2219
+ };
1398
2220
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
1399
2221
  var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
1400
2222
  "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
@@ -1459,7 +2281,20 @@ function loadConfig(projectDirectory) {
1459
2281
  debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_g = merged.debug) != null ? _g : false,
1460
2282
  captureSystemPrompt: process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] !== void 0 ? process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] === "true" : (_h = merged.capture_system_prompt) != null ? _h : false,
1461
2283
  eventMetadata,
1462
- localWorkshopUrl: resolveLocalWorkshopUrl(merged.local_workshop_url)
2284
+ localWorkshopUrl: resolveLocalWorkshopUrl(merged.local_workshop_url),
2285
+ appGit: mapAppGit(merged.app_git)
2286
+ };
2287
+ }
2288
+ function mapAppGit(value) {
2289
+ if (value === false) return false;
2290
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
2291
+ return {
2292
+ commitSha: "commit_sha" in value && (typeof value.commit_sha === "string" || value.commit_sha === null) ? value.commit_sha : void 0,
2293
+ commitDirty: "commit_dirty" in value && (typeof value.commit_dirty === "boolean" || value.commit_dirty === null) ? value.commit_dirty : void 0,
2294
+ branch: "branch" in value && (typeof value.branch === "string" || value.branch === null) ? value.branch : void 0,
2295
+ detectBranch: "detect_branch" in value && typeof value.detect_branch === "boolean" ? value.detect_branch : void 0,
2296
+ sourceDirectory: "source_directory" in value && typeof value.source_directory === "string" ? value.source_directory : void 0,
2297
+ autoDetect: "auto_detect" in value && typeof value.auto_detect === "boolean" ? value.auto_detect : void 0
1463
2298
  };
1464
2299
  }
1465
2300
  function resolveLocalWorkshopUrl(fileValue) {
@@ -1476,7 +2311,7 @@ function resolveLocalWorkshopUrl(fileValue) {
1476
2311
  // package.json
1477
2312
  var package_default = {
1478
2313
  name: "@raindrop-ai/opencode-plugin",
1479
- version: "0.1.2",
2314
+ version: "0.1.4",
1480
2315
  description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
1481
2316
  type: "module",
1482
2317
  main: "dist/index.js",
@@ -1547,7 +2382,7 @@ var PLUGIN_NAME = package_default.name;
1547
2382
  var PLUGIN_VERSION = package_default.version;
1548
2383
 
1549
2384
  // src/shipper.ts
1550
- var EventShipper2 = class extends EventShipper {
2385
+ var EventShipper3 = class extends EventShipper2 {
1551
2386
  constructor(opts) {
1552
2387
  var _a, _b, _c, _d;
1553
2388
  super({
@@ -1559,7 +2394,7 @@ var EventShipper2 = class extends EventShipper {
1559
2394
  });
1560
2395
  }
1561
2396
  };
1562
- var TraceShipper2 = class extends TraceShipper {
2397
+ var TraceShipper3 = class extends TraceShipper2 {
1563
2398
  constructor(opts) {
1564
2399
  var _a, _b, _c;
1565
2400
  super({
@@ -1681,18 +2516,13 @@ function parseOpenCodeAssistantTokens(tokens) {
1681
2516
  };
1682
2517
  }
1683
2518
  function modelSpendSpanAttributes(input) {
1684
- const providerAttr = attrString(
1685
- "gen_ai.provider.name",
1686
- canonicalModelProvider(input.providerID)
1687
- );
1688
- const usageAttrs = buildModelUsageAttributes({
2519
+ const providerAttr = attrString("gen_ai.provider.name", canonicalModelProvider(input.providerID));
2520
+ const usageAttrs = buildRawModelUsageAttributes({
1689
2521
  inputTokens: input.tokens.input,
1690
2522
  outputTokens: input.tokens.output,
1691
2523
  reasoningTokens: input.tokens.reasoning,
1692
2524
  cacheReadInputTokens: input.tokens.cacheRead,
1693
- cacheWriteInputTokens: input.tokens.cacheWrite,
1694
- inputTokenSemantics: "exclusive",
1695
- outputTokenSemantics: "exclusive"
2525
+ cacheWriteInputTokens: input.tokens.cacheWrite
1696
2526
  });
1697
2527
  return providerAttr ? [providerAttr, ...usageAttrs] : usageAttrs;
1698
2528
  }
@@ -1700,6 +2530,9 @@ function buildLlmGenAiAttributes(input) {
1700
2530
  const parsed = parseOpenCodeAssistantTokens(input.tokens);
1701
2531
  if (!parsed) return [];
1702
2532
  return [
2533
+ // OpenCode has already normalized both token axes. Provenance lets Dawn
2534
+ // select that contract without the SDK or customer declaring semantics.
2535
+ attrString(MODEL_USAGE_ATTRIBUTES.usageSource, "opencode"),
1703
2536
  attrString("gen_ai.system", input.providerID),
1704
2537
  attrString("gen_ai.request.model", input.modelID),
1705
2538
  attrString("gen_ai.response.model", input.modelID),
@@ -1719,11 +2552,11 @@ function createSessionParentMapHelpers({
1719
2552
  }) {
1720
2553
  function applyChildSessionParentToState(childSessionId) {
1721
2554
  const parentInfo = mapChildSessionToParent2.get(childSessionId);
1722
- const state = sessions2.get(childSessionId);
1723
- if (!parentInfo || !state) return;
1724
- state.parentId = parentInfo.parentId;
1725
- state.parentTaskSpanIds = parentInfo.parentTaskSpanIds;
1726
- state.parentEventContext = parentInfo.eventContext;
2555
+ const state2 = sessions2.get(childSessionId);
2556
+ if (!parentInfo || !state2) return;
2557
+ state2.parentId = parentInfo.parentId;
2558
+ state2.parentTaskSpanIds = parentInfo.parentTaskSpanIds;
2559
+ state2.parentEventContext = parentInfo.eventContext;
1727
2560
  }
1728
2561
  function queuePendingChildByCallKey(key, childSessionId) {
1729
2562
  var _a;
@@ -1819,10 +2652,10 @@ function markTaskCallFinished(sessionID, callID) {
1819
2652
  running.delete(callID);
1820
2653
  if (running.size === 0) runningTaskCallsBySession.delete(sessionID);
1821
2654
  }
1822
- function endPendingToolSpanWithError(state, callID, tool, toolCallArgs, errorMessage, traceShipper) {
1823
- const startInfo = state.toolSpanStarts.get(callID);
2655
+ function endPendingToolSpanWithError(state2, callID, tool, toolCallArgs, errorMessage, traceShipper) {
2656
+ const startInfo = state2.toolSpanStarts.get(callID);
1824
2657
  if (!startInfo) return false;
1825
- state.toolSpanStarts.delete(callID);
2658
+ state2.toolSpanStarts.delete(callID);
1826
2659
  const error = new Error(errorMessage);
1827
2660
  const endAttrs = [
1828
2661
  attrString("ai.toolCall.args", toolCallArgs),
@@ -1922,17 +2755,17 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
1922
2755
  if (info == null || typeof info !== "object")
1923
2756
  throw new Error("session.created: props.info is required");
1924
2757
  const parentID = info["parentID"];
1925
- let state = sessions.get(sessionID);
1926
- if (!state) {
1927
- state = createSessionState(sessionID);
1928
- sessions.set(sessionID, state);
2758
+ let state2 = sessions.get(sessionID);
2759
+ if (!state2) {
2760
+ state2 = createSessionState(sessionID);
2761
+ sessions.set(sessionID, state2);
1929
2762
  }
1930
- state.parentId = parentID;
1931
- if (state.parentId) mapChildSessionToParent.set(sessionID, { parentId: state.parentId });
2763
+ state2.parentId = parentID;
2764
+ if (state2.parentId) mapChildSessionToParent.set(sessionID, { parentId: state2.parentId });
1932
2765
  } else if (event.type === "session.compacted") {
1933
- const state = sessions.get(String(sessionID));
1934
- if (!state) return;
1935
- state.isCompacting = true;
2766
+ const state2 = sessions.get(String(sessionID));
2767
+ if (!state2) return;
2768
+ state2.isCompacting = true;
1936
2769
  } else if (event.type === "message.part.updated") {
1937
2770
  const part = props["part"];
1938
2771
  if (part == null || typeof part !== "object")
@@ -1944,10 +2777,10 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
1944
2777
  throw new Error("message.part.updated: part.sessionID is required");
1945
2778
  if (messageId == null || messageId === "")
1946
2779
  throw new Error("message.part.updated: part.messageID is required");
1947
- const state = sessions.get(partSessionID);
1948
- if (!state) return;
2780
+ const state2 = sessions.get(partSessionID);
2781
+ if (!state2) return;
1949
2782
  if (partObj["type"] === "text" && typeof partObj["text"] === "string") {
1950
- state.outputParts.set(messageId, capText2(partObj["text"]));
2783
+ state2.outputParts.set(messageId, capText2(partObj["text"]));
1951
2784
  } else if (partObj["type"] === "tool" && messageId) {
1952
2785
  const tool = partObj["tool"];
1953
2786
  const callID = partObj["callID"];
@@ -1979,7 +2812,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
1979
2812
  const input = toolState["input"];
1980
2813
  const toolCallArgs = input === void 0 ? void 0 : typeof input === "string" ? capText2(input) : boundedStringify(input);
1981
2814
  if (endPendingToolSpanWithError(
1982
- state,
2815
+ state2,
1983
2816
  callID,
1984
2817
  tool,
1985
2818
  toolCallArgs,
@@ -1990,7 +2823,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
1990
2823
  }
1991
2824
  }
1992
2825
  } else if (partObj["type"] === "reasoning" && typeof partObj["text"] === "string") {
1993
- state.reasoningParts.set(messageId, capText2(partObj["text"]));
2826
+ state2.reasoningParts.set(messageId, capText2(partObj["text"]));
1994
2827
  }
1995
2828
  } else if (event.type === "message.updated") {
1996
2829
  const msgInfo = props["info"];
@@ -2010,10 +2843,10 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
2010
2843
  throw new Error("message.updated: info.time is required");
2011
2844
  const timeObj = time;
2012
2845
  if (timeObj["completed"] == null) return;
2013
- const state = sessions.get(msgSessionID);
2014
- if (!state || !state.currentEventId || !state.currentRootSpan) return;
2015
- if (state.processedMessages.has(messageId)) return;
2016
- state.processedMessages.add(messageId);
2846
+ const state2 = sessions.get(msgSessionID);
2847
+ if (!state2 || !state2.currentEventId || !state2.currentRootSpan) return;
2848
+ if (state2.processedMessages.has(messageId)) return;
2849
+ state2.processedMessages.add(messageId);
2017
2850
  const tokens = info2["tokens"];
2018
2851
  if (tokens == null || typeof tokens !== "object")
2019
2852
  throw new Error("message.updated: info.tokens is required");
@@ -2030,13 +2863,13 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
2030
2863
  );
2031
2864
  const modelName = `${providerID}/${modelID}`;
2032
2865
  const finishReason = info2["finish"];
2033
- const outputText = state.outputParts.get(messageId);
2034
- const reasoningText = state.reasoningParts.get(messageId);
2866
+ const outputText = state2.outputParts.get(messageId);
2867
+ const reasoningText = state2.reasoningParts.get(messageId);
2035
2868
  const isToolCallsCompletion = finishReason === "tool-calls";
2036
2869
  if (outputText !== void 0 && outputText.trim().length > 0) {
2037
- state.turnOutputParts.push(outputText);
2870
+ state2.turnOutputParts.push(outputText);
2038
2871
  }
2039
- state.turnOutputTokens += outputTokens;
2872
+ state2.turnOutputTokens += outputTokens;
2040
2873
  if (outputText === void 0 && !isToolCallsCompletion) return;
2041
2874
  const msgError = info2["error"];
2042
2875
  const errorForSpan = msgError ? (() => {
@@ -2056,17 +2889,17 @@ type: ${name != null ? name : "UnknownError"}`;
2056
2889
  attrString("ai.response.text", responseText),
2057
2890
  ...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
2058
2891
  ];
2059
- if (state.currentSystemPrompt) {
2892
+ if (state2.currentSystemPrompt) {
2060
2893
  llmAttrs.push(
2061
2894
  attrString("gen_ai.prompt.0.role", "system"),
2062
- attrString("gen_ai.prompt.0.content", state.currentSystemPrompt)
2895
+ attrString("gen_ai.prompt.0.content", state2.currentSystemPrompt)
2063
2896
  );
2064
2897
  }
2065
- const llmParent = state.parentId && state.parentTaskSpanIds ? state.parentTaskSpanIds : state.currentRootSpan.ids;
2898
+ const llmParent = state2.parentId && state2.parentTaskSpanIds ? state2.parentTaskSpanIds : state2.currentRootSpan.ids;
2066
2899
  const llmSpan = traceShipper.startSpan({
2067
2900
  name: modelName,
2068
2901
  parent: llmParent,
2069
- eventId: state.currentEventId,
2902
+ eventId: state2.currentEventId,
2070
2903
  attributes: llmAttrs
2071
2904
  });
2072
2905
  if (typeof timeObj["created"] === "number") {
@@ -2077,40 +2910,40 @@ type: ${name != null ? name : "UnknownError"}`;
2077
2910
  return;
2078
2911
  }
2079
2912
  if (outputText === void 0) return;
2080
- const turnOutputText = capText2(state.turnOutputParts.join("\n\n"));
2081
- const rootSpan = state.currentRootSpan;
2082
- const isCompaction = state.isCompacting === true;
2083
- if (state.parentId) {
2084
- const userInput = unwrapQuotes(state.currentInput);
2913
+ const turnOutputText = capText2(state2.turnOutputParts.join("\n\n"));
2914
+ const rootSpan = state2.currentRootSpan;
2915
+ const isCompaction = state2.isCompacting === true;
2916
+ if (state2.parentId) {
2917
+ const userInput = unwrapQuotes(state2.currentInput);
2085
2918
  const llmAttrs = [
2086
2919
  attrString("ai.operationId", "generateText"),
2087
2920
  attrString("ai.prompt", userInput),
2088
2921
  attrString(
2089
2922
  "ai.prompt.messages",
2090
- buildPromptMessages(state.currentSystemPrompt, state.currentInput)
2923
+ buildPromptMessages(state2.currentSystemPrompt, state2.currentInput)
2091
2924
  ),
2092
2925
  attrString("ai.response.text", turnOutputText),
2093
2926
  ...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
2094
2927
  ];
2095
- if (state.currentSystemPrompt) {
2928
+ if (state2.currentSystemPrompt) {
2096
2929
  llmAttrs.push(
2097
2930
  attrString("gen_ai.prompt.0.role", "system"),
2098
- attrString("gen_ai.prompt.0.content", state.currentSystemPrompt)
2931
+ attrString("gen_ai.prompt.0.content", state2.currentSystemPrompt)
2099
2932
  );
2100
2933
  }
2101
- if (!state.parentTaskSpanIds) {
2934
+ if (!state2.parentTaskSpanIds) {
2102
2935
  traceShipper.endSpan(rootSpan, {
2103
2936
  error: "Missing strict parent task for child session"
2104
2937
  });
2105
- state.currentRootSpan = void 0;
2106
- state.currentEventId = void 0;
2938
+ state2.currentRootSpan = void 0;
2939
+ state2.currentEventId = void 0;
2107
2940
  await traceShipper.flush();
2108
2941
  return;
2109
2942
  }
2110
2943
  const finalLlmSpan = traceShipper.startSpan({
2111
2944
  name: modelName,
2112
- parent: state.parentTaskSpanIds,
2113
- eventId: state.currentEventId,
2945
+ parent: state2.parentTaskSpanIds,
2946
+ eventId: state2.currentEventId,
2114
2947
  attributes: llmAttrs
2115
2948
  });
2116
2949
  if (typeof timeObj["created"] === "number") {
@@ -2120,19 +2953,19 @@ type: ${name != null ? name : "UnknownError"}`;
2120
2953
  traceShipper.endSpan(rootSpan, {
2121
2954
  attributes: [
2122
2955
  attrString("is_subagent", "true"),
2123
- attrString("parent_session_id", state.parentId)
2956
+ attrString("parent_session_id", state2.parentId)
2124
2957
  ],
2125
2958
  error: errorForSpan
2126
2959
  });
2127
2960
  } else {
2128
2961
  rootSpan.name = isCompaction ? "ai.compaction" : modelName;
2129
- const userInput = unwrapQuotes(state.currentInput);
2962
+ const userInput = unwrapQuotes(state2.currentInput);
2130
2963
  const rootAttrs = [
2131
2964
  attrString("ai.operationId", "generateText"),
2132
2965
  attrString("ai.prompt", userInput),
2133
2966
  attrString(
2134
2967
  "ai.prompt.messages",
2135
- buildPromptMessages(state.currentSystemPrompt, state.currentInput)
2968
+ buildPromptMessages(state2.currentSystemPrompt, state2.currentInput)
2136
2969
  ),
2137
2970
  attrString("ai.response.text", turnOutputText),
2138
2971
  ...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
@@ -2141,10 +2974,10 @@ type: ${name != null ? name : "UnknownError"}`;
2141
2974
  rootAttrs.push(attrString("is_compaction", "true"));
2142
2975
  }
2143
2976
  rootAttrs.push(attrString("message_id", messageId));
2144
- if (state.currentSystemPrompt) {
2977
+ if (state2.currentSystemPrompt) {
2145
2978
  rootAttrs.push(
2146
2979
  attrString("gen_ai.prompt.0.role", "system"),
2147
- attrString("gen_ai.prompt.0.content", state.currentSystemPrompt)
2980
+ attrString("gen_ai.prompt.0.content", state2.currentSystemPrompt)
2148
2981
  );
2149
2982
  }
2150
2983
  traceShipper.endSpan(rootSpan, {
@@ -2152,24 +2985,24 @@ type: ${name != null ? name : "UnknownError"}`;
2152
2985
  error: errorForSpan
2153
2986
  });
2154
2987
  if (isCompaction) {
2155
- state.isCompacting = false;
2988
+ state2.isCompacting = false;
2156
2989
  }
2157
2990
  }
2158
- state.currentRootSpan = void 0;
2991
+ state2.currentRootSpan = void 0;
2159
2992
  const hasAssistantResponse = turnOutputText.trim().length > 0;
2160
2993
  if (!hasAssistantResponse && !msgError) {
2161
2994
  await traceShipper.flush();
2162
- state.currentEventId = void 0;
2995
+ state2.currentEventId = void 0;
2163
2996
  return;
2164
2997
  }
2165
- if (!state.parentId) {
2166
- await eventShipper.finish(state.currentEventId, {
2167
- userId: (_d = (_c = state.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state.sessionId,
2998
+ if (!state2.parentId) {
2999
+ await eventShipper.finish(state2.currentEventId, {
3000
+ userId: (_d = (_c = state2.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state2.sessionId,
2168
3001
  model: modelID,
2169
3002
  output: msgError ? void 0 : turnOutputText,
2170
3003
  usage: {
2171
3004
  promptTokens: inputTokens,
2172
- completionTokens: state.turnOutputTokens
3005
+ completionTokens: state2.turnOutputTokens
2173
3006
  },
2174
3007
  error: msgError ? {
2175
3008
  name: msgError.name,
@@ -2184,35 +3017,35 @@ type: ${name != null ? name : "UnknownError"}`;
2184
3017
  });
2185
3018
  }
2186
3019
  await traceShipper.flush();
2187
- state.currentEventId = void 0;
3020
+ state2.currentEventId = void 0;
2188
3021
  } else if (event.type === "session.idle") {
2189
3022
  if (!sessionID) return;
2190
- const state = sessions.get(String(sessionID));
2191
- if (!state) return;
2192
- if (state.currentRootSpan) {
2193
- traceShipper.endSpan(state.currentRootSpan);
2194
- state.currentRootSpan = void 0;
3023
+ const state2 = sessions.get(String(sessionID));
3024
+ if (!state2) return;
3025
+ if (state2.currentRootSpan) {
3026
+ traceShipper.endSpan(state2.currentRootSpan);
3027
+ state2.currentRootSpan = void 0;
2195
3028
  }
2196
- if (state.currentEventId) {
2197
- state.currentEventId = void 0;
3029
+ if (state2.currentEventId) {
3030
+ state2.currentEventId = void 0;
2198
3031
  }
2199
- state.isCompacting = false;
3032
+ state2.isCompacting = false;
2200
3033
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
2201
- if (state.parentId) {
3034
+ if (state2.parentId) {
2202
3035
  cleanupSessionParentMap(String(sessionID));
2203
3036
  sessions.delete(String(sessionID));
2204
3037
  } else {
2205
3038
  }
2206
3039
  } else if (event.type === "session.deleted") {
2207
3040
  if (!sessionID) return;
2208
- const state = sessions.get(String(sessionID));
2209
- if (!state) return;
2210
- if (state.currentRootSpan) {
2211
- traceShipper.endSpan(state.currentRootSpan);
2212
- state.currentRootSpan = void 0;
3041
+ const state2 = sessions.get(String(sessionID));
3042
+ if (!state2) return;
3043
+ if (state2.currentRootSpan) {
3044
+ traceShipper.endSpan(state2.currentRootSpan);
3045
+ state2.currentRootSpan = void 0;
2213
3046
  }
2214
- if (state.currentEventId) {
2215
- state.currentEventId = void 0;
3047
+ if (state2.currentEventId) {
3048
+ state2.currentEventId = void 0;
2216
3049
  }
2217
3050
  cleanupSessionParentMap(String(sessionID));
2218
3051
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
@@ -2221,8 +3054,8 @@ type: ${name != null ? name : "UnknownError"}`;
2221
3054
  const errorSessionID = (_h = props["sessionID"]) != null ? _h : sessionID;
2222
3055
  if (errorSessionID === void 0 || errorSessionID === "")
2223
3056
  throw new Error("session.error: missing sessionID");
2224
- const state = sessions.get(String(errorSessionID));
2225
- if (!state) return;
3057
+ const state2 = sessions.get(String(errorSessionID));
3058
+ if (!state2) return;
2226
3059
  const errorObj = props["error"];
2227
3060
  if (errorObj == null) throw new Error("session.error: missing error object");
2228
3061
  const errorName = errorObj.name;
@@ -2232,12 +3065,12 @@ type: ${name != null ? name : "UnknownError"}`;
2232
3065
  const errorStr = `${errorMessage != null ? errorMessage : errorName}
2233
3066
 
2234
3067
  type: ${errorName != null ? errorName : "UnknownError"}`;
2235
- if (state.currentRootSpan) {
2236
- traceShipper.endSpan(state.currentRootSpan, { error: errorStr });
2237
- state.currentRootSpan = void 0;
3068
+ if (state2.currentRootSpan) {
3069
+ traceShipper.endSpan(state2.currentRootSpan, { error: errorStr });
3070
+ state2.currentRootSpan = void 0;
2238
3071
  }
2239
- if (state.currentEventId) {
2240
- state.currentEventId = void 0;
3072
+ if (state2.currentEventId) {
3073
+ state2.currentEventId = void 0;
2241
3074
  }
2242
3075
  cleanupSessionParentMap(String(errorSessionID));
2243
3076
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
@@ -2257,15 +3090,15 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2257
3090
  var _a, _b, _c, _d, _e, _f, _g;
2258
3091
  try {
2259
3092
  const { sessionID } = messageInput;
2260
- let state = sessions.get(sessionID);
2261
- if (!state) {
2262
- state = createSessionState(sessionID);
2263
- sessions.set(sessionID, state);
3093
+ let state2 = sessions.get(sessionID);
3094
+ if (!state2) {
3095
+ state2 = createSessionState(sessionID);
3096
+ sessions.set(sessionID, state2);
2264
3097
  }
2265
3098
  applyChildSessionParentToState(sessionID);
2266
- if (state.currentRootSpan) {
2267
- traceShipper.endSpan(state.currentRootSpan);
2268
- state.currentRootSpan = void 0;
3099
+ if (state2.currentRootSpan) {
3100
+ traceShipper.endSpan(state2.currentRootSpan);
3101
+ state2.currentRootSpan = void 0;
2269
3102
  }
2270
3103
  if (output == null) throw new Error("chat.message: output is required");
2271
3104
  const parts = output.parts;
@@ -2296,39 +3129,39 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2296
3129
  }
2297
3130
  }
2298
3131
  }
2299
- state.eventMetadata = {
3132
+ state2.eventMetadata = {
2300
3133
  ...baseMeta,
2301
3134
  ...promptMeta,
2302
3135
  properties: baseMeta.properties || promptMeta.properties ? { ...(_a = baseMeta.properties) != null ? _a : {}, ...(_b = promptMeta.properties) != null ? _b : {} } : void 0
2303
3136
  };
2304
3137
  const userText = capText2(textParts.join("\n"));
2305
- state.currentInput = userText;
2306
- state.turnOutputParts = [];
2307
- state.turnOutputTokens = 0;
2308
- if (state.parentId) {
2309
- if (!state.parentTaskSpanIds || !state.parentEventContext) {
3138
+ state2.currentInput = userText;
3139
+ state2.turnOutputParts = [];
3140
+ state2.turnOutputTokens = 0;
3141
+ if (state2.parentId) {
3142
+ if (!state2.parentTaskSpanIds || !state2.parentEventContext) {
2310
3143
  return;
2311
3144
  }
2312
- state.currentEventId = state.parentEventContext.eventId;
2313
- state.currentRootSpan = traceShipper.startSpan({
3145
+ state2.currentEventId = state2.parentEventContext.eventId;
3146
+ state2.currentRootSpan = traceShipper.startSpan({
2314
3147
  name: "Subagent",
2315
- parent: state.parentTaskSpanIds,
2316
- eventId: state.currentEventId,
3148
+ parent: state2.parentTaskSpanIds,
3149
+ eventId: state2.currentEventId,
2317
3150
  attributes: [
2318
3151
  attrString("workspace", worktree),
2319
3152
  attrString("directory", directory),
2320
3153
  attrString("hostname", hostname),
2321
3154
  attrString("os", os),
2322
3155
  attrString("is_subagent", "true"),
2323
- attrString("parent_session_id", state.parentId)
3156
+ attrString("parent_session_id", state2.parentId)
2324
3157
  ]
2325
3158
  });
2326
3159
  return;
2327
3160
  }
2328
- state.currentEventId = generateId();
2329
- state.currentRootSpan = traceShipper.startSpan({
2330
- name: state.isCompacting ? "ai.compaction" : "ai.event",
2331
- eventId: state.currentEventId,
3161
+ state2.currentEventId = generateId();
3162
+ state2.currentRootSpan = traceShipper.startSpan({
3163
+ name: state2.isCompacting ? "ai.compaction" : "ai.event",
3164
+ eventId: state2.currentEventId,
2332
3165
  attributes: [
2333
3166
  attrString("workspace", worktree),
2334
3167
  attrString("directory", directory),
@@ -2336,11 +3169,11 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2336
3169
  attrString("os", os)
2337
3170
  ]
2338
3171
  });
2339
- await eventShipper.patch(state.currentEventId, {
3172
+ await eventShipper.patch(state2.currentEventId, {
2340
3173
  isPending: true,
2341
- userId: (_d = (_c = state.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state.sessionId,
2342
- convoId: state.sessionId,
2343
- eventName: (_f = (_e = state.eventMetadata) == null ? void 0 : _e.eventName) != null ? _f : config.eventName,
3174
+ userId: (_d = (_c = state2.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state2.sessionId,
3175
+ convoId: state2.sessionId,
3176
+ eventName: (_f = (_e = state2.eventMetadata) == null ? void 0 : _e.eventName) != null ? _f : config.eventName,
2344
3177
  input: userText,
2345
3178
  ...newAttachments.length > 0 ? { attachments: newAttachments } : {},
2346
3179
  properties: {
@@ -2349,7 +3182,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2349
3182
  hostname,
2350
3183
  os,
2351
3184
  plugin_version: PLUGIN_VERSION,
2352
- ...(_g = state.eventMetadata) == null ? void 0 : _g.properties
3185
+ ...(_g = state2.eventMetadata) == null ? void 0 : _g.properties
2353
3186
  }
2354
3187
  });
2355
3188
  } catch (err) {
@@ -2363,16 +3196,16 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2363
3196
  var _a, _b, _c;
2364
3197
  try {
2365
3198
  const { tool, sessionID, callID } = toolInput;
2366
- const state = sessions.get(sessionID);
2367
- if (!state || !state.currentEventId) return;
2368
- const spanParent = state.parentId && state.parentTaskSpanIds ? state.parentTaskSpanIds : (_a = state.currentRootSpan) == null ? void 0 : _a.ids;
3199
+ const state2 = sessions.get(sessionID);
3200
+ if (!state2 || !state2.currentEventId) return;
3201
+ const spanParent = state2.parentId && state2.parentTaskSpanIds ? state2.parentTaskSpanIds : (_a = state2.currentRootSpan) == null ? void 0 : _a.ids;
2369
3202
  if (!spanParent) return;
2370
3203
  const startTimeUnixNano = nowUnixNanoString();
2371
3204
  if (tool === "task") {
2372
3205
  const liveTaskSpan = traceShipper.startSpan({
2373
3206
  name: "ai.toolCall",
2374
3207
  parent: spanParent,
2375
- eventId: state.currentEventId,
3208
+ eventId: state2.currentEventId,
2376
3209
  attributes: [
2377
3210
  attrString("ai.operationId", "ai.toolCall"),
2378
3211
  attrString("ai.toolCall.name", tool),
@@ -2383,9 +3216,9 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2383
3216
  const ctx = {
2384
3217
  ids: liveTaskSpan.ids,
2385
3218
  eventContext: {
2386
- eventId: state.currentEventId,
2387
- userId: (_c = (_b = state.eventMetadata) == null ? void 0 : _b.userId) != null ? _c : state.sessionId,
2388
- convoId: state.sessionId
3219
+ eventId: state2.currentEventId,
3220
+ userId: (_c = (_b = state2.eventMetadata) == null ? void 0 : _b.userId) != null ? _c : state2.sessionId,
3221
+ convoId: state2.sessionId
2389
3222
  }
2390
3223
  };
2391
3224
  taskContexts.set(callKey(sessionID, callID), ctx);
@@ -2396,18 +3229,18 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2396
3229
  }
2397
3230
  running.add(callID);
2398
3231
  resolvePendingChildrenForCall(sessionID, callID, "tool.execute.before");
2399
- state.toolSpanStarts.set(callID, {
3232
+ state2.toolSpanStarts.set(callID, {
2400
3233
  startTimeUnixNano,
2401
3234
  parent: spanParent,
2402
- eventId: state.currentEventId,
3235
+ eventId: state2.currentEventId,
2403
3236
  name: tool,
2404
3237
  liveSpan: liveTaskSpan
2405
3238
  });
2406
3239
  } else {
2407
- state.toolSpanStarts.set(callID, {
3240
+ state2.toolSpanStarts.set(callID, {
2408
3241
  startTimeUnixNano,
2409
3242
  parent: spanParent,
2410
- eventId: state.currentEventId,
3243
+ eventId: state2.currentEventId,
2411
3244
  name: tool
2412
3245
  });
2413
3246
  }
@@ -2424,13 +3257,13 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2424
3257
  "tool.execute.after": async (toolInput, result) => {
2425
3258
  try {
2426
3259
  const { tool, sessionID, callID, args } = toolInput;
2427
- const state = sessions.get(sessionID);
2428
- if (!state || !state.currentEventId) return;
3260
+ const state2 = sessions.get(sessionID);
3261
+ if (!state2 || !state2.currentEventId) return;
2429
3262
  if (args === void 0 || args === null)
2430
3263
  throw new Error("tool.execute.after: args is required");
2431
3264
  const toolCallArgs = typeof args === "string" ? capText2(args) : boundedStringify(args);
2432
- const startInfo = state.toolSpanStarts.get(callID);
2433
- state.toolSpanStarts.delete(callID);
3265
+ const startInfo = state2.toolSpanStarts.get(callID);
3266
+ state2.toolSpanStarts.delete(callID);
2434
3267
  const resultMetadata = result.metadata;
2435
3268
  const childSessionId = resultMetadata == null ? void 0 : resultMetadata["sessionId"];
2436
3269
  if (tool === "task" && childSessionId) {
@@ -2483,9 +3316,9 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2483
3316
  try {
2484
3317
  const sessionID = input.sessionID;
2485
3318
  if (!sessionID) return;
2486
- const state = sessions.get(sessionID);
2487
- if (state) {
2488
- state.isCompacting = true;
3319
+ const state2 = sessions.get(sessionID);
3320
+ if (state2) {
3321
+ state2.isCompacting = true;
2489
3322
  }
2490
3323
  } catch (err) {
2491
3324
  rateLimitedErrorLog(
@@ -2502,15 +3335,15 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2502
3335
  if (!config.captureSystemPrompt) return;
2503
3336
  const sessionID = input.sessionID;
2504
3337
  if (!sessionID) return;
2505
- let state = sessions.get(sessionID);
2506
- if (!state) {
2507
- state = createSessionState(sessionID);
2508
- sessions.set(sessionID, state);
3338
+ let state2 = sessions.get(sessionID);
3339
+ if (!state2) {
3340
+ state2 = createSessionState(sessionID);
3341
+ sessions.set(sessionID, state2);
2509
3342
  }
2510
3343
  const MAX_SYSTEM_PROMPT_LENGTH = 32768;
2511
3344
  const TRUNCATION_MARKER3 = "\n...[truncated]";
2512
3345
  const joined = output.system.join("\n\n");
2513
- state.currentSystemPrompt = joined.length > MAX_SYSTEM_PROMPT_LENGTH ? joined.slice(0, MAX_SYSTEM_PROMPT_LENGTH - TRUNCATION_MARKER3.length) + TRUNCATION_MARKER3 : joined;
3346
+ state2.currentSystemPrompt = joined.length > MAX_SYSTEM_PROMPT_LENGTH ? joined.slice(0, MAX_SYSTEM_PROMPT_LENGTH - TRUNCATION_MARKER3.length) + TRUNCATION_MARKER3 : joined;
2514
3347
  } catch (err) {
2515
3348
  rateLimitedErrorLog(
2516
3349
  "experimental.chat.system.transform",
@@ -2522,7 +3355,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2522
3355
  }
2523
3356
 
2524
3357
  // src/index.ts
2525
- function createRaindropHandle(eventShipper, traceShipper) {
3358
+ function createRaindropHandle(eventShipper, traceShipper, appGitContext) {
2526
3359
  return {
2527
3360
  users: {
2528
3361
  async identify(users) {
@@ -2533,12 +3366,19 @@ function createRaindropHandle(eventShipper, traceShipper) {
2533
3366
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
2534
3367
  },
2535
3368
  async shutdown() {
2536
- await Promise.all([eventShipper.shutdown(), traceShipper.shutdown()]);
3369
+ try {
3370
+ await Promise.all([eventShipper.shutdown(), traceShipper.shutdown()]);
3371
+ } finally {
3372
+ try {
3373
+ appGitContext.dispose();
3374
+ } catch (e) {
3375
+ }
3376
+ }
2537
3377
  }
2538
3378
  };
2539
3379
  }
2540
- async function plugin(input) {
2541
- var _a;
3380
+ async function plugin(input, opts = {}) {
3381
+ var _a, _b, _c;
2542
3382
  const config = loadConfig(input.directory);
2543
3383
  function appLog(level, message) {
2544
3384
  console.log(`[raindrop-ai/opencode-plugin] [${level}] ${message}`);
@@ -2560,21 +3400,28 @@ async function plugin(input) {
2560
3400
  ].filter(Boolean);
2561
3401
  appLog("info", `Raindrop tracing enabled \u2014 destinations: ${destinations.join(", ")}`);
2562
3402
  }
2563
- const eventShipper = new EventShipper2({
3403
+ const configuredAppGit = (_a = opts.appGit) != null ? _a : config.appGit;
3404
+ const appGitContext = createAppGitContext2(
3405
+ ((_b = config.writeKey) == null ? void 0 : _b.trim()) || hasLocalDestination ? configuredAppGit : false,
3406
+ { allowLocalGit: false }
3407
+ );
3408
+ const eventShipper = new EventShipper3({
2564
3409
  writeKey: config.writeKey,
2565
3410
  endpoint: config.endpoint,
2566
3411
  debug: config.debug,
2567
3412
  projectId: config.projectId,
2568
- localDebuggerUrl: config.localWorkshopUrl
3413
+ localDebuggerUrl: config.localWorkshopUrl,
3414
+ appGitContext
2569
3415
  });
2570
- const traceShipper = new TraceShipper2({
3416
+ const traceShipper = new TraceShipper3({
2571
3417
  writeKey: config.writeKey,
2572
3418
  endpoint: config.endpoint,
2573
3419
  debug: config.debug,
2574
3420
  projectId: config.projectId,
2575
- localDebuggerUrl: config.localWorkshopUrl
3421
+ localDebuggerUrl: config.localWorkshopUrl,
3422
+ appGitContext
2576
3423
  });
2577
- const worktree = (_a = input.worktree) != null ? _a : input.directory;
3424
+ const worktree = (_c = input.worktree) != null ? _c : input.directory;
2578
3425
  const hooks = createHooks(
2579
3426
  config,
2580
3427
  worktree,
@@ -2582,7 +3429,7 @@ async function plugin(input) {
2582
3429
  eventShipper,
2583
3430
  traceShipper
2584
3431
  );
2585
- return { ...hooks, raindrop: createRaindropHandle(eventShipper, traceShipper) };
3432
+ return { ...hooks, raindrop: createRaindropHandle(eventShipper, traceShipper, appGitContext) };
2586
3433
  }
2587
3434
  export {
2588
3435
  plugin as default