@raindrop-ai/opencode-plugin 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -24,7 +24,521 @@ __export(index_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(index_exports);
26
26
 
27
- // ../core/dist/chunk-3V4MIA75.js
27
+ // ../core/dist/chunk-BYBMRLZM.js
28
+ var EVAL_CORRELATION_ID_ATTRIBUTE = "raindrop.eval_correlation_id";
29
+ var PROBE_SCOPE = { correlationId: "" };
30
+ var EvalScopeStorage = class {
31
+ constructor() {
32
+ this._als = null;
33
+ this._probed = false;
34
+ }
35
+ maybeAdoptAsyncLocalStorage() {
36
+ if (this._probed) return;
37
+ this._probed = true;
38
+ try {
39
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
40
+ if (!Ctor) return;
41
+ const als = new Ctor();
42
+ als.run(PROBE_SCOPE, () => {
43
+ });
44
+ this._als = als;
45
+ } catch (e) {
46
+ this._als = null;
47
+ }
48
+ }
49
+ run(scope, callback) {
50
+ this.maybeAdoptAsyncLocalStorage();
51
+ if (this._als) return this._als.run(scope, callback);
52
+ const previous = this._syncScope;
53
+ this._syncScope = scope;
54
+ try {
55
+ return callback();
56
+ } finally {
57
+ this._syncScope = previous;
58
+ }
59
+ }
60
+ current() {
61
+ this.maybeAdoptAsyncLocalStorage();
62
+ if (this._als) return this._als.getStore();
63
+ return this._syncScope;
64
+ }
65
+ };
66
+ var EVAL_SCOPE_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.evalScopeStorage");
67
+ function evalScopeStorage() {
68
+ const holder = globalThis;
69
+ let storage = holder[EVAL_SCOPE_STORAGE_KEY];
70
+ if (!storage) {
71
+ storage = new EvalScopeStorage();
72
+ holder[EVAL_SCOPE_STORAGE_KEY] = storage;
73
+ }
74
+ return storage;
75
+ }
76
+ function currentEvalScope() {
77
+ try {
78
+ return evalScopeStorage().current();
79
+ } catch (e) {
80
+ return void 0;
81
+ }
82
+ }
83
+ function evalScopeAttributes() {
84
+ const scope = currentEvalScope();
85
+ if (!scope) return {};
86
+ return { [EVAL_CORRELATION_ID_ATTRIBUTE]: scope.correlationId };
87
+ }
88
+
89
+ // ../core/dist/chunk-WJOR6M7H.js
90
+ var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
91
+ function state() {
92
+ const holder = globalThis;
93
+ let current = holder[STATE_KEY];
94
+ if (!current) {
95
+ let storage = null;
96
+ try {
97
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
98
+ if (Ctor) storage = new Ctor();
99
+ } catch (e) {
100
+ storage = null;
101
+ }
102
+ current = {
103
+ storage,
104
+ syncDestination: void 0,
105
+ spanDestinations: /* @__PURE__ */ new WeakMap()
106
+ };
107
+ holder[STATE_KEY] = current;
108
+ }
109
+ return current;
110
+ }
111
+ function currentReplayTraceDestination() {
112
+ var _a, _b;
113
+ const routing = state();
114
+ return (_b = (_a = routing.storage) == null ? void 0 : _a.getStore()) != null ? _b : routing.syncDestination;
115
+ }
116
+ function rememberReplayTraceDestination(span) {
117
+ const destination = currentReplayTraceDestination();
118
+ if (destination) state().spanDestinations.set(span, destination);
119
+ }
120
+ function copyReplayTraceDestination(source, target) {
121
+ const destination = state().spanDestinations.get(source);
122
+ if (destination) state().spanDestinations.set(target, destination);
123
+ }
124
+ function replayTraceDestinationFor(value) {
125
+ if (typeof value !== "object" && typeof value !== "function" || value === null) {
126
+ return void 0;
127
+ }
128
+ return state().spanDestinations.get(value);
129
+ }
130
+ function recordReplayExportedSpan(destination, spanId, traceId) {
131
+ var _a, _b;
132
+ if (!destination || !spanId || !traceId) return;
133
+ (_a = destination.exportedSpanIds) == null ? void 0 : _a.add(spanId);
134
+ (_b = destination.exportedTraceIds) == null ? void 0 : _b.add(traceId);
135
+ }
136
+ function replayTraceUrl(baseUrl) {
137
+ return `${baseUrl.replace(/\/+$/, "")}/v1/traces`;
138
+ }
139
+ function getAsyncLocalStorageCtor() {
140
+ return globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
141
+ }
142
+ var MAX_BINDING_STACK = 128;
143
+ var EMPTY_STACK = [];
144
+ var ContextStackStorage = class {
145
+ /** Emitted once when this runtime cannot support an ambient push. */
146
+ constructor(noAmbientBindWarning) {
147
+ this.noAmbientBindWarning = noAmbientBindWarning;
148
+ this._als = null;
149
+ this._enterWithUsable = false;
150
+ this._syncStack = EMPTY_STACK;
151
+ this._adoptionChecked = false;
152
+ this._warnedNoAmbientBind = false;
153
+ }
154
+ maybeAdoptAsyncLocalStorage() {
155
+ if (this._adoptionChecked) return;
156
+ this._adoptionChecked = true;
157
+ const Ctor = getAsyncLocalStorageCtor();
158
+ if (!Ctor) return;
159
+ const als = new Ctor();
160
+ try {
161
+ als.run(EMPTY_STACK, () => {
162
+ });
163
+ } catch (e) {
164
+ return;
165
+ }
166
+ this._als = als;
167
+ if (typeof als.enterWith === "function") {
168
+ try {
169
+ als.enterWith(EMPTY_STACK);
170
+ this._enterWithUsable = true;
171
+ } catch (e) {
172
+ this._enterWithUsable = false;
173
+ }
174
+ }
175
+ }
176
+ getStack() {
177
+ var _a;
178
+ this.maybeAdoptAsyncLocalStorage();
179
+ if (this._als) return (_a = this._als.getStore()) != null ? _a : this._syncStack;
180
+ return this._syncStack;
181
+ }
182
+ /**
183
+ * Persist an ambient stack mutation. Returns false when an ALS is adopted but
184
+ * `enterWith` is unusable (workerd): callers must NOT fall back to the
185
+ * process-shared sync stack, since concurrent isolates would cross-stamp.
186
+ */
187
+ setAmbientStack(stack) {
188
+ if (this._als) {
189
+ if (!this._enterWithUsable) return false;
190
+ this._als.enterWith(stack);
191
+ return true;
192
+ }
193
+ this._syncStack = stack;
194
+ return true;
195
+ }
196
+ push(binding) {
197
+ this.maybeAdoptAsyncLocalStorage();
198
+ if (this._als && !this._enterWithUsable) {
199
+ this.warnNoAmbientBindOnce();
200
+ return;
201
+ }
202
+ let stack = this.getStack();
203
+ if (stack.length >= MAX_BINDING_STACK) {
204
+ stack = stack.filter((b) => b.isLive());
205
+ if (stack.length >= MAX_BINDING_STACK) {
206
+ stack = stack.slice(stack.length - (MAX_BINDING_STACK - 1));
207
+ }
208
+ }
209
+ this.setAmbientStack([...stack, binding]);
210
+ }
211
+ remove(binding) {
212
+ binding.markDead();
213
+ const stack = this.getStack();
214
+ const pruned = stack.filter((b) => b !== binding && b.isLive());
215
+ if (pruned.length !== stack.length) {
216
+ this.setAmbientStack(pruned);
217
+ }
218
+ }
219
+ runWith(binding, callback) {
220
+ this.maybeAdoptAsyncLocalStorage();
221
+ if (this._als) {
222
+ return this._als.run([...this.getStack(), binding], callback);
223
+ }
224
+ const previous = this._syncStack;
225
+ this._syncStack = [...previous, binding];
226
+ try {
227
+ return callback();
228
+ } finally {
229
+ this._syncStack = previous;
230
+ }
231
+ }
232
+ current() {
233
+ const stack = this.getStack();
234
+ if (stack.length === 0) return void 0;
235
+ const top = stack[stack.length - 1];
236
+ if (top.isLive()) return top;
237
+ const pruned = stack.filter((b) => b.isLive());
238
+ this.setAmbientStack(pruned);
239
+ return pruned.length > 0 ? pruned[pruned.length - 1] : void 0;
240
+ }
241
+ warnNoAmbientBindOnce() {
242
+ if (this._warnedNoAmbientBind) return;
243
+ this._warnedNoAmbientBind = true;
244
+ console.warn(this.noAmbientBindWarning);
245
+ }
246
+ };
247
+ var NO_AMBIENT_ROUTING_BIND_WARNING = "[raindrop] This runtime's AsyncLocalStorage does not implement enterWith (e.g. Cloudflare Workers), so begin()/finish() cannot scope auto-instrumented spans to a project without cross-contaminating concurrent requests. Manual events and explicit span routing are unaffected; use asCurrent()/withSpan (run-based) to route auto-instrumented spans on this runtime.";
248
+ var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
249
+ function routingStorage() {
250
+ const holder = globalThis;
251
+ let storage = holder[ROUTING_STORAGE_KEY];
252
+ if (!storage) {
253
+ storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
254
+ holder[ROUTING_STORAGE_KEY] = storage;
255
+ }
256
+ return storage;
257
+ }
258
+ function currentRoutingBinding() {
259
+ const bound = routingStorage().current();
260
+ if (!bound) return void 0;
261
+ return {
262
+ projectId: bound.projectId,
263
+ authHint: bound.authHint,
264
+ ...bound.appGit ? { appGit: bound.appGit } : {},
265
+ ...bound.appGitSource ? { appGitSource: bound.appGitSource } : {}
266
+ };
267
+ }
268
+ var APP_COMMIT_SHA = "raindrop.app.commit_sha";
269
+ var APP_COMMIT_DIRTY = "raindrop.app.commit_dirty";
270
+ var APP_BRANCH = "raindrop.app.branch";
271
+ var APP_GIT_KEYS = [APP_COMMIT_SHA, APP_COMMIT_DIRTY, APP_BRANCH];
272
+ var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
273
+ function appGitExplicitKeys(properties) {
274
+ const keys = /* @__PURE__ */ new Set();
275
+ if (!properties) return keys;
276
+ for (const key of APP_GIT_KEYS) {
277
+ if (own(properties, key)) keys.add(key);
278
+ }
279
+ return keys;
280
+ }
281
+ function mergeAppGitSnapshot(base, properties, explicitKeys = /* @__PURE__ */ new Set()) {
282
+ try {
283
+ const result = { ...base };
284
+ if (properties && own(properties, APP_COMMIT_SHA) && properties[APP_COMMIT_SHA] !== result[APP_COMMIT_SHA]) {
285
+ if (!explicitKeys.has(APP_COMMIT_DIRTY) && !own(properties, APP_COMMIT_DIRTY)) {
286
+ delete result[APP_COMMIT_DIRTY];
287
+ }
288
+ if (!explicitKeys.has(APP_BRANCH) && !own(properties, APP_BRANCH)) {
289
+ delete result[APP_BRANCH];
290
+ }
291
+ }
292
+ for (const key of APP_GIT_KEYS) {
293
+ if (properties && own(properties, key)) result[key] = properties[key];
294
+ }
295
+ return Object.freeze(result);
296
+ } catch (e) {
297
+ return Object.freeze({});
298
+ }
299
+ }
300
+ function validAppCommitSha(value) {
301
+ if (typeof value !== "string" || value.length > 128) return void 0;
302
+ const cleaned = value.trim();
303
+ return /^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/.test(cleaned) ? cleaned.toLowerCase() : void 0;
304
+ }
305
+ function shortString(value) {
306
+ if (typeof value !== "string" || value.length > 1024) return void 0;
307
+ const cleaned = value.trim();
308
+ return cleaned.length > 0 ? cleaned : void 0;
309
+ }
310
+ function flag(value) {
311
+ if (value === true || value === "true" || value === "1") return true;
312
+ if (value === false || value === "false" || value === "0") return false;
313
+ return void 0;
314
+ }
315
+ function environment() {
316
+ try {
317
+ return typeof process === "undefined" ? {} : process.env;
318
+ } catch (e) {
319
+ return {};
320
+ }
321
+ }
322
+ var DEPLOYMENTS = [
323
+ ["VERCEL", "VERCEL_GIT_COMMIT_SHA", "VERCEL_GIT_COMMIT_REF"],
324
+ ["NETLIFY", "COMMIT_REF", "BRANCH"],
325
+ ["RAILWAY_ENVIRONMENT_NAME", "RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH"],
326
+ ["RAILWAY_ENVIRONMENT", "RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH"],
327
+ ["RENDER", "RENDER_GIT_COMMIT", "RENDER_GIT_BRANCH"],
328
+ ["DYNO", "HEROKU_BUILD_COMMIT", ""],
329
+ ["DYNO", "HEROKU_SLUG_COMMIT", ""],
330
+ ["AWS_APP_ID", "AWS_COMMIT_ID", "AWS_BRANCH"],
331
+ ["WORKERS_CI", "WORKERS_CI_COMMIT_SHA", "WORKERS_CI_BRANCH"]
332
+ ];
333
+ var CI_SOURCES = [
334
+ ["GITHUB_ACTIONS", "GITHUB_SHA", "GITHUB_REF_NAME"],
335
+ ["GITLAB_CI", "CI_COMMIT_SHA", "CI_COMMIT_REF_NAME"],
336
+ ["CIRCLECI", "CIRCLE_SHA1", "CIRCLE_BRANCH"]
337
+ ];
338
+ function detectedSource(env, table, detectBranch) {
339
+ for (const row of table) {
340
+ const [marker, shaKey, branchKey] = row;
341
+ if (!marker || !shaKey || !shortString(env[marker]) || flag(env[marker]) === false) continue;
342
+ const commitSha = validAppCommitSha(env[shaKey]);
343
+ if (commitSha)
344
+ return {
345
+ commitSha,
346
+ ...detectBranch && branchKey ? { branch: shortString(env[branchKey]) } : {}
347
+ };
348
+ }
349
+ return {};
350
+ }
351
+ var AppGitContextImpl = class {
352
+ constructor(options, runtime = {}) {
353
+ this.defaults = {};
354
+ this.explicit = {};
355
+ this.explicitKeys = /* @__PURE__ */ new Set();
356
+ this.disposed = false;
357
+ this.operations = /* @__PURE__ */ new Map();
358
+ this.operationOverflow = false;
359
+ this.detectBranch = false;
360
+ this.disabled = false;
361
+ var _a, _b, _c, _d, _e, _f;
362
+ try {
363
+ if (options === false) {
364
+ this.disabled = true;
365
+ return;
366
+ }
367
+ const opts = options != null ? options : {};
368
+ const env = (_a = runtime.env) != null ? _a : environment();
369
+ const detectBranch = (_c = (_b = flag(opts.detectBranch)) != null ? _b : flag(env.RAINDROP_GIT_DETECT_BRANCH)) != null ? _c : false;
370
+ this.detectBranch = detectBranch;
371
+ const collectBranch = detectBranch || runtime.captureLegacyReplayBranch === true;
372
+ const autoDetect = runtime.allowDiscovery === false ? false : (_e = (_d = flag(opts.autoDetect)) != null ? _d : flag(env.RAINDROP_GIT_AUTO_DETECT)) != null ? _e : true;
373
+ const configuredSha = opts.commitSha;
374
+ const explicitSha = configuredSha === void 0 ? env.RAINDROP_COMMIT_SHA : configuredSha;
375
+ if (explicitSha === null) this.explicit[APP_COMMIT_SHA] = null;
376
+ else if (typeof explicitSha === "string" && validAppCommitSha(explicitSha))
377
+ this.explicit[APP_COMMIT_SHA] = explicitSha;
378
+ const configuredDirty = opts.commitDirty;
379
+ const explicitDirty = configuredDirty === void 0 ? flag(env.RAINDROP_COMMIT_DIRTY) : configuredDirty;
380
+ if (explicitDirty === null || typeof explicitDirty === "boolean")
381
+ this.explicit[APP_COMMIT_DIRTY] = explicitDirty;
382
+ const configuredBranch = opts.branch;
383
+ const explicitBranch = configuredBranch === void 0 ? env.RAINDROP_BRANCH : configuredBranch;
384
+ if (explicitBranch === null) this.explicit[APP_BRANCH] = null;
385
+ else if (typeof explicitBranch === "string" && explicitBranch.length <= 1024)
386
+ this.explicit[APP_BRANCH] = explicitBranch;
387
+ this.explicitKeys = appGitExplicitKeys(this.explicit);
388
+ this.defaults = { ...this.explicit };
389
+ if (!autoDetect || explicitSha !== void 0) return;
390
+ const sourceDirectory = (_f = shortString(opts.sourceDirectory)) != null ? _f : shortString(env.RAINDROP_GIT_SOURCE_DIRECTORY);
391
+ if (runtime.allowLocalGit === false && !sourceDirectory) return;
392
+ const deploy = sourceDirectory ? {} : detectedSource(env, DEPLOYMENTS, collectBranch);
393
+ if (deploy.commitSha) {
394
+ this.accept(deploy);
395
+ return;
396
+ }
397
+ const fallback = sourceDirectory ? {} : detectedSource(env, CI_SOURCES, collectBranch);
398
+ if (runtime.resolveLocal && (runtime.allowLocalGit !== false || sourceDirectory)) {
399
+ this.cancel = runtime.resolveLocal(sourceDirectory, collectBranch, (result) => {
400
+ try {
401
+ if (!this.disposed) this.accept(result.commitSha ? result : fallback);
402
+ } catch (e) {
403
+ }
404
+ });
405
+ } else this.accept(fallback);
406
+ } catch (e) {
407
+ }
408
+ }
409
+ accept(result) {
410
+ const sha = validAppCommitSha(result.commitSha);
411
+ if (!sha) return;
412
+ this.replayBranch = shortString(result.branch);
413
+ this.defaults = {
414
+ [APP_COMMIT_SHA]: sha,
415
+ ...typeof result.commitDirty === "boolean" ? { [APP_COMMIT_DIRTY]: result.commitDirty } : {},
416
+ ...this.detectBranch && this.replayBranch ? { [APP_BRANCH]: this.replayBranch } : {},
417
+ ...this.explicit
418
+ };
419
+ }
420
+ snapshot(properties) {
421
+ var _a;
422
+ try {
423
+ const binding = currentRoutingBinding();
424
+ const base = this.disabled && (binding == null ? void 0 : binding.appGitSource) !== "replay-override" ? {} : (_a = binding == null ? void 0 : binding.appGit) != null ? _a : this.defaults;
425
+ return this.mergeSnapshot(base, properties);
426
+ } catch (e) {
427
+ return Object.freeze({});
428
+ }
429
+ }
430
+ /** Safe defaults when no operation/span start was observed; never infer identity. */
431
+ snapshotExplicit(properties, eventId) {
432
+ try {
433
+ const base = { ...this.explicit };
434
+ const operation = eventId ? this.operations.get(eventId) : void 0;
435
+ if (operation) {
436
+ for (const key of operation.explicitKeys) base[key] = operation.snapshot[key];
437
+ }
438
+ return this.mergeSnapshot(base, properties, new Set(Object.keys(base)));
439
+ } catch (e) {
440
+ return mergeAppGitSnapshot({}, properties);
441
+ }
442
+ }
443
+ /** Existing replay branch reporting stays separate from opt-in event properties. */
444
+ snapshotForReplay() {
445
+ const snapshot = this.snapshot();
446
+ if (this.replayBranch && !own(snapshot, APP_BRANCH) && snapshot[APP_COMMIT_SHA] === this.defaults[APP_COMMIT_SHA]) {
447
+ return Object.freeze({ ...snapshot, [APP_BRANCH]: this.replayBranch });
448
+ }
449
+ return snapshot;
450
+ }
451
+ /** Merge a frozen span snapshot without losing client-authored provenance. */
452
+ mergeSnapshot(base, properties, explicitKeys = this.explicitKeys) {
453
+ return mergeAppGitSnapshot(base, properties, /* @__PURE__ */ new Set([...this.explicitKeys, ...explicitKeys]));
454
+ }
455
+ /** Shippers belonging to one client share this bounded operation registry. */
456
+ forOperation(eventId, properties, owner) {
457
+ if (!eventId) return this.snapshot(properties);
458
+ let operation = this.operations.get(eventId);
459
+ if (!operation) {
460
+ if (this.operations.size >= 1e4) this.operationOverflow = true;
461
+ if (this.operationOverflow) return this.mergeSnapshot({}, properties);
462
+ operation = {
463
+ snapshot: this.snapshot(properties),
464
+ owners: /* @__PURE__ */ new Set(),
465
+ unowned: owner === void 0,
466
+ explicitKeys: appGitExplicitKeys(properties)
467
+ };
468
+ this.operations.set(eventId, operation);
469
+ } else if (properties && APP_GIT_KEYS.some((key) => own(properties, key))) {
470
+ const explicitKeys = /* @__PURE__ */ new Set([...this.explicitKeys, ...operation.explicitKeys]);
471
+ operation.snapshot = this.mergeSnapshot(operation.snapshot, properties, explicitKeys);
472
+ for (const key of appGitExplicitKeys(properties)) operation.explicitKeys.add(key);
473
+ }
474
+ if (!owner) operation.unowned = true;
475
+ if (owner) operation.owners.add(owner);
476
+ return operation.snapshot;
477
+ }
478
+ releaseOperation(eventId, owner) {
479
+ const operation = this.operations.get(eventId);
480
+ if (!operation) return;
481
+ operation.owners.delete(owner);
482
+ if (operation.owners.size === 0 && !operation.unowned) this.operations.delete(eventId);
483
+ }
484
+ finishOperation(eventId) {
485
+ this.operations.delete(eventId);
486
+ }
487
+ dispose() {
488
+ var _a;
489
+ this.disposed = true;
490
+ try {
491
+ (_a = this.cancel) == null ? void 0 : _a.call(this);
492
+ } catch (e) {
493
+ }
494
+ }
495
+ };
496
+ var AppGitContext = AppGitContextImpl;
497
+ function createAppGitContext(options, runtime = {}) {
498
+ return new AppGitContext(options, runtime);
499
+ }
500
+ function withAppGitProperties(properties, snapshot) {
501
+ try {
502
+ const merged = { ...properties };
503
+ for (const key of APP_GIT_KEYS)
504
+ if (!own(merged, key) && own(snapshot, key)) merged[key] = snapshot[key];
505
+ return merged;
506
+ } catch (e) {
507
+ return properties != null ? properties : {};
508
+ }
509
+ }
510
+ function withAppGitAttributes(attributes, snapshot) {
511
+ try {
512
+ const present = new Set(attributes.flatMap((attr) => attr ? [attr.key] : []));
513
+ const added = [];
514
+ for (const key of APP_GIT_KEYS) {
515
+ if (present.has(key) || present.has(`ai.telemetry.metadata.${key}`) || present.has(`traceloop.association.properties.${key}`))
516
+ continue;
517
+ const value = snapshot[key];
518
+ if (typeof value === "string") added.push({ key, value: { stringValue: value } });
519
+ else if (typeof value === "boolean") added.push({ key, value: { boolValue: value } });
520
+ }
521
+ return added.length ? [...attributes, ...added] : attributes;
522
+ } catch (e) {
523
+ return attributes;
524
+ }
525
+ }
526
+ function appGitPropertiesFromAttributes(attributes) {
527
+ var _a, _b, _c;
528
+ const properties = {};
529
+ try {
530
+ for (const attr of attributes) {
531
+ if (!attr) continue;
532
+ for (const key of APP_GIT_KEYS) {
533
+ if (attr.key === key || attr.key === `ai.telemetry.metadata.${key}` || attr.key === `traceloop.association.properties.${key}`) {
534
+ properties[key] = (_c = (_b = (_a = attr.value.stringValue) != null ? _a : attr.value.boolValue) != null ? _b : attr.value.intValue) != null ? _c : null;
535
+ }
536
+ }
537
+ }
538
+ } catch (e) {
539
+ }
540
+ return properties;
541
+ }
28
542
  function getCrypto() {
29
543
  const c = globalThis.crypto;
30
544
  return c;
@@ -79,6 +593,25 @@ function base64Encode(bytes) {
79
593
  }
80
594
  return out;
81
595
  }
596
+ function base64ToHex(value) {
597
+ if (!value) return "";
598
+ const maybeBuffer = globalThis.Buffer;
599
+ if (maybeBuffer) {
600
+ return maybeBuffer.from(value, "base64").toString("hex");
601
+ }
602
+ const atobFn = globalThis.atob;
603
+ if (typeof atobFn !== "function") return "";
604
+ try {
605
+ const binary = atobFn(value);
606
+ let hex = "";
607
+ for (let i = 0; i < binary.length; i++) {
608
+ hex += (binary.charCodeAt(i) & 255).toString(16).padStart(2, "0");
609
+ }
610
+ return hex;
611
+ } catch (e) {
612
+ return "";
613
+ }
614
+ }
82
615
  function generateId() {
83
616
  return randomUUID();
84
617
  }
@@ -113,7 +646,7 @@ function runWithTracingSuppressed(fn) {
113
646
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
114
647
  var MAX_RETRY_DELAY_MS = 3e4;
115
648
  function wait(ms) {
116
- return new Promise((resolve) => setTimeout(resolve, ms));
649
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
117
650
  }
118
651
  function formatEndpoint(endpoint) {
119
652
  if (!endpoint) return void 0;
@@ -150,9 +683,9 @@ async function raceWithTimeout(promise, timeoutMs) {
150
683
  () => true,
151
684
  () => true
152
685
  ),
153
- new Promise((resolve) => {
686
+ new Promise((resolve2) => {
154
687
  var _a;
155
- timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
688
+ timer = setTimeout(() => resolve2(false), Math.max(0, timeoutMs));
156
689
  (_a = timer.unref) == null ? void 0 : _a.call(timer);
157
690
  })
158
691
  ]);
@@ -223,6 +756,7 @@ async function postJson(url, body, headers, opts) {
223
756
  const resp = await runWithTracingSuppressed(
224
757
  () => fetch(url, {
225
758
  method: "POST",
759
+ redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
226
760
  headers: {
227
761
  "Content-Type": "application/json",
228
762
  ...headers
@@ -391,26 +925,29 @@ function resolveLocalDebuggerBaseUrl(baseUrl) {
391
925
  if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
392
926
  return null;
393
927
  }
928
+ var LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS = 2e3;
394
929
  function mirrorTraceExportToLocalDebugger(body, options = {}) {
395
930
  var _a;
396
931
  const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
397
- if (!baseUrl) return;
398
- void postJson(`${baseUrl}traces`, body, {}, {
932
+ if (!baseUrl) return Promise.resolve();
933
+ return postJson(`${baseUrl}traces`, body, {}, {
399
934
  maxAttempts: 1,
400
935
  debug: (_a = options.debug) != null ? _a : false,
401
- sdkName: options.sdkName
936
+ sdkName: options.sdkName,
937
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
402
938
  }).catch(() => {
403
939
  });
404
940
  }
405
941
  function mirrorPartialEventToLocalDebugger(event, options = {}) {
406
942
  var _a;
407
943
  const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
408
- if (!baseUrl) return;
944
+ if (!baseUrl) return Promise.resolve();
409
945
  const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
410
- void postJson(`${baseUrl}events/track_partial`, event, headers, {
946
+ return postJson(`${baseUrl}events/track_partial`, event, headers, {
411
947
  maxAttempts: 1,
412
948
  debug: (_a = options.debug) != null ? _a : false,
413
- sdkName: options.sdkName
949
+ sdkName: options.sdkName,
950
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
414
951
  }).catch(() => {
415
952
  });
416
953
  }
@@ -435,6 +972,13 @@ function projectIdHeaders(projectId) {
435
972
  }
436
973
  var SHUTDOWN_DEADLINE_MS = 1e4;
437
974
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
975
+ function appGitOwnerId(prefix) {
976
+ try {
977
+ return `${prefix}:${randomUUID()}`;
978
+ } catch (e) {
979
+ return `${prefix}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
980
+ }
981
+ }
438
982
  function mergePatches(target, source) {
439
983
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
440
984
  const out = { ...target, ...source };
@@ -484,22 +1028,25 @@ function eventTelemetryProperties(patch) {
484
1028
  }
485
1029
  var EventShipper = class {
486
1030
  constructor(opts) {
1031
+ this.appGitOwner = appGitOwnerId("event");
487
1032
  this.buffers = /* @__PURE__ */ new Map();
488
1033
  this.sticky = /* @__PURE__ */ new Map();
489
1034
  this.timers = /* @__PURE__ */ new Map();
490
1035
  this.inFlight = /* @__PURE__ */ new Set();
491
1036
  this.hasShutdown = false;
492
- var _a, _b, _c, _d, _e, _f, _g, _h;
493
- this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
494
- this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
1037
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1038
+ this.appGitContext = (_a = opts.appGitContext) != null ? _a : createAppGitContext(opts.appGit);
1039
+ this.ownsAppGitContext = opts.appGitContext === void 0;
1040
+ this.writeKey = (_b = opts.writeKey) == null ? void 0 : _b.trim();
1041
+ this.baseUrl = (_c = formatEndpoint(opts.endpoint)) != null ? _c : "https://api.raindrop.ai/v1/";
495
1042
  this.enabled = opts.enabled !== false;
496
1043
  this.debug = opts.debug;
497
- this.partialFlushMs = (_c = opts.partialFlushMs) != null ? _c : 1e3;
498
- this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
1044
+ this.partialFlushMs = (_d = opts.partialFlushMs) != null ? _d : 1e3;
1045
+ this.sdkName = (_e = opts.sdkName) != null ? _e : "core";
499
1046
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
500
- this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
1047
+ this.defaultEventName = (_f = opts.defaultEventName) != null ? _f : "ai_generation";
501
1048
  this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
502
- this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
1049
+ this.localDebuggerUrl = (_g = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _g : void 0;
503
1050
  if (this.debug && this.localDebuggerUrl) {
504
1051
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
505
1052
  }
@@ -510,8 +1057,8 @@ var EventShipper = class {
510
1057
  const isNode = typeof process !== "undefined" && typeof process.version === "string";
511
1058
  this.context = {
512
1059
  library: {
513
- name: (_g = opts.libraryName) != null ? _g : "@raindrop-ai/core",
514
- version: (_h = opts.libraryVersion) != null ? _h : "0.0.0"
1060
+ name: (_h = opts.libraryName) != null ? _h : "@raindrop-ai/core",
1061
+ version: (_i = opts.libraryVersion) != null ? _i : "0.0.0"
515
1062
  },
516
1063
  metadata: {
517
1064
  jsRuntime: isNode ? "node" : "web",
@@ -519,6 +1066,9 @@ var EventShipper = class {
519
1066
  }
520
1067
  };
521
1068
  }
1069
+ ownAppGitContextForShutdown() {
1070
+ this.ownsAppGitContext = true;
1071
+ }
522
1072
  isDebugEnabled() {
523
1073
  return this.debug;
524
1074
  }
@@ -567,6 +1117,15 @@ var EventShipper = class {
567
1117
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
568
1118
  if (!this.enabled) return;
569
1119
  if (!eventId || !eventId.trim()) return;
1120
+ const capturedSticky = this.sticky.get(eventId);
1121
+ const eventAlreadyKnown = capturedSticky !== void 0 || this.buffers.has(eventId);
1122
+ const destination = currentReplayTraceDestination();
1123
+ const cloudReplay = destination !== void 0 && destination.kind !== "workshop";
1124
+ if ((capturedSticky == null ? void 0 : capturedSticky.replayOnly) === true || !eventAlreadyKnown && cloudReplay) {
1125
+ if (patch.isPending === false) this.sticky.delete(eventId);
1126
+ else this.sticky.set(eventId, { ...capturedSticky, replayOnly: true });
1127
+ return;
1128
+ }
570
1129
  const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
571
1130
  if (typeof patch.input === "string" && patch.input.length > maxChars) {
572
1131
  patch = { ...patch, input: capText(patch.input, maxChars) };
@@ -588,6 +1147,13 @@ var EventShipper = class {
588
1147
  }
589
1148
  const sticky = (_c = this.sticky.get(eventId)) != null ? _c : {};
590
1149
  const existing = (_d = this.buffers.get(eventId)) != null ? _d : {};
1150
+ patch = {
1151
+ ...patch,
1152
+ properties: withAppGitProperties(
1153
+ patch.properties,
1154
+ this.appGitContext.forOperation(eventId, patch.properties, this.appGitOwner)
1155
+ )
1156
+ };
591
1157
  const merged = mergePatches(existing, patch);
592
1158
  merged.isPending = (_g = (_f = (_e = patch.isPending) != null ? _e : existing.isPending) != null ? _f : sticky.isPending) != null ? _g : true;
593
1159
  this.buffers.set(eventId, merged);
@@ -601,6 +1167,8 @@ var EventShipper = class {
601
1167
  if (t) clearTimeout(t);
602
1168
  if (merged.isPending === false) {
603
1169
  await this.flushOne(eventId);
1170
+ if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(eventId, this.appGitOwner);
1171
+ else this.appGitContext.finishOperation(eventId);
604
1172
  return;
605
1173
  }
606
1174
  const timeout = setTimeout(() => {
@@ -625,15 +1193,21 @@ var EventShipper = class {
625
1193
  try {
626
1194
  for (const t of this.timers.values()) clearTimeout(t);
627
1195
  this.timers.clear();
1196
+ for (const [eventId, sticky] of this.sticky) {
1197
+ if (sticky.replayOnly) this.sticky.delete(eventId);
1198
+ }
628
1199
  const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
629
1200
  if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
630
1201
  } finally {
631
1202
  this.shutdownDeadlineAt = void 0;
1203
+ if (this.ownsAppGitContext) this.appGitContext.dispose();
632
1204
  }
633
1205
  }
634
1206
  async trackSignal(signal) {
635
1207
  var _a, _b;
636
1208
  if (!this.enabled) return;
1209
+ const destination = currentReplayTraceDestination();
1210
+ if (destination && destination.kind !== "workshop") return;
637
1211
  const body = [
638
1212
  {
639
1213
  event_id: signal.eventId,
@@ -668,6 +1242,8 @@ var EventShipper = class {
668
1242
  }
669
1243
  async identify(users) {
670
1244
  if (!this.enabled) return;
1245
+ const destination = currentReplayTraceDestination();
1246
+ if (destination && destination.kind !== "workshop") return;
671
1247
  const list = Array.isArray(users) ? users : [users];
672
1248
  const body = list.filter((user) => {
673
1249
  if (!(user == null ? void 0 : user.userId) || !user.userId.trim()) {
@@ -720,8 +1296,9 @@ var EventShipper = class {
720
1296
  this.buffers.delete(eventId);
721
1297
  if (!accumulated) return;
722
1298
  const sticky = (_a = this.sticky.get(eventId)) != null ? _a : {};
723
- const eventName = (_c = (_b = accumulated.eventName) != null ? _b : sticky.eventName) != null ? _c : this.defaultEventName;
724
- const userId = (_d = accumulated.userId) != null ? _d : sticky.userId;
1299
+ const isPending = (_c = (_b = accumulated.isPending) != null ? _b : sticky.isPending) != null ? _c : true;
1300
+ const eventName = (_e = (_d = accumulated.eventName) != null ? _d : sticky.eventName) != null ? _e : this.defaultEventName;
1301
+ const userId = (_f = accumulated.userId) != null ? _f : sticky.userId;
725
1302
  if (!userId) {
726
1303
  if (this.debug) {
727
1304
  console.warn(`${this.prefix} skipping track_partial for ${eventId}: missing userId`);
@@ -729,14 +1306,13 @@ var EventShipper = class {
729
1306
  this.sticky.delete(eventId);
730
1307
  return;
731
1308
  }
732
- const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
1309
+ const { wizardSession, ...restProperties } = (_g = accumulated.properties) != null ? _g : {};
733
1310
  let telemetryProperties = {};
734
1311
  try {
735
1312
  telemetryProperties = eventTelemetryProperties(accumulated);
736
1313
  } catch (e) {
737
1314
  }
738
- const convoId = (_f = accumulated.convoId) != null ? _f : sticky.convoId;
739
- const isPending = (_h = (_g = accumulated.isPending) != null ? _g : sticky.isPending) != null ? _h : true;
1315
+ const convoId = (_h = accumulated.convoId) != null ? _h : sticky.convoId;
740
1316
  const payload = {
741
1317
  event_id: eventId,
742
1318
  user_id: userId,
@@ -778,29 +1354,30 @@ var EventShipper = class {
778
1354
  endpoint: url
779
1355
  });
780
1356
  }
781
- if (this.localDebuggerUrl) {
782
- mirrorPartialEventToLocalDebugger(payload, {
783
- baseUrl: this.localDebuggerUrl,
784
- writeKey: this.writeKey,
785
- debug: this.debug,
786
- sdkName: this.sdkName
787
- });
788
- }
1357
+ const mirror = this.localDebuggerUrl ? mirrorPartialEventToLocalDebugger(payload, {
1358
+ baseUrl: this.localDebuggerUrl,
1359
+ writeKey: this.writeKey,
1360
+ debug: this.debug,
1361
+ sdkName: this.sdkName
1362
+ }) : void 0;
789
1363
  if (!this.writeKey) {
790
1364
  if (!isPending) this.sticky.delete(eventId);
1365
+ if (mirror) await mirror;
791
1366
  return;
792
1367
  }
793
1368
  const opts = this.requestOpts();
794
1369
  if (!opts) {
795
1370
  this.warnShutdownDrop(`track_partial ${eventId}`);
796
1371
  if (!isPending) this.sticky.delete(eventId);
1372
+ if (mirror) await mirror;
797
1373
  return;
798
1374
  }
799
1375
  const p = postJson(url, payload, this.requestHeaders(), opts);
800
- this.inFlight.add(p);
1376
+ const combined = mirror ? Promise.all([mirror, p]).then(() => void 0) : p;
1377
+ this.inFlight.add(combined);
801
1378
  try {
802
1379
  try {
803
- await p;
1380
+ await combined;
804
1381
  if (this.debug) {
805
1382
  console.log(`${this.prefix} sent track_partial ${eventId} (${eventName})`);
806
1383
  }
@@ -812,7 +1389,7 @@ var EventShipper = class {
812
1389
  );
813
1390
  }
814
1391
  } finally {
815
- this.inFlight.delete(p);
1392
+ this.inFlight.delete(combined);
816
1393
  }
817
1394
  if (!isPending) {
818
1395
  this.sticky.delete(eventId);
@@ -1065,6 +1642,13 @@ function redactJsonAttributeValue(key, value) {
1065
1642
  if (scrubbedJson === json) return void 0;
1066
1643
  return { stringValue: scrubbedJson };
1067
1644
  }
1645
+ function appGitOwnerId2(prefix) {
1646
+ try {
1647
+ return `${prefix}:${randomUUID()}`;
1648
+ } catch (e) {
1649
+ return `${prefix}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
1650
+ }
1651
+ }
1068
1652
  function identityAttrs(args) {
1069
1653
  return [
1070
1654
  attrString("ai.telemetry.metadata.raindrop.ai.userId", args.userId),
@@ -1087,23 +1671,30 @@ function applyOtelSpanAttributeLimit(limit) {
1087
1671
  }
1088
1672
  var TraceShipper = class {
1089
1673
  constructor(opts) {
1674
+ this.appGitOwner = appGitOwnerId2("trace");
1675
+ this.appGitEventIds = /* @__PURE__ */ new WeakMap();
1676
+ this.appGitActiveSpans = /* @__PURE__ */ new Map();
1677
+ this.appGitCaptured = /* @__PURE__ */ new WeakSet();
1678
+ this.appGitAdded = /* @__PURE__ */ new WeakMap();
1090
1679
  this.queue = [];
1091
1680
  this.inFlight = /* @__PURE__ */ new Set();
1092
1681
  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/";
1682
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1683
+ this.appGitContext = (_a = opts.appGitContext) != null ? _a : createAppGitContext(opts.appGit);
1684
+ this.ownsAppGitContext = opts.appGitContext === void 0;
1685
+ this.writeKey = (_b = opts.writeKey) == null ? void 0 : _b.trim();
1686
+ this.baseUrl = (_c = formatEndpoint(opts.endpoint)) != null ? _c : "https://api.raindrop.ai/v1/";
1096
1687
  this.enabled = opts.enabled !== false;
1097
1688
  this.debug = opts.debug;
1098
1689
  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";
1690
+ this.flushIntervalMs = (_d = opts.flushIntervalMs) != null ? _d : 1e3;
1691
+ this.maxBatchSize = (_e = opts.maxBatchSize) != null ? _e : 50;
1692
+ this.maxQueueSize = (_f = opts.maxQueueSize) != null ? _f : 5e3;
1693
+ this.sdkName = (_g = opts.sdkName) != null ? _g : "core";
1103
1694
  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;
1695
+ this.serviceName = (_h = opts.serviceName) != null ? _h : "raindrop.core";
1696
+ this.serviceVersion = (_i = opts.serviceVersion) != null ? _i : "0.0.0";
1697
+ this.localDebuggerUrl = (_j = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _j : void 0;
1107
1698
  if (this.debug && this.localDebuggerUrl) {
1108
1699
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
1109
1700
  }
@@ -1167,7 +1758,10 @@ var TraceShipper = class {
1167
1758
  try {
1168
1759
  const result = this.transformSpanHook(current);
1169
1760
  if (result === null) return null;
1170
- if (result !== void 0) current = result;
1761
+ if (result !== void 0) {
1762
+ copyReplayTraceDestination(current, result);
1763
+ current = result;
1764
+ }
1171
1765
  } catch (err) {
1172
1766
  if (this.debug) {
1173
1767
  const msg = err instanceof Error ? err.message : String(err);
@@ -1177,13 +1771,19 @@ var TraceShipper = class {
1177
1771
  }
1178
1772
  }
1179
1773
  try {
1180
- current = normalizeModelUsageSpan(current);
1774
+ const normalized = normalizeModelUsageSpan(current);
1775
+ copyReplayTraceDestination(current, normalized);
1776
+ current = normalized;
1181
1777
  } catch (e) {
1182
1778
  }
1183
1779
  if (!this.disableDefaultRedaction) {
1184
- current = defaultTransformSpan(current);
1780
+ const redacted = defaultTransformSpan(current);
1781
+ copyReplayTraceDestination(current, redacted);
1782
+ current = redacted;
1185
1783
  }
1186
- return this.capSpanAttributes(current);
1784
+ const capped = this.capSpanAttributes(current);
1785
+ copyReplayTraceDestination(current, capped);
1786
+ return capped;
1187
1787
  }
1188
1788
  isDebugEnabled() {
1189
1789
  return this.debug;
@@ -1195,46 +1795,84 @@ var TraceShipper = class {
1195
1795
  return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
1196
1796
  }
1197
1797
  startSpan(args) {
1198
- var _a, _b;
1798
+ var _a, _b, _c;
1199
1799
  const ids = createSpanIds(args.parent);
1200
1800
  const started = (_a = args.startTimeUnixNano) != null ? _a : nowUnixNanoString();
1201
1801
  const attrs = [
1202
1802
  attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
1203
1803
  ...identityAttrs(args),
1204
- attrString("ai.operationId", args.operationId)
1804
+ attrString("ai.operationId", args.operationId),
1805
+ ...Object.entries(evalScopeAttributes()).map(([key, value]) => attrString(key, value))
1205
1806
  ];
1206
1807
  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
- );
1808
+ const tracksAppGit = this.appGitActiveSpans.has(args.eventId) || this.appGitActiveSpans.size < 1e4;
1809
+ const appGitOverrides = appGitPropertiesFromAttributes(attrs);
1810
+ const span = {
1811
+ ids,
1812
+ name: args.name,
1813
+ startTimeUnixNano: started,
1814
+ attributes: withAppGitAttributes(
1815
+ attrs,
1816
+ this.appGitContext.forOperation(args.eventId, appGitOverrides, this.appGitOwner)
1817
+ )
1818
+ };
1819
+ rememberReplayTraceDestination(span);
1820
+ this.appGitCaptured.add(span);
1821
+ this.appGitEventIds.set(span, args.eventId);
1822
+ if (tracksAppGit) {
1823
+ this.appGitActiveSpans.set(args.eventId, ((_c = this.appGitActiveSpans.get(args.eventId)) != null ? _c : 0) + 1);
1824
+ }
1825
+ this.appGitAdded.set(span, new Set(span.attributes.filter((attr) => !attrs.includes(attr))));
1826
+ const openSpan = buildOtlpSpan({
1827
+ ids: span.ids,
1828
+ name: span.name,
1829
+ startTimeUnixNano: span.startTimeUnixNano,
1830
+ endTimeUnixNano: span.startTimeUnixNano,
1831
+ // placeholder — will be updated on endSpan
1832
+ attributes: span.attributes,
1833
+ status: { code: SpanStatusCode.UNSET }
1834
+ });
1835
+ copyReplayTraceDestination(span, openSpan);
1836
+ this.mirrorToLocalDebugger(openSpan);
1219
1837
  return span;
1220
1838
  }
1221
- mirrorToLocalDebugger(span) {
1222
- if (!this.localDebuggerUrl) return;
1839
+ mirrorToLocalDebugger(span, completed = false) {
1840
+ const destination = replayTraceDestinationFor(span);
1841
+ if (destination && destination.kind !== "workshop") return;
1842
+ const localDebuggerUrl = (destination == null ? void 0 : destination.kind) === "workshop" ? destination.url : this.localDebuggerUrl;
1843
+ if (!localDebuggerUrl) return;
1223
1844
  const redacted = this.redactSpan(span);
1224
1845
  if (redacted === null) return;
1846
+ if (completed && (destination == null ? void 0 : destination.kind) === "workshop") {
1847
+ recordReplayExportedSpan(
1848
+ destination,
1849
+ base64ToHex(redacted.spanId),
1850
+ base64ToHex(redacted.traceId)
1851
+ );
1852
+ }
1225
1853
  const body = buildExportTraceServiceRequest([redacted], this.serviceName, this.serviceVersion);
1226
- mirrorTraceExportToLocalDebugger(body, {
1227
- baseUrl: this.localDebuggerUrl,
1854
+ const p = mirrorTraceExportToLocalDebugger(body, {
1855
+ baseUrl: localDebuggerUrl,
1228
1856
  debug: false,
1229
1857
  sdkName: this.sdkName
1230
1858
  });
1859
+ this.inFlight.add(p);
1860
+ void p.finally(() => {
1861
+ this.inFlight.delete(p);
1862
+ });
1231
1863
  }
1232
1864
  endSpan(span, extra) {
1233
- var _a, _b;
1865
+ var _a, _b, _c;
1234
1866
  if (span.endTimeUnixNano) return;
1235
1867
  span.endTimeUnixNano = (_a = extra == null ? void 0 : extra.endTimeUnixNano) != null ? _a : nowUnixNanoString();
1236
1868
  if ((_b = extra == null ? void 0 : extra.attributes) == null ? void 0 : _b.length) {
1869
+ const overrides = appGitPropertiesFromAttributes(extra.attributes);
1870
+ const eventId2 = this.appGitEventIds.get(span);
1871
+ const snapshot = eventId2 ? this.appGitContext.forOperation(eventId2, overrides, this.appGitOwner) : overrides;
1872
+ const added = this.appGitAdded.get(span);
1873
+ span.attributes = span.attributes.filter((attr) => !(added == null ? void 0 : added.has(attr)) || !attr || !Object.prototype.hasOwnProperty.call(overrides, APP_COMMIT_SHA) && !Object.prototype.hasOwnProperty.call(overrides, attr.key));
1237
1874
  span.attributes.push(...extra.attributes);
1875
+ span.attributes = withAppGitAttributes(span.attributes, snapshot);
1238
1876
  }
1239
1877
  let status = extra == null ? void 0 : extra.status;
1240
1878
  if (!status && (extra == null ? void 0 : extra.error) !== void 0) {
@@ -1249,30 +1887,65 @@ var TraceShipper = class {
1249
1887
  attributes: span.attributes,
1250
1888
  status
1251
1889
  });
1890
+ copyReplayTraceDestination(span, otlp);
1891
+ this.appGitCaptured.add(otlp);
1252
1892
  this.enqueue(otlp);
1253
- this.mirrorToLocalDebugger(otlp);
1893
+ this.mirrorToLocalDebugger(otlp, true);
1894
+ const eventId = this.appGitEventIds.get(span);
1895
+ if (eventId && this.appGitActiveSpans.has(eventId)) {
1896
+ const remaining = ((_c = this.appGitActiveSpans.get(eventId)) != null ? _c : 1) - 1;
1897
+ if (remaining > 0) this.appGitActiveSpans.set(eventId, remaining);
1898
+ else {
1899
+ this.appGitActiveSpans.delete(eventId);
1900
+ if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(eventId, this.appGitOwner);
1901
+ else if (this.ownsAppGitContext) this.appGitContext.finishOperation(eventId);
1902
+ }
1903
+ }
1254
1904
  }
1255
1905
  createSpan(args) {
1256
1906
  var _a;
1257
1907
  const ids = createSpanIds(args.parent);
1258
1908
  const attrs = [
1259
1909
  attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
1260
- ...identityAttrs(args)
1910
+ ...identityAttrs(args),
1911
+ ...Object.entries(evalScopeAttributes()).map(([key, value]) => attrString(key, value))
1261
1912
  ];
1262
1913
  if ((_a = args.attributes) == null ? void 0 : _a.length) attrs.push(...args.attributes);
1914
+ const tracksAppGit = !this.appGitActiveSpans.has(args.eventId);
1263
1915
  const otlp = buildOtlpSpan({
1264
1916
  ids,
1265
1917
  name: args.name,
1266
1918
  startTimeUnixNano: args.startTimeUnixNano,
1267
1919
  endTimeUnixNano: args.endTimeUnixNano,
1268
- attributes: attrs,
1920
+ attributes: withAppGitAttributes(
1921
+ attrs,
1922
+ this.appGitContext.forOperation(
1923
+ args.eventId,
1924
+ appGitPropertiesFromAttributes(attrs),
1925
+ this.appGitOwner
1926
+ )
1927
+ ),
1269
1928
  status: args.status
1270
1929
  });
1930
+ rememberReplayTraceDestination(otlp);
1931
+ this.appGitCaptured.add(otlp);
1932
+ if (tracksAppGit) {
1933
+ if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(args.eventId, this.appGitOwner);
1934
+ else if (this.ownsAppGitContext) this.appGitContext.finishOperation(args.eventId);
1935
+ }
1271
1936
  this.enqueue(otlp);
1272
- this.mirrorToLocalDebugger(otlp);
1937
+ this.mirrorToLocalDebugger(otlp, true);
1273
1938
  }
1274
1939
  enqueue(span) {
1940
+ var _a;
1275
1941
  if (!this.enabled) return;
1942
+ const attributes = (_a = span.attributes) != null ? _a : [];
1943
+ const enriched = this.appGitCaptured.has(span) ? attributes : withAppGitAttributes(attributes, this.appGitContext.snapshot(appGitPropertiesFromAttributes(attributes)));
1944
+ if (enriched !== attributes) {
1945
+ const original = span;
1946
+ span = { ...span, attributes: enriched.filter((attr) => attr !== void 0) };
1947
+ copyReplayTraceDestination(original, span);
1948
+ }
1276
1949
  if (this.debugSpans) {
1277
1950
  const short = (s) => s ? s.slice(-8) : "none";
1278
1951
  console.log(
@@ -1283,6 +1956,13 @@ var TraceShipper = class {
1283
1956
  }
1284
1957
  const redacted = this.redactSpan(span);
1285
1958
  if (redacted === null) return;
1959
+ const destination = replayTraceDestinationFor(redacted);
1960
+ recordReplayExportedSpan(
1961
+ destination,
1962
+ base64ToHex(redacted.spanId),
1963
+ base64ToHex(redacted.traceId)
1964
+ );
1965
+ if ((destination == null ? void 0 : destination.kind) === "workshop") return;
1286
1966
  if (this.queue.length >= this.maxQueueSize) {
1287
1967
  this.queue.shift();
1288
1968
  }
@@ -1301,47 +1981,67 @@ var TraceShipper = class {
1301
1981
  }
1302
1982
  }
1303
1983
  async flush() {
1304
- if (!this.enabled) return;
1984
+ var _a;
1985
+ if (!this.enabled) {
1986
+ await Promise.all([...this.inFlight].map((p) => p.catch(() => {
1987
+ })));
1988
+ return;
1989
+ }
1305
1990
  if (this.timer) {
1306
1991
  clearTimeout(this.timer);
1307
1992
  this.timer = void 0;
1308
1993
  }
1309
1994
  while (this.queue.length > 0) {
1310
1995
  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;
1996
+ const groups = /* @__PURE__ */ new Map();
1997
+ for (const span of batch) {
1998
+ const destination = replayTraceDestinationFor(span);
1999
+ const spans = groups.get(destination);
2000
+ if (spans) spans.push(span);
2001
+ else groups.set(destination, [span]);
1321
2002
  }
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
- });
1329
- }
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);
2003
+ for (const [destination, spans] of groups) {
2004
+ if (!destination && !this.writeKey) continue;
2005
+ if ((destination == null ? void 0 : destination.kind) === "workshop") continue;
2006
+ const opts = this.requestOpts();
2007
+ if (!opts) {
1338
2008
  rateLimitedLog(
1339
- `${this.prefix}.send_spans_failed`,
1340
- () => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
2009
+ `${this.prefix}.shutdown_deadline`,
2010
+ () => console.warn(
2011
+ `${this.prefix} shutdown flush deadline exceeded; dropping ${spans.length} spans`
2012
+ )
1341
2013
  );
2014
+ continue;
2015
+ }
2016
+ const body = buildExportTraceServiceRequest(spans, this.serviceName, this.serviceVersion);
2017
+ const url = destination ? replayTraceUrl(destination.url) : `${this.baseUrl}traces`;
2018
+ const headers = destination ? {
2019
+ Authorization: `Bearer ${destination.apiKey}`,
2020
+ "X-Raindrop-Replay-Id": destination.replayId,
2021
+ "X-Raindrop-Project-Id": (_a = destination.projectId) != null ? _a : "default"
2022
+ } : this.requestHeaders();
2023
+ if (this.debug) {
2024
+ console.log(`${this.prefix} sending traces batch`, {
2025
+ spans: spans.length,
2026
+ endpoint: url
2027
+ });
2028
+ }
2029
+ const p = postJson(url, body, headers, opts);
2030
+ this.inFlight.add(p);
2031
+ try {
2032
+ try {
2033
+ await p;
2034
+ if (this.debug) console.log(`${this.prefix} sent ${spans.length} spans`);
2035
+ } catch (err) {
2036
+ const msg = err instanceof Error ? err.message : String(err);
2037
+ rateLimitedLog(
2038
+ `${this.prefix}.send_spans_failed`,
2039
+ () => console.warn(`${this.prefix} failed to send ${spans.length} spans: ${msg}`)
2040
+ );
2041
+ }
2042
+ } finally {
2043
+ this.inFlight.delete(p);
1342
2044
  }
1343
- } finally {
1344
- this.inFlight.delete(p);
1345
2045
  }
1346
2046
  }
1347
2047
  }
@@ -1384,17 +2084,165 @@ var TraceShipper = class {
1384
2084
  if (!settled) {
1385
2085
  rateLimitedLog(
1386
2086
  `${this.prefix}.shutdown_deadline`,
1387
- () => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
2087
+ () => console.warn(
2088
+ `${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`
2089
+ )
1388
2090
  );
1389
2091
  }
1390
2092
  } finally {
1391
2093
  this.shutdownDeadlineAt = void 0;
2094
+ if (this.ownsAppGitContext) this.appGitContext.dispose();
1392
2095
  }
1393
2096
  }
1394
2097
  };
1395
2098
 
1396
2099
  // ../core/dist/index.node.js
1397
2100
  var import_async_hooks = require("async_hooks");
2101
+ var import_child_process = require("child_process");
2102
+ var import_path = require("path");
2103
+ var resolveLocal = (sourceDirectory, detectBranch, completed) => {
2104
+ const cwd = sourceDirectory ? (0, import_path.resolve)(sourceDirectory) : process.cwd();
2105
+ let cancelled = false;
2106
+ let active;
2107
+ let deadlineTimer;
2108
+ const deadline = Date.now() + 1e3;
2109
+ const run = (args, done) => {
2110
+ if (cancelled) return;
2111
+ const remaining = deadline - Date.now();
2112
+ if (remaining <= 0) {
2113
+ done(void 0);
2114
+ return;
2115
+ }
2116
+ try {
2117
+ active = (0, import_child_process.execFile)(
2118
+ "git",
2119
+ args,
2120
+ {
2121
+ cwd,
2122
+ encoding: "utf8",
2123
+ maxBuffer: 4096,
2124
+ windowsHide: true,
2125
+ env: {
2126
+ ...process.env,
2127
+ // The selected application cwd, not the invoking shell's Git plumbing, owns identity.
2128
+ GIT_DIR: void 0,
2129
+ GIT_WORK_TREE: void 0,
2130
+ GIT_COMMON_DIR: void 0,
2131
+ GIT_NAMESPACE: void 0,
2132
+ GIT_INDEX_FILE: void 0,
2133
+ GIT_OBJECT_DIRECTORY: void 0,
2134
+ GIT_ALTERNATE_OBJECT_DIRECTORIES: void 0,
2135
+ GIT_CONFIG_PARAMETERS: void 0,
2136
+ GIT_CONFIG_COUNT: "0",
2137
+ GIT_TERMINAL_PROMPT: "0",
2138
+ GIT_OPTIONAL_LOCKS: "0"
2139
+ }
2140
+ },
2141
+ (error, stdout) => {
2142
+ clearTimeout(deadlineTimer);
2143
+ deadlineTimer = void 0;
2144
+ active = void 0;
2145
+ if (!cancelled) {
2146
+ try {
2147
+ done(error ? void 0 : stdout.trim());
2148
+ } catch (e) {
2149
+ }
2150
+ }
2151
+ }
2152
+ );
2153
+ active.unref();
2154
+ if (active.stdout && "unref" in active.stdout && typeof active.stdout.unref === "function")
2155
+ active.stdout.unref();
2156
+ if (active.stderr && "unref" in active.stderr && typeof active.stderr.unref === "function")
2157
+ active.stderr.unref();
2158
+ deadlineTimer = setTimeout(() => {
2159
+ try {
2160
+ active == null ? void 0 : active.kill("SIGKILL");
2161
+ } catch (e) {
2162
+ }
2163
+ }, remaining);
2164
+ deadlineTimer.unref();
2165
+ } catch (e) {
2166
+ try {
2167
+ done(void 0);
2168
+ } catch (e2) {
2169
+ }
2170
+ }
2171
+ };
2172
+ const scheduled = setTimeout(() => {
2173
+ try {
2174
+ run(["rev-parse", "HEAD"], (rawSha) => {
2175
+ const commitSha = validAppCommitSha(rawSha);
2176
+ if (!commitSha) {
2177
+ completed({});
2178
+ return;
2179
+ }
2180
+ run(["-c", "core.fsmonitor=false", "status", "--porcelain"], (status) => {
2181
+ const finish = (branch) => {
2182
+ run(["rev-parse", "HEAD"], (finalSha) => {
2183
+ const coherent = validAppCommitSha(finalSha) === commitSha;
2184
+ completed({
2185
+ commitSha,
2186
+ ...coherent && status !== void 0 ? { commitDirty: status !== "" } : {},
2187
+ ...coherent && branch && branch !== "HEAD" ? { branch } : {}
2188
+ });
2189
+ });
2190
+ };
2191
+ if (detectBranch) run(["symbolic-ref", "--quiet", "--short", "HEAD"], finish);
2192
+ else finish();
2193
+ });
2194
+ });
2195
+ } catch (e) {
2196
+ completed({});
2197
+ }
2198
+ }, 0);
2199
+ scheduled.unref();
2200
+ return () => {
2201
+ cancelled = true;
2202
+ clearTimeout(scheduled);
2203
+ clearTimeout(deadlineTimer);
2204
+ try {
2205
+ active == null ? void 0 : active.kill("SIGKILL");
2206
+ } catch (e) {
2207
+ }
2208
+ };
2209
+ };
2210
+ function createAppGitContext2(options, runtime = {}) {
2211
+ return new AppGitContext(options, { ...runtime, resolveLocal });
2212
+ }
2213
+ function hasDestination(options) {
2214
+ var _a;
2215
+ if (options.enabled === false) return false;
2216
+ return Boolean(((_a = options.writeKey) == null ? void 0 : _a.trim()) || resolveLocalDebuggerBaseUrl(options.localDebuggerUrl));
2217
+ }
2218
+ var EventShipper2 = class extends EventShipper {
2219
+ constructor(options) {
2220
+ var _a;
2221
+ const ownsAppGitContext = options.appGitContext === void 0;
2222
+ const appGitContext = (_a = options.appGitContext) != null ? _a : createAppGitContext2(options.appGit, { allowDiscovery: hasDestination(options) });
2223
+ try {
2224
+ super({ ...options, appGitContext });
2225
+ } catch (error) {
2226
+ if (ownsAppGitContext) appGitContext.dispose();
2227
+ throw error;
2228
+ }
2229
+ if (ownsAppGitContext) this.ownAppGitContextForShutdown();
2230
+ }
2231
+ };
2232
+ var TraceShipper2 = class extends TraceShipper {
2233
+ constructor(options) {
2234
+ var _a;
2235
+ const ownsAppGitContext = options.appGitContext === void 0;
2236
+ const appGitContext = (_a = options.appGitContext) != null ? _a : createAppGitContext2(options.appGit, { allowDiscovery: hasDestination(options) });
2237
+ try {
2238
+ super({ ...options, appGitContext });
2239
+ } catch (error) {
2240
+ if (ownsAppGitContext) appGitContext.dispose();
2241
+ throw error;
2242
+ }
2243
+ this.ownsAppGitContext = ownsAppGitContext;
2244
+ }
2245
+ };
1398
2246
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = import_async_hooks.AsyncLocalStorage;
1399
2247
  var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
1400
2248
  "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
@@ -1459,7 +2307,20 @@ function loadConfig(projectDirectory) {
1459
2307
  debug: process.env["RAINDROP_DEBUG"] === "true" ? true : (_g = merged.debug) != null ? _g : false,
1460
2308
  captureSystemPrompt: process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] !== void 0 ? process.env["RAINDROP_CAPTURE_SYSTEM_PROMPT"] === "true" : (_h = merged.capture_system_prompt) != null ? _h : false,
1461
2309
  eventMetadata,
1462
- localWorkshopUrl: resolveLocalWorkshopUrl(merged.local_workshop_url)
2310
+ localWorkshopUrl: resolveLocalWorkshopUrl(merged.local_workshop_url),
2311
+ appGit: mapAppGit(merged.app_git)
2312
+ };
2313
+ }
2314
+ function mapAppGit(value) {
2315
+ if (value === false) return false;
2316
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
2317
+ return {
2318
+ commitSha: "commit_sha" in value && (typeof value.commit_sha === "string" || value.commit_sha === null) ? value.commit_sha : void 0,
2319
+ commitDirty: "commit_dirty" in value && (typeof value.commit_dirty === "boolean" || value.commit_dirty === null) ? value.commit_dirty : void 0,
2320
+ branch: "branch" in value && (typeof value.branch === "string" || value.branch === null) ? value.branch : void 0,
2321
+ detectBranch: "detect_branch" in value && typeof value.detect_branch === "boolean" ? value.detect_branch : void 0,
2322
+ sourceDirectory: "source_directory" in value && typeof value.source_directory === "string" ? value.source_directory : void 0,
2323
+ autoDetect: "auto_detect" in value && typeof value.auto_detect === "boolean" ? value.auto_detect : void 0
1463
2324
  };
1464
2325
  }
1465
2326
  function resolveLocalWorkshopUrl(fileValue) {
@@ -1476,7 +2337,7 @@ function resolveLocalWorkshopUrl(fileValue) {
1476
2337
  // package.json
1477
2338
  var package_default = {
1478
2339
  name: "@raindrop-ai/opencode-plugin",
1479
- version: "0.1.3",
2340
+ version: "0.2.0",
1480
2341
  description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
1481
2342
  type: "module",
1482
2343
  main: "dist/index.js",
@@ -1492,6 +2353,11 @@ var package_default = {
1492
2353
  types: "./dist/index.d.ts",
1493
2354
  import: "./dist/index.js",
1494
2355
  require: "./dist/index.cjs"
2356
+ },
2357
+ "./v2": {
2358
+ types: "./dist/v2.d.ts",
2359
+ import: "./dist/v2.js",
2360
+ require: "./dist/v2.cjs"
1495
2361
  }
1496
2362
  },
1497
2363
  sideEffects: false,
@@ -1506,24 +2372,38 @@ var package_default = {
1506
2372
  },
1507
2373
  peerDependencies: {
1508
2374
  "@opencode-ai/plugin": ">=1.3.0",
1509
- "@opencode-ai/sdk": ">=1.3.0"
2375
+ "@opencode-ai/sdk": ">=1.3.0",
2376
+ "@opencode/plugin": ">=2.0.0",
2377
+ "@opencode/sdk": ">=2.0.0"
1510
2378
  },
1511
2379
  peerDependenciesMeta: {
2380
+ "@opencode-ai/plugin": {
2381
+ optional: true
2382
+ },
1512
2383
  "@opencode-ai/sdk": {
1513
2384
  optional: true
2385
+ },
2386
+ "@opencode/plugin": {
2387
+ optional: true
2388
+ },
2389
+ "@opencode/sdk": {
2390
+ optional: true
1514
2391
  }
1515
2392
  },
1516
2393
  devDependencies: {
1517
2394
  "@raindrop-ai/core": "workspace:*",
1518
2395
  "@opencode-ai/plugin": "^1.3.3",
1519
2396
  "@opencode-ai/sdk": "^1.3.3",
2397
+ "@opencode/plugin": "^2.0.10",
2398
+ "@opencode/sdk": "^2.0.10",
1520
2399
  "@types/node": "^20.11.17",
1521
2400
  tsup: "^8.4.0",
1522
2401
  typescript: "^5.3.3"
1523
2402
  },
1524
2403
  tsup: {
1525
2404
  entry: [
1526
- "src/index.ts"
2405
+ "src/index.ts",
2406
+ "src/v2.ts"
1527
2407
  ],
1528
2408
  format: [
1529
2409
  "cjs",
@@ -1547,7 +2427,7 @@ var PLUGIN_NAME = package_default.name;
1547
2427
  var PLUGIN_VERSION = package_default.version;
1548
2428
 
1549
2429
  // src/shipper.ts
1550
- var EventShipper2 = class extends EventShipper {
2430
+ var EventShipper3 = class extends EventShipper2 {
1551
2431
  constructor(opts) {
1552
2432
  var _a, _b, _c, _d;
1553
2433
  super({
@@ -1559,7 +2439,7 @@ var EventShipper2 = class extends EventShipper {
1559
2439
  });
1560
2440
  }
1561
2441
  };
1562
- var TraceShipper2 = class extends TraceShipper {
2442
+ var TraceShipper3 = class extends TraceShipper2 {
1563
2443
  constructor(opts) {
1564
2444
  var _a, _b, _c;
1565
2445
  super({
@@ -1717,11 +2597,11 @@ function createSessionParentMapHelpers({
1717
2597
  }) {
1718
2598
  function applyChildSessionParentToState(childSessionId) {
1719
2599
  const parentInfo = mapChildSessionToParent2.get(childSessionId);
1720
- const state = sessions2.get(childSessionId);
1721
- if (!parentInfo || !state) return;
1722
- state.parentId = parentInfo.parentId;
1723
- state.parentTaskSpanIds = parentInfo.parentTaskSpanIds;
1724
- state.parentEventContext = parentInfo.eventContext;
2600
+ const state2 = sessions2.get(childSessionId);
2601
+ if (!parentInfo || !state2) return;
2602
+ state2.parentId = parentInfo.parentId;
2603
+ state2.parentTaskSpanIds = parentInfo.parentTaskSpanIds;
2604
+ state2.parentEventContext = parentInfo.eventContext;
1725
2605
  }
1726
2606
  function queuePendingChildByCallKey(key, childSessionId) {
1727
2607
  var _a;
@@ -1817,10 +2697,10 @@ function markTaskCallFinished(sessionID, callID) {
1817
2697
  running.delete(callID);
1818
2698
  if (running.size === 0) runningTaskCallsBySession.delete(sessionID);
1819
2699
  }
1820
- function endPendingToolSpanWithError(state, callID, tool, toolCallArgs, errorMessage, traceShipper) {
1821
- const startInfo = state.toolSpanStarts.get(callID);
2700
+ function endPendingToolSpanWithError(state2, callID, tool, toolCallArgs, errorMessage, traceShipper) {
2701
+ const startInfo = state2.toolSpanStarts.get(callID);
1822
2702
  if (!startInfo) return false;
1823
- state.toolSpanStarts.delete(callID);
2703
+ state2.toolSpanStarts.delete(callID);
1824
2704
  const error = new Error(errorMessage);
1825
2705
  const endAttrs = [
1826
2706
  attrString("ai.toolCall.args", toolCallArgs),
@@ -1920,17 +2800,17 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
1920
2800
  if (info == null || typeof info !== "object")
1921
2801
  throw new Error("session.created: props.info is required");
1922
2802
  const parentID = info["parentID"];
1923
- let state = sessions.get(sessionID);
1924
- if (!state) {
1925
- state = createSessionState(sessionID);
1926
- sessions.set(sessionID, state);
2803
+ let state2 = sessions.get(sessionID);
2804
+ if (!state2) {
2805
+ state2 = createSessionState(sessionID);
2806
+ sessions.set(sessionID, state2);
1927
2807
  }
1928
- state.parentId = parentID;
1929
- if (state.parentId) mapChildSessionToParent.set(sessionID, { parentId: state.parentId });
2808
+ state2.parentId = parentID;
2809
+ if (state2.parentId) mapChildSessionToParent.set(sessionID, { parentId: state2.parentId });
1930
2810
  } else if (event.type === "session.compacted") {
1931
- const state = sessions.get(String(sessionID));
1932
- if (!state) return;
1933
- state.isCompacting = true;
2811
+ const state2 = sessions.get(String(sessionID));
2812
+ if (!state2) return;
2813
+ state2.isCompacting = true;
1934
2814
  } else if (event.type === "message.part.updated") {
1935
2815
  const part = props["part"];
1936
2816
  if (part == null || typeof part !== "object")
@@ -1942,10 +2822,10 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
1942
2822
  throw new Error("message.part.updated: part.sessionID is required");
1943
2823
  if (messageId == null || messageId === "")
1944
2824
  throw new Error("message.part.updated: part.messageID is required");
1945
- const state = sessions.get(partSessionID);
1946
- if (!state) return;
2825
+ const state2 = sessions.get(partSessionID);
2826
+ if (!state2) return;
1947
2827
  if (partObj["type"] === "text" && typeof partObj["text"] === "string") {
1948
- state.outputParts.set(messageId, capText2(partObj["text"]));
2828
+ state2.outputParts.set(messageId, capText2(partObj["text"]));
1949
2829
  } else if (partObj["type"] === "tool" && messageId) {
1950
2830
  const tool = partObj["tool"];
1951
2831
  const callID = partObj["callID"];
@@ -1977,7 +2857,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
1977
2857
  const input = toolState["input"];
1978
2858
  const toolCallArgs = input === void 0 ? void 0 : typeof input === "string" ? capText2(input) : boundedStringify(input);
1979
2859
  if (endPendingToolSpanWithError(
1980
- state,
2860
+ state2,
1981
2861
  callID,
1982
2862
  tool,
1983
2863
  toolCallArgs,
@@ -1988,7 +2868,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
1988
2868
  }
1989
2869
  }
1990
2870
  } else if (partObj["type"] === "reasoning" && typeof partObj["text"] === "string") {
1991
- state.reasoningParts.set(messageId, capText2(partObj["text"]));
2871
+ state2.reasoningParts.set(messageId, capText2(partObj["text"]));
1992
2872
  }
1993
2873
  } else if (event.type === "message.updated") {
1994
2874
  const msgInfo = props["info"];
@@ -2008,10 +2888,10 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
2008
2888
  throw new Error("message.updated: info.time is required");
2009
2889
  const timeObj = time;
2010
2890
  if (timeObj["completed"] == null) return;
2011
- const state = sessions.get(msgSessionID);
2012
- if (!state || !state.currentEventId || !state.currentRootSpan) return;
2013
- if (state.processedMessages.has(messageId)) return;
2014
- state.processedMessages.add(messageId);
2891
+ const state2 = sessions.get(msgSessionID);
2892
+ if (!state2 || !state2.currentEventId || !state2.currentRootSpan) return;
2893
+ if (state2.processedMessages.has(messageId)) return;
2894
+ state2.processedMessages.add(messageId);
2015
2895
  const tokens = info2["tokens"];
2016
2896
  if (tokens == null || typeof tokens !== "object")
2017
2897
  throw new Error("message.updated: info.tokens is required");
@@ -2028,13 +2908,13 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
2028
2908
  );
2029
2909
  const modelName = `${providerID}/${modelID}`;
2030
2910
  const finishReason = info2["finish"];
2031
- const outputText = state.outputParts.get(messageId);
2032
- const reasoningText = state.reasoningParts.get(messageId);
2911
+ const outputText = state2.outputParts.get(messageId);
2912
+ const reasoningText = state2.reasoningParts.get(messageId);
2033
2913
  const isToolCallsCompletion = finishReason === "tool-calls";
2034
2914
  if (outputText !== void 0 && outputText.trim().length > 0) {
2035
- state.turnOutputParts.push(outputText);
2915
+ state2.turnOutputParts.push(outputText);
2036
2916
  }
2037
- state.turnOutputTokens += outputTokens;
2917
+ state2.turnOutputTokens += outputTokens;
2038
2918
  if (outputText === void 0 && !isToolCallsCompletion) return;
2039
2919
  const msgError = info2["error"];
2040
2920
  const errorForSpan = msgError ? (() => {
@@ -2054,17 +2934,17 @@ type: ${name != null ? name : "UnknownError"}`;
2054
2934
  attrString("ai.response.text", responseText),
2055
2935
  ...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
2056
2936
  ];
2057
- if (state.currentSystemPrompt) {
2937
+ if (state2.currentSystemPrompt) {
2058
2938
  llmAttrs.push(
2059
2939
  attrString("gen_ai.prompt.0.role", "system"),
2060
- attrString("gen_ai.prompt.0.content", state.currentSystemPrompt)
2940
+ attrString("gen_ai.prompt.0.content", state2.currentSystemPrompt)
2061
2941
  );
2062
2942
  }
2063
- const llmParent = state.parentId && state.parentTaskSpanIds ? state.parentTaskSpanIds : state.currentRootSpan.ids;
2943
+ const llmParent = state2.parentId && state2.parentTaskSpanIds ? state2.parentTaskSpanIds : state2.currentRootSpan.ids;
2064
2944
  const llmSpan = traceShipper.startSpan({
2065
2945
  name: modelName,
2066
2946
  parent: llmParent,
2067
- eventId: state.currentEventId,
2947
+ eventId: state2.currentEventId,
2068
2948
  attributes: llmAttrs
2069
2949
  });
2070
2950
  if (typeof timeObj["created"] === "number") {
@@ -2075,40 +2955,40 @@ type: ${name != null ? name : "UnknownError"}`;
2075
2955
  return;
2076
2956
  }
2077
2957
  if (outputText === void 0) return;
2078
- const turnOutputText = capText2(state.turnOutputParts.join("\n\n"));
2079
- const rootSpan = state.currentRootSpan;
2080
- const isCompaction = state.isCompacting === true;
2081
- if (state.parentId) {
2082
- const userInput = unwrapQuotes(state.currentInput);
2958
+ const turnOutputText = capText2(state2.turnOutputParts.join("\n\n"));
2959
+ const rootSpan = state2.currentRootSpan;
2960
+ const isCompaction = state2.isCompacting === true;
2961
+ if (state2.parentId) {
2962
+ const userInput = unwrapQuotes(state2.currentInput);
2083
2963
  const llmAttrs = [
2084
2964
  attrString("ai.operationId", "generateText"),
2085
2965
  attrString("ai.prompt", userInput),
2086
2966
  attrString(
2087
2967
  "ai.prompt.messages",
2088
- buildPromptMessages(state.currentSystemPrompt, state.currentInput)
2968
+ buildPromptMessages(state2.currentSystemPrompt, state2.currentInput)
2089
2969
  ),
2090
2970
  attrString("ai.response.text", turnOutputText),
2091
2971
  ...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
2092
2972
  ];
2093
- if (state.currentSystemPrompt) {
2973
+ if (state2.currentSystemPrompt) {
2094
2974
  llmAttrs.push(
2095
2975
  attrString("gen_ai.prompt.0.role", "system"),
2096
- attrString("gen_ai.prompt.0.content", state.currentSystemPrompt)
2976
+ attrString("gen_ai.prompt.0.content", state2.currentSystemPrompt)
2097
2977
  );
2098
2978
  }
2099
- if (!state.parentTaskSpanIds) {
2979
+ if (!state2.parentTaskSpanIds) {
2100
2980
  traceShipper.endSpan(rootSpan, {
2101
2981
  error: "Missing strict parent task for child session"
2102
2982
  });
2103
- state.currentRootSpan = void 0;
2104
- state.currentEventId = void 0;
2983
+ state2.currentRootSpan = void 0;
2984
+ state2.currentEventId = void 0;
2105
2985
  await traceShipper.flush();
2106
2986
  return;
2107
2987
  }
2108
2988
  const finalLlmSpan = traceShipper.startSpan({
2109
2989
  name: modelName,
2110
- parent: state.parentTaskSpanIds,
2111
- eventId: state.currentEventId,
2990
+ parent: state2.parentTaskSpanIds,
2991
+ eventId: state2.currentEventId,
2112
2992
  attributes: llmAttrs
2113
2993
  });
2114
2994
  if (typeof timeObj["created"] === "number") {
@@ -2118,19 +2998,19 @@ type: ${name != null ? name : "UnknownError"}`;
2118
2998
  traceShipper.endSpan(rootSpan, {
2119
2999
  attributes: [
2120
3000
  attrString("is_subagent", "true"),
2121
- attrString("parent_session_id", state.parentId)
3001
+ attrString("parent_session_id", state2.parentId)
2122
3002
  ],
2123
3003
  error: errorForSpan
2124
3004
  });
2125
3005
  } else {
2126
3006
  rootSpan.name = isCompaction ? "ai.compaction" : modelName;
2127
- const userInput = unwrapQuotes(state.currentInput);
3007
+ const userInput = unwrapQuotes(state2.currentInput);
2128
3008
  const rootAttrs = [
2129
3009
  attrString("ai.operationId", "generateText"),
2130
3010
  attrString("ai.prompt", userInput),
2131
3011
  attrString(
2132
3012
  "ai.prompt.messages",
2133
- buildPromptMessages(state.currentSystemPrompt, state.currentInput)
3013
+ buildPromptMessages(state2.currentSystemPrompt, state2.currentInput)
2134
3014
  ),
2135
3015
  attrString("ai.response.text", turnOutputText),
2136
3016
  ...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
@@ -2139,10 +3019,10 @@ type: ${name != null ? name : "UnknownError"}`;
2139
3019
  rootAttrs.push(attrString("is_compaction", "true"));
2140
3020
  }
2141
3021
  rootAttrs.push(attrString("message_id", messageId));
2142
- if (state.currentSystemPrompt) {
3022
+ if (state2.currentSystemPrompt) {
2143
3023
  rootAttrs.push(
2144
3024
  attrString("gen_ai.prompt.0.role", "system"),
2145
- attrString("gen_ai.prompt.0.content", state.currentSystemPrompt)
3025
+ attrString("gen_ai.prompt.0.content", state2.currentSystemPrompt)
2146
3026
  );
2147
3027
  }
2148
3028
  traceShipper.endSpan(rootSpan, {
@@ -2150,24 +3030,24 @@ type: ${name != null ? name : "UnknownError"}`;
2150
3030
  error: errorForSpan
2151
3031
  });
2152
3032
  if (isCompaction) {
2153
- state.isCompacting = false;
3033
+ state2.isCompacting = false;
2154
3034
  }
2155
3035
  }
2156
- state.currentRootSpan = void 0;
3036
+ state2.currentRootSpan = void 0;
2157
3037
  const hasAssistantResponse = turnOutputText.trim().length > 0;
2158
3038
  if (!hasAssistantResponse && !msgError) {
2159
3039
  await traceShipper.flush();
2160
- state.currentEventId = void 0;
3040
+ state2.currentEventId = void 0;
2161
3041
  return;
2162
3042
  }
2163
- if (!state.parentId) {
2164
- await eventShipper.finish(state.currentEventId, {
2165
- userId: (_d = (_c = state.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state.sessionId,
3043
+ if (!state2.parentId) {
3044
+ await eventShipper.finish(state2.currentEventId, {
3045
+ userId: (_d = (_c = state2.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state2.sessionId,
2166
3046
  model: modelID,
2167
3047
  output: msgError ? void 0 : turnOutputText,
2168
3048
  usage: {
2169
3049
  promptTokens: inputTokens,
2170
- completionTokens: state.turnOutputTokens
3050
+ completionTokens: state2.turnOutputTokens
2171
3051
  },
2172
3052
  error: msgError ? {
2173
3053
  name: msgError.name,
@@ -2182,35 +3062,35 @@ type: ${name != null ? name : "UnknownError"}`;
2182
3062
  });
2183
3063
  }
2184
3064
  await traceShipper.flush();
2185
- state.currentEventId = void 0;
3065
+ state2.currentEventId = void 0;
2186
3066
  } else if (event.type === "session.idle") {
2187
3067
  if (!sessionID) return;
2188
- const state = sessions.get(String(sessionID));
2189
- if (!state) return;
2190
- if (state.currentRootSpan) {
2191
- traceShipper.endSpan(state.currentRootSpan);
2192
- state.currentRootSpan = void 0;
3068
+ const state2 = sessions.get(String(sessionID));
3069
+ if (!state2) return;
3070
+ if (state2.currentRootSpan) {
3071
+ traceShipper.endSpan(state2.currentRootSpan);
3072
+ state2.currentRootSpan = void 0;
2193
3073
  }
2194
- if (state.currentEventId) {
2195
- state.currentEventId = void 0;
3074
+ if (state2.currentEventId) {
3075
+ state2.currentEventId = void 0;
2196
3076
  }
2197
- state.isCompacting = false;
3077
+ state2.isCompacting = false;
2198
3078
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
2199
- if (state.parentId) {
3079
+ if (state2.parentId) {
2200
3080
  cleanupSessionParentMap(String(sessionID));
2201
3081
  sessions.delete(String(sessionID));
2202
3082
  } else {
2203
3083
  }
2204
3084
  } else if (event.type === "session.deleted") {
2205
3085
  if (!sessionID) return;
2206
- const state = sessions.get(String(sessionID));
2207
- if (!state) return;
2208
- if (state.currentRootSpan) {
2209
- traceShipper.endSpan(state.currentRootSpan);
2210
- state.currentRootSpan = void 0;
3086
+ const state2 = sessions.get(String(sessionID));
3087
+ if (!state2) return;
3088
+ if (state2.currentRootSpan) {
3089
+ traceShipper.endSpan(state2.currentRootSpan);
3090
+ state2.currentRootSpan = void 0;
2211
3091
  }
2212
- if (state.currentEventId) {
2213
- state.currentEventId = void 0;
3092
+ if (state2.currentEventId) {
3093
+ state2.currentEventId = void 0;
2214
3094
  }
2215
3095
  cleanupSessionParentMap(String(sessionID));
2216
3096
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
@@ -2219,8 +3099,8 @@ type: ${name != null ? name : "UnknownError"}`;
2219
3099
  const errorSessionID = (_h = props["sessionID"]) != null ? _h : sessionID;
2220
3100
  if (errorSessionID === void 0 || errorSessionID === "")
2221
3101
  throw new Error("session.error: missing sessionID");
2222
- const state = sessions.get(String(errorSessionID));
2223
- if (!state) return;
3102
+ const state2 = sessions.get(String(errorSessionID));
3103
+ if (!state2) return;
2224
3104
  const errorObj = props["error"];
2225
3105
  if (errorObj == null) throw new Error("session.error: missing error object");
2226
3106
  const errorName = errorObj.name;
@@ -2230,12 +3110,12 @@ type: ${name != null ? name : "UnknownError"}`;
2230
3110
  const errorStr = `${errorMessage != null ? errorMessage : errorName}
2231
3111
 
2232
3112
  type: ${errorName != null ? errorName : "UnknownError"}`;
2233
- if (state.currentRootSpan) {
2234
- traceShipper.endSpan(state.currentRootSpan, { error: errorStr });
2235
- state.currentRootSpan = void 0;
3113
+ if (state2.currentRootSpan) {
3114
+ traceShipper.endSpan(state2.currentRootSpan, { error: errorStr });
3115
+ state2.currentRootSpan = void 0;
2236
3116
  }
2237
- if (state.currentEventId) {
2238
- state.currentEventId = void 0;
3117
+ if (state2.currentEventId) {
3118
+ state2.currentEventId = void 0;
2239
3119
  }
2240
3120
  cleanupSessionParentMap(String(errorSessionID));
2241
3121
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
@@ -2255,15 +3135,15 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2255
3135
  var _a, _b, _c, _d, _e, _f, _g;
2256
3136
  try {
2257
3137
  const { sessionID } = messageInput;
2258
- let state = sessions.get(sessionID);
2259
- if (!state) {
2260
- state = createSessionState(sessionID);
2261
- sessions.set(sessionID, state);
3138
+ let state2 = sessions.get(sessionID);
3139
+ if (!state2) {
3140
+ state2 = createSessionState(sessionID);
3141
+ sessions.set(sessionID, state2);
2262
3142
  }
2263
3143
  applyChildSessionParentToState(sessionID);
2264
- if (state.currentRootSpan) {
2265
- traceShipper.endSpan(state.currentRootSpan);
2266
- state.currentRootSpan = void 0;
3144
+ if (state2.currentRootSpan) {
3145
+ traceShipper.endSpan(state2.currentRootSpan);
3146
+ state2.currentRootSpan = void 0;
2267
3147
  }
2268
3148
  if (output == null) throw new Error("chat.message: output is required");
2269
3149
  const parts = output.parts;
@@ -2294,39 +3174,39 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2294
3174
  }
2295
3175
  }
2296
3176
  }
2297
- state.eventMetadata = {
3177
+ state2.eventMetadata = {
2298
3178
  ...baseMeta,
2299
3179
  ...promptMeta,
2300
3180
  properties: baseMeta.properties || promptMeta.properties ? { ...(_a = baseMeta.properties) != null ? _a : {}, ...(_b = promptMeta.properties) != null ? _b : {} } : void 0
2301
3181
  };
2302
3182
  const userText = capText2(textParts.join("\n"));
2303
- state.currentInput = userText;
2304
- state.turnOutputParts = [];
2305
- state.turnOutputTokens = 0;
2306
- if (state.parentId) {
2307
- if (!state.parentTaskSpanIds || !state.parentEventContext) {
3183
+ state2.currentInput = userText;
3184
+ state2.turnOutputParts = [];
3185
+ state2.turnOutputTokens = 0;
3186
+ if (state2.parentId) {
3187
+ if (!state2.parentTaskSpanIds || !state2.parentEventContext) {
2308
3188
  return;
2309
3189
  }
2310
- state.currentEventId = state.parentEventContext.eventId;
2311
- state.currentRootSpan = traceShipper.startSpan({
3190
+ state2.currentEventId = state2.parentEventContext.eventId;
3191
+ state2.currentRootSpan = traceShipper.startSpan({
2312
3192
  name: "Subagent",
2313
- parent: state.parentTaskSpanIds,
2314
- eventId: state.currentEventId,
3193
+ parent: state2.parentTaskSpanIds,
3194
+ eventId: state2.currentEventId,
2315
3195
  attributes: [
2316
3196
  attrString("workspace", worktree),
2317
3197
  attrString("directory", directory),
2318
3198
  attrString("hostname", hostname),
2319
3199
  attrString("os", os),
2320
3200
  attrString("is_subagent", "true"),
2321
- attrString("parent_session_id", state.parentId)
3201
+ attrString("parent_session_id", state2.parentId)
2322
3202
  ]
2323
3203
  });
2324
3204
  return;
2325
3205
  }
2326
- state.currentEventId = generateId();
2327
- state.currentRootSpan = traceShipper.startSpan({
2328
- name: state.isCompacting ? "ai.compaction" : "ai.event",
2329
- eventId: state.currentEventId,
3206
+ state2.currentEventId = generateId();
3207
+ state2.currentRootSpan = traceShipper.startSpan({
3208
+ name: state2.isCompacting ? "ai.compaction" : "ai.event",
3209
+ eventId: state2.currentEventId,
2330
3210
  attributes: [
2331
3211
  attrString("workspace", worktree),
2332
3212
  attrString("directory", directory),
@@ -2334,11 +3214,11 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2334
3214
  attrString("os", os)
2335
3215
  ]
2336
3216
  });
2337
- await eventShipper.patch(state.currentEventId, {
3217
+ await eventShipper.patch(state2.currentEventId, {
2338
3218
  isPending: true,
2339
- userId: (_d = (_c = state.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state.sessionId,
2340
- convoId: state.sessionId,
2341
- eventName: (_f = (_e = state.eventMetadata) == null ? void 0 : _e.eventName) != null ? _f : config.eventName,
3219
+ userId: (_d = (_c = state2.eventMetadata) == null ? void 0 : _c.userId) != null ? _d : state2.sessionId,
3220
+ convoId: state2.sessionId,
3221
+ eventName: (_f = (_e = state2.eventMetadata) == null ? void 0 : _e.eventName) != null ? _f : config.eventName,
2342
3222
  input: userText,
2343
3223
  ...newAttachments.length > 0 ? { attachments: newAttachments } : {},
2344
3224
  properties: {
@@ -2347,7 +3227,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2347
3227
  hostname,
2348
3228
  os,
2349
3229
  plugin_version: PLUGIN_VERSION,
2350
- ...(_g = state.eventMetadata) == null ? void 0 : _g.properties
3230
+ ...(_g = state2.eventMetadata) == null ? void 0 : _g.properties
2351
3231
  }
2352
3232
  });
2353
3233
  } catch (err) {
@@ -2361,16 +3241,16 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2361
3241
  var _a, _b, _c;
2362
3242
  try {
2363
3243
  const { tool, sessionID, callID } = toolInput;
2364
- const state = sessions.get(sessionID);
2365
- if (!state || !state.currentEventId) return;
2366
- const spanParent = state.parentId && state.parentTaskSpanIds ? state.parentTaskSpanIds : (_a = state.currentRootSpan) == null ? void 0 : _a.ids;
3244
+ const state2 = sessions.get(sessionID);
3245
+ if (!state2 || !state2.currentEventId) return;
3246
+ const spanParent = state2.parentId && state2.parentTaskSpanIds ? state2.parentTaskSpanIds : (_a = state2.currentRootSpan) == null ? void 0 : _a.ids;
2367
3247
  if (!spanParent) return;
2368
3248
  const startTimeUnixNano = nowUnixNanoString();
2369
3249
  if (tool === "task") {
2370
3250
  const liveTaskSpan = traceShipper.startSpan({
2371
3251
  name: "ai.toolCall",
2372
3252
  parent: spanParent,
2373
- eventId: state.currentEventId,
3253
+ eventId: state2.currentEventId,
2374
3254
  attributes: [
2375
3255
  attrString("ai.operationId", "ai.toolCall"),
2376
3256
  attrString("ai.toolCall.name", tool),
@@ -2381,9 +3261,9 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2381
3261
  const ctx = {
2382
3262
  ids: liveTaskSpan.ids,
2383
3263
  eventContext: {
2384
- eventId: state.currentEventId,
2385
- userId: (_c = (_b = state.eventMetadata) == null ? void 0 : _b.userId) != null ? _c : state.sessionId,
2386
- convoId: state.sessionId
3264
+ eventId: state2.currentEventId,
3265
+ userId: (_c = (_b = state2.eventMetadata) == null ? void 0 : _b.userId) != null ? _c : state2.sessionId,
3266
+ convoId: state2.sessionId
2387
3267
  }
2388
3268
  };
2389
3269
  taskContexts.set(callKey(sessionID, callID), ctx);
@@ -2394,18 +3274,18 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2394
3274
  }
2395
3275
  running.add(callID);
2396
3276
  resolvePendingChildrenForCall(sessionID, callID, "tool.execute.before");
2397
- state.toolSpanStarts.set(callID, {
3277
+ state2.toolSpanStarts.set(callID, {
2398
3278
  startTimeUnixNano,
2399
3279
  parent: spanParent,
2400
- eventId: state.currentEventId,
3280
+ eventId: state2.currentEventId,
2401
3281
  name: tool,
2402
3282
  liveSpan: liveTaskSpan
2403
3283
  });
2404
3284
  } else {
2405
- state.toolSpanStarts.set(callID, {
3285
+ state2.toolSpanStarts.set(callID, {
2406
3286
  startTimeUnixNano,
2407
3287
  parent: spanParent,
2408
- eventId: state.currentEventId,
3288
+ eventId: state2.currentEventId,
2409
3289
  name: tool
2410
3290
  });
2411
3291
  }
@@ -2422,13 +3302,13 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2422
3302
  "tool.execute.after": async (toolInput, result) => {
2423
3303
  try {
2424
3304
  const { tool, sessionID, callID, args } = toolInput;
2425
- const state = sessions.get(sessionID);
2426
- if (!state || !state.currentEventId) return;
3305
+ const state2 = sessions.get(sessionID);
3306
+ if (!state2 || !state2.currentEventId) return;
2427
3307
  if (args === void 0 || args === null)
2428
3308
  throw new Error("tool.execute.after: args is required");
2429
3309
  const toolCallArgs = typeof args === "string" ? capText2(args) : boundedStringify(args);
2430
- const startInfo = state.toolSpanStarts.get(callID);
2431
- state.toolSpanStarts.delete(callID);
3310
+ const startInfo = state2.toolSpanStarts.get(callID);
3311
+ state2.toolSpanStarts.delete(callID);
2432
3312
  const resultMetadata = result.metadata;
2433
3313
  const childSessionId = resultMetadata == null ? void 0 : resultMetadata["sessionId"];
2434
3314
  if (tool === "task" && childSessionId) {
@@ -2481,9 +3361,9 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2481
3361
  try {
2482
3362
  const sessionID = input.sessionID;
2483
3363
  if (!sessionID) return;
2484
- const state = sessions.get(sessionID);
2485
- if (state) {
2486
- state.isCompacting = true;
3364
+ const state2 = sessions.get(sessionID);
3365
+ if (state2) {
3366
+ state2.isCompacting = true;
2487
3367
  }
2488
3368
  } catch (err) {
2489
3369
  rateLimitedErrorLog(
@@ -2500,15 +3380,15 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2500
3380
  if (!config.captureSystemPrompt) return;
2501
3381
  const sessionID = input.sessionID;
2502
3382
  if (!sessionID) return;
2503
- let state = sessions.get(sessionID);
2504
- if (!state) {
2505
- state = createSessionState(sessionID);
2506
- sessions.set(sessionID, state);
3383
+ let state2 = sessions.get(sessionID);
3384
+ if (!state2) {
3385
+ state2 = createSessionState(sessionID);
3386
+ sessions.set(sessionID, state2);
2507
3387
  }
2508
3388
  const MAX_SYSTEM_PROMPT_LENGTH = 32768;
2509
3389
  const TRUNCATION_MARKER3 = "\n...[truncated]";
2510
3390
  const joined = output.system.join("\n\n");
2511
- state.currentSystemPrompt = joined.length > MAX_SYSTEM_PROMPT_LENGTH ? joined.slice(0, MAX_SYSTEM_PROMPT_LENGTH - TRUNCATION_MARKER3.length) + TRUNCATION_MARKER3 : joined;
3391
+ state2.currentSystemPrompt = joined.length > MAX_SYSTEM_PROMPT_LENGTH ? joined.slice(0, MAX_SYSTEM_PROMPT_LENGTH - TRUNCATION_MARKER3.length) + TRUNCATION_MARKER3 : joined;
2512
3392
  } catch (err) {
2513
3393
  rateLimitedErrorLog(
2514
3394
  "experimental.chat.system.transform",
@@ -2520,7 +3400,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
2520
3400
  }
2521
3401
 
2522
3402
  // src/index.ts
2523
- function createRaindropHandle(eventShipper, traceShipper) {
3403
+ function createRaindropHandle(eventShipper, traceShipper, appGitContext) {
2524
3404
  return {
2525
3405
  users: {
2526
3406
  async identify(users) {
@@ -2531,12 +3411,19 @@ function createRaindropHandle(eventShipper, traceShipper) {
2531
3411
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
2532
3412
  },
2533
3413
  async shutdown() {
2534
- await Promise.all([eventShipper.shutdown(), traceShipper.shutdown()]);
3414
+ try {
3415
+ await Promise.all([eventShipper.shutdown(), traceShipper.shutdown()]);
3416
+ } finally {
3417
+ try {
3418
+ appGitContext.dispose();
3419
+ } catch (e) {
3420
+ }
3421
+ }
2535
3422
  }
2536
3423
  };
2537
3424
  }
2538
- async function plugin(input) {
2539
- var _a;
3425
+ async function plugin(input, opts = {}) {
3426
+ var _a, _b, _c;
2540
3427
  const config = loadConfig(input.directory);
2541
3428
  function appLog(level, message) {
2542
3429
  console.log(`[raindrop-ai/opencode-plugin] [${level}] ${message}`);
@@ -2558,21 +3445,28 @@ async function plugin(input) {
2558
3445
  ].filter(Boolean);
2559
3446
  appLog("info", `Raindrop tracing enabled \u2014 destinations: ${destinations.join(", ")}`);
2560
3447
  }
2561
- const eventShipper = new EventShipper2({
3448
+ const configuredAppGit = (_a = opts.appGit) != null ? _a : config.appGit;
3449
+ const appGitContext = createAppGitContext2(
3450
+ ((_b = config.writeKey) == null ? void 0 : _b.trim()) || hasLocalDestination ? configuredAppGit : false,
3451
+ { allowLocalGit: false }
3452
+ );
3453
+ const eventShipper = new EventShipper3({
2562
3454
  writeKey: config.writeKey,
2563
3455
  endpoint: config.endpoint,
2564
3456
  debug: config.debug,
2565
3457
  projectId: config.projectId,
2566
- localDebuggerUrl: config.localWorkshopUrl
3458
+ localDebuggerUrl: config.localWorkshopUrl,
3459
+ appGitContext
2567
3460
  });
2568
- const traceShipper = new TraceShipper2({
3461
+ const traceShipper = new TraceShipper3({
2569
3462
  writeKey: config.writeKey,
2570
3463
  endpoint: config.endpoint,
2571
3464
  debug: config.debug,
2572
3465
  projectId: config.projectId,
2573
- localDebuggerUrl: config.localWorkshopUrl
3466
+ localDebuggerUrl: config.localWorkshopUrl,
3467
+ appGitContext
2574
3468
  });
2575
- const worktree = (_a = input.worktree) != null ? _a : input.directory;
3469
+ const worktree = (_c = input.worktree) != null ? _c : input.directory;
2576
3470
  const hooks = createHooks(
2577
3471
  config,
2578
3472
  worktree,
@@ -2580,5 +3474,5 @@ async function plugin(input) {
2580
3474
  eventShipper,
2581
3475
  traceShipper
2582
3476
  );
2583
- return { ...hooks, raindrop: createRaindropHandle(eventShipper, traceShipper) };
3477
+ return { ...hooks, raindrop: createRaindropHandle(eventShipper, traceShipper, appGitContext) };
2584
3478
  }