@raindrop-ai/deep-agents 0.1.1 → 0.1.3

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.mjs CHANGED
@@ -63,7 +63,459 @@ function evalScopeAttributes() {
63
63
  return { [EVAL_CORRELATION_ID_ATTRIBUTE]: scope.correlationId };
64
64
  }
65
65
 
66
- // ../core/dist/chunk-T53LQAH7.js
66
+ // ../core/dist/chunk-WJOR6M7H.js
67
+ var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
68
+ function state() {
69
+ const holder = globalThis;
70
+ let current = holder[STATE_KEY];
71
+ if (!current) {
72
+ let storage = null;
73
+ try {
74
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
75
+ if (Ctor) storage = new Ctor();
76
+ } catch (e) {
77
+ storage = null;
78
+ }
79
+ current = {
80
+ storage,
81
+ syncDestination: void 0,
82
+ spanDestinations: /* @__PURE__ */ new WeakMap()
83
+ };
84
+ holder[STATE_KEY] = current;
85
+ }
86
+ return current;
87
+ }
88
+ function currentReplayTraceDestination() {
89
+ var _a, _b;
90
+ const routing = state();
91
+ return (_b = (_a = routing.storage) == null ? void 0 : _a.getStore()) != null ? _b : routing.syncDestination;
92
+ }
93
+ function rememberReplayTraceDestination(span) {
94
+ const destination = currentReplayTraceDestination();
95
+ if (destination) state().spanDestinations.set(span, destination);
96
+ }
97
+ function copyReplayTraceDestination(source, target) {
98
+ const destination = state().spanDestinations.get(source);
99
+ if (destination) state().spanDestinations.set(target, destination);
100
+ }
101
+ function replayTraceDestinationFor(value) {
102
+ if (typeof value !== "object" && typeof value !== "function" || value === null) {
103
+ return void 0;
104
+ }
105
+ return state().spanDestinations.get(value);
106
+ }
107
+ function recordReplayExportedSpan(destination, spanId, traceId) {
108
+ var _a, _b;
109
+ if (!destination || !spanId || !traceId) return;
110
+ (_a = destination.exportedSpanIds) == null ? void 0 : _a.add(spanId);
111
+ (_b = destination.exportedTraceIds) == null ? void 0 : _b.add(traceId);
112
+ }
113
+ function replayTraceUrl(baseUrl) {
114
+ return `${baseUrl.replace(/\/+$/, "")}/v1/traces`;
115
+ }
116
+ function getAsyncLocalStorageCtor() {
117
+ return globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
118
+ }
119
+ var MAX_BINDING_STACK = 128;
120
+ var EMPTY_STACK = [];
121
+ var ContextStackStorage = class {
122
+ /** Emitted once when this runtime cannot support an ambient push. */
123
+ constructor(noAmbientBindWarning) {
124
+ this.noAmbientBindWarning = noAmbientBindWarning;
125
+ this._als = null;
126
+ this._enterWithUsable = false;
127
+ this._syncStack = EMPTY_STACK;
128
+ this._adoptionChecked = false;
129
+ this._warnedNoAmbientBind = false;
130
+ }
131
+ maybeAdoptAsyncLocalStorage() {
132
+ if (this._adoptionChecked) return;
133
+ this._adoptionChecked = true;
134
+ const Ctor = getAsyncLocalStorageCtor();
135
+ if (!Ctor) return;
136
+ const als = new Ctor();
137
+ try {
138
+ als.run(EMPTY_STACK, () => {
139
+ });
140
+ } catch (e) {
141
+ return;
142
+ }
143
+ this._als = als;
144
+ if (typeof als.enterWith === "function") {
145
+ try {
146
+ als.enterWith(EMPTY_STACK);
147
+ this._enterWithUsable = true;
148
+ } catch (e) {
149
+ this._enterWithUsable = false;
150
+ }
151
+ }
152
+ }
153
+ getStack() {
154
+ var _a;
155
+ this.maybeAdoptAsyncLocalStorage();
156
+ if (this._als) return (_a = this._als.getStore()) != null ? _a : this._syncStack;
157
+ return this._syncStack;
158
+ }
159
+ /**
160
+ * Persist an ambient stack mutation. Returns false when an ALS is adopted but
161
+ * `enterWith` is unusable (workerd): callers must NOT fall back to the
162
+ * process-shared sync stack, since concurrent isolates would cross-stamp.
163
+ */
164
+ setAmbientStack(stack) {
165
+ if (this._als) {
166
+ if (!this._enterWithUsable) return false;
167
+ this._als.enterWith(stack);
168
+ return true;
169
+ }
170
+ this._syncStack = stack;
171
+ return true;
172
+ }
173
+ push(binding) {
174
+ this.maybeAdoptAsyncLocalStorage();
175
+ if (this._als && !this._enterWithUsable) {
176
+ this.warnNoAmbientBindOnce();
177
+ return;
178
+ }
179
+ let stack = this.getStack();
180
+ if (stack.length >= MAX_BINDING_STACK) {
181
+ stack = stack.filter((b) => b.isLive());
182
+ if (stack.length >= MAX_BINDING_STACK) {
183
+ stack = stack.slice(stack.length - (MAX_BINDING_STACK - 1));
184
+ }
185
+ }
186
+ this.setAmbientStack([...stack, binding]);
187
+ }
188
+ remove(binding) {
189
+ binding.markDead();
190
+ const stack = this.getStack();
191
+ const pruned = stack.filter((b) => b !== binding && b.isLive());
192
+ if (pruned.length !== stack.length) {
193
+ this.setAmbientStack(pruned);
194
+ }
195
+ }
196
+ runWith(binding, callback) {
197
+ this.maybeAdoptAsyncLocalStorage();
198
+ if (this._als) {
199
+ return this._als.run([...this.getStack(), binding], callback);
200
+ }
201
+ const previous = this._syncStack;
202
+ this._syncStack = [...previous, binding];
203
+ try {
204
+ return callback();
205
+ } finally {
206
+ this._syncStack = previous;
207
+ }
208
+ }
209
+ current() {
210
+ const stack = this.getStack();
211
+ if (stack.length === 0) return void 0;
212
+ const top = stack[stack.length - 1];
213
+ if (top.isLive()) return top;
214
+ const pruned = stack.filter((b) => b.isLive());
215
+ this.setAmbientStack(pruned);
216
+ return pruned.length > 0 ? pruned[pruned.length - 1] : void 0;
217
+ }
218
+ warnNoAmbientBindOnce() {
219
+ if (this._warnedNoAmbientBind) return;
220
+ this._warnedNoAmbientBind = true;
221
+ console.warn(this.noAmbientBindWarning);
222
+ }
223
+ };
224
+ 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.";
225
+ var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
226
+ function routingStorage() {
227
+ const holder = globalThis;
228
+ let storage = holder[ROUTING_STORAGE_KEY];
229
+ if (!storage) {
230
+ storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
231
+ holder[ROUTING_STORAGE_KEY] = storage;
232
+ }
233
+ return storage;
234
+ }
235
+ function currentRoutingBinding() {
236
+ const bound = routingStorage().current();
237
+ if (!bound) return void 0;
238
+ return {
239
+ projectId: bound.projectId,
240
+ authHint: bound.authHint,
241
+ ...bound.appGit ? { appGit: bound.appGit } : {},
242
+ ...bound.appGitSource ? { appGitSource: bound.appGitSource } : {}
243
+ };
244
+ }
245
+ var APP_COMMIT_SHA = "raindrop.app.commit_sha";
246
+ var APP_COMMIT_DIRTY = "raindrop.app.commit_dirty";
247
+ var APP_BRANCH = "raindrop.app.branch";
248
+ var APP_GIT_KEYS = [APP_COMMIT_SHA, APP_COMMIT_DIRTY, APP_BRANCH];
249
+ var own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
250
+ function appGitExplicitKeys(properties) {
251
+ const keys = /* @__PURE__ */ new Set();
252
+ if (!properties) return keys;
253
+ for (const key of APP_GIT_KEYS) {
254
+ if (own(properties, key)) keys.add(key);
255
+ }
256
+ return keys;
257
+ }
258
+ function mergeAppGitSnapshot(base, properties, explicitKeys = /* @__PURE__ */ new Set()) {
259
+ try {
260
+ const result = { ...base };
261
+ if (properties && own(properties, APP_COMMIT_SHA) && properties[APP_COMMIT_SHA] !== result[APP_COMMIT_SHA]) {
262
+ if (!explicitKeys.has(APP_COMMIT_DIRTY) && !own(properties, APP_COMMIT_DIRTY)) {
263
+ delete result[APP_COMMIT_DIRTY];
264
+ }
265
+ if (!explicitKeys.has(APP_BRANCH) && !own(properties, APP_BRANCH)) {
266
+ delete result[APP_BRANCH];
267
+ }
268
+ }
269
+ for (const key of APP_GIT_KEYS) {
270
+ if (properties && own(properties, key)) result[key] = properties[key];
271
+ }
272
+ return Object.freeze(result);
273
+ } catch (e) {
274
+ return Object.freeze({});
275
+ }
276
+ }
277
+ function validAppCommitSha(value) {
278
+ if (typeof value !== "string" || value.length > 128) return void 0;
279
+ const cleaned = value.trim();
280
+ return /^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/.test(cleaned) ? cleaned.toLowerCase() : void 0;
281
+ }
282
+ function shortString(value) {
283
+ if (typeof value !== "string" || value.length > 1024) return void 0;
284
+ const cleaned = value.trim();
285
+ return cleaned.length > 0 ? cleaned : void 0;
286
+ }
287
+ function flag(value) {
288
+ if (value === true || value === "true" || value === "1") return true;
289
+ if (value === false || value === "false" || value === "0") return false;
290
+ return void 0;
291
+ }
292
+ function environment() {
293
+ try {
294
+ return typeof process === "undefined" ? {} : process.env;
295
+ } catch (e) {
296
+ return {};
297
+ }
298
+ }
299
+ var DEPLOYMENTS = [
300
+ ["VERCEL", "VERCEL_GIT_COMMIT_SHA", "VERCEL_GIT_COMMIT_REF"],
301
+ ["NETLIFY", "COMMIT_REF", "BRANCH"],
302
+ ["RAILWAY_ENVIRONMENT_NAME", "RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH"],
303
+ ["RAILWAY_ENVIRONMENT", "RAILWAY_GIT_COMMIT_SHA", "RAILWAY_GIT_BRANCH"],
304
+ ["RENDER", "RENDER_GIT_COMMIT", "RENDER_GIT_BRANCH"],
305
+ ["DYNO", "HEROKU_BUILD_COMMIT", ""],
306
+ ["DYNO", "HEROKU_SLUG_COMMIT", ""],
307
+ ["AWS_APP_ID", "AWS_COMMIT_ID", "AWS_BRANCH"],
308
+ ["WORKERS_CI", "WORKERS_CI_COMMIT_SHA", "WORKERS_CI_BRANCH"]
309
+ ];
310
+ var CI_SOURCES = [
311
+ ["GITHUB_ACTIONS", "GITHUB_SHA", "GITHUB_REF_NAME"],
312
+ ["GITLAB_CI", "CI_COMMIT_SHA", "CI_COMMIT_REF_NAME"],
313
+ ["CIRCLECI", "CIRCLE_SHA1", "CIRCLE_BRANCH"]
314
+ ];
315
+ function detectedSource(env, table, detectBranch) {
316
+ for (const row of table) {
317
+ const [marker, shaKey, branchKey] = row;
318
+ if (!marker || !shaKey || !shortString(env[marker]) || flag(env[marker]) === false) continue;
319
+ const commitSha = validAppCommitSha(env[shaKey]);
320
+ if (commitSha)
321
+ return {
322
+ commitSha,
323
+ ...detectBranch && branchKey ? { branch: shortString(env[branchKey]) } : {}
324
+ };
325
+ }
326
+ return {};
327
+ }
328
+ var AppGitContextImpl = class {
329
+ constructor(options, runtime = {}) {
330
+ this.defaults = {};
331
+ this.explicit = {};
332
+ this.explicitKeys = /* @__PURE__ */ new Set();
333
+ this.disposed = false;
334
+ this.operations = /* @__PURE__ */ new Map();
335
+ this.operationOverflow = false;
336
+ this.detectBranch = false;
337
+ this.disabled = false;
338
+ var _a, _b, _c, _d, _e, _f;
339
+ try {
340
+ if (options === false) {
341
+ this.disabled = true;
342
+ return;
343
+ }
344
+ const opts = options != null ? options : {};
345
+ const env = (_a = runtime.env) != null ? _a : environment();
346
+ const detectBranch = (_c = (_b = flag(opts.detectBranch)) != null ? _b : flag(env.RAINDROP_GIT_DETECT_BRANCH)) != null ? _c : false;
347
+ this.detectBranch = detectBranch;
348
+ const collectBranch = detectBranch || runtime.captureLegacyReplayBranch === true;
349
+ const autoDetect = runtime.allowDiscovery === false ? false : (_e = (_d = flag(opts.autoDetect)) != null ? _d : flag(env.RAINDROP_GIT_AUTO_DETECT)) != null ? _e : true;
350
+ const configuredSha = opts.commitSha;
351
+ const explicitSha = configuredSha === void 0 ? env.RAINDROP_COMMIT_SHA : configuredSha;
352
+ if (explicitSha === null) this.explicit[APP_COMMIT_SHA] = null;
353
+ else if (typeof explicitSha === "string" && validAppCommitSha(explicitSha))
354
+ this.explicit[APP_COMMIT_SHA] = explicitSha;
355
+ const configuredDirty = opts.commitDirty;
356
+ const explicitDirty = configuredDirty === void 0 ? flag(env.RAINDROP_COMMIT_DIRTY) : configuredDirty;
357
+ if (explicitDirty === null || typeof explicitDirty === "boolean")
358
+ this.explicit[APP_COMMIT_DIRTY] = explicitDirty;
359
+ const configuredBranch = opts.branch;
360
+ const explicitBranch = configuredBranch === void 0 ? env.RAINDROP_BRANCH : configuredBranch;
361
+ if (explicitBranch === null) this.explicit[APP_BRANCH] = null;
362
+ else if (typeof explicitBranch === "string" && explicitBranch.length <= 1024)
363
+ this.explicit[APP_BRANCH] = explicitBranch;
364
+ this.explicitKeys = appGitExplicitKeys(this.explicit);
365
+ this.defaults = { ...this.explicit };
366
+ if (!autoDetect || explicitSha !== void 0) return;
367
+ const sourceDirectory = (_f = shortString(opts.sourceDirectory)) != null ? _f : shortString(env.RAINDROP_GIT_SOURCE_DIRECTORY);
368
+ if (runtime.allowLocalGit === false && !sourceDirectory) return;
369
+ const deploy = sourceDirectory ? {} : detectedSource(env, DEPLOYMENTS, collectBranch);
370
+ if (deploy.commitSha) {
371
+ this.accept(deploy);
372
+ return;
373
+ }
374
+ const fallback = sourceDirectory ? {} : detectedSource(env, CI_SOURCES, collectBranch);
375
+ if (runtime.resolveLocal && (runtime.allowLocalGit !== false || sourceDirectory)) {
376
+ this.cancel = runtime.resolveLocal(sourceDirectory, collectBranch, (result) => {
377
+ try {
378
+ if (!this.disposed) this.accept(result.commitSha ? result : fallback);
379
+ } catch (e) {
380
+ }
381
+ });
382
+ } else this.accept(fallback);
383
+ } catch (e) {
384
+ }
385
+ }
386
+ accept(result) {
387
+ const sha = validAppCommitSha(result.commitSha);
388
+ if (!sha) return;
389
+ this.replayBranch = shortString(result.branch);
390
+ this.defaults = {
391
+ [APP_COMMIT_SHA]: sha,
392
+ ...typeof result.commitDirty === "boolean" ? { [APP_COMMIT_DIRTY]: result.commitDirty } : {},
393
+ ...this.detectBranch && this.replayBranch ? { [APP_BRANCH]: this.replayBranch } : {},
394
+ ...this.explicit
395
+ };
396
+ }
397
+ snapshot(properties) {
398
+ var _a;
399
+ try {
400
+ const binding = currentRoutingBinding();
401
+ const base = this.disabled && (binding == null ? void 0 : binding.appGitSource) !== "replay-override" ? {} : (_a = binding == null ? void 0 : binding.appGit) != null ? _a : this.defaults;
402
+ return this.mergeSnapshot(base, properties);
403
+ } catch (e) {
404
+ return Object.freeze({});
405
+ }
406
+ }
407
+ /** Safe defaults when no operation/span start was observed; never infer identity. */
408
+ snapshotExplicit(properties, eventId) {
409
+ try {
410
+ const base = { ...this.explicit };
411
+ const operation = eventId ? this.operations.get(eventId) : void 0;
412
+ if (operation) {
413
+ for (const key of operation.explicitKeys) base[key] = operation.snapshot[key];
414
+ }
415
+ return this.mergeSnapshot(base, properties, new Set(Object.keys(base)));
416
+ } catch (e) {
417
+ return mergeAppGitSnapshot({}, properties);
418
+ }
419
+ }
420
+ /** Existing replay branch reporting stays separate from opt-in event properties. */
421
+ snapshotForReplay() {
422
+ const snapshot = this.snapshot();
423
+ if (this.replayBranch && !own(snapshot, APP_BRANCH) && snapshot[APP_COMMIT_SHA] === this.defaults[APP_COMMIT_SHA]) {
424
+ return Object.freeze({ ...snapshot, [APP_BRANCH]: this.replayBranch });
425
+ }
426
+ return snapshot;
427
+ }
428
+ /** Merge a frozen span snapshot without losing client-authored provenance. */
429
+ mergeSnapshot(base, properties, explicitKeys = this.explicitKeys) {
430
+ return mergeAppGitSnapshot(base, properties, /* @__PURE__ */ new Set([...this.explicitKeys, ...explicitKeys]));
431
+ }
432
+ /** Shippers belonging to one client share this bounded operation registry. */
433
+ forOperation(eventId, properties, owner) {
434
+ if (!eventId) return this.snapshot(properties);
435
+ let operation = this.operations.get(eventId);
436
+ if (!operation) {
437
+ if (this.operations.size >= 1e4) this.operationOverflow = true;
438
+ if (this.operationOverflow) return this.mergeSnapshot({}, properties);
439
+ operation = {
440
+ snapshot: this.snapshot(properties),
441
+ owners: /* @__PURE__ */ new Set(),
442
+ unowned: owner === void 0,
443
+ explicitKeys: appGitExplicitKeys(properties)
444
+ };
445
+ this.operations.set(eventId, operation);
446
+ } else if (properties && APP_GIT_KEYS.some((key) => own(properties, key))) {
447
+ const explicitKeys = /* @__PURE__ */ new Set([...this.explicitKeys, ...operation.explicitKeys]);
448
+ operation.snapshot = this.mergeSnapshot(operation.snapshot, properties, explicitKeys);
449
+ for (const key of appGitExplicitKeys(properties)) operation.explicitKeys.add(key);
450
+ }
451
+ if (!owner) operation.unowned = true;
452
+ if (owner) operation.owners.add(owner);
453
+ return operation.snapshot;
454
+ }
455
+ releaseOperation(eventId, owner) {
456
+ const operation = this.operations.get(eventId);
457
+ if (!operation) return;
458
+ operation.owners.delete(owner);
459
+ if (operation.owners.size === 0 && !operation.unowned) this.operations.delete(eventId);
460
+ }
461
+ finishOperation(eventId) {
462
+ this.operations.delete(eventId);
463
+ }
464
+ dispose() {
465
+ var _a;
466
+ this.disposed = true;
467
+ try {
468
+ (_a = this.cancel) == null ? void 0 : _a.call(this);
469
+ } catch (e) {
470
+ }
471
+ }
472
+ };
473
+ var AppGitContext = AppGitContextImpl;
474
+ function createAppGitContext(options, runtime = {}) {
475
+ return new AppGitContext(options, runtime);
476
+ }
477
+ function withAppGitProperties(properties, snapshot) {
478
+ try {
479
+ const merged = { ...properties };
480
+ for (const key of APP_GIT_KEYS)
481
+ if (!own(merged, key) && own(snapshot, key)) merged[key] = snapshot[key];
482
+ return merged;
483
+ } catch (e) {
484
+ return properties != null ? properties : {};
485
+ }
486
+ }
487
+ function withAppGitAttributes(attributes, snapshot) {
488
+ try {
489
+ const present = new Set(attributes.flatMap((attr) => attr ? [attr.key] : []));
490
+ const added = [];
491
+ for (const key of APP_GIT_KEYS) {
492
+ if (present.has(key) || present.has(`ai.telemetry.metadata.${key}`) || present.has(`traceloop.association.properties.${key}`))
493
+ continue;
494
+ const value = snapshot[key];
495
+ if (typeof value === "string") added.push({ key, value: { stringValue: value } });
496
+ else if (typeof value === "boolean") added.push({ key, value: { boolValue: value } });
497
+ }
498
+ return added.length ? [...attributes, ...added] : attributes;
499
+ } catch (e) {
500
+ return attributes;
501
+ }
502
+ }
503
+ function appGitPropertiesFromAttributes(attributes) {
504
+ var _a, _b, _c;
505
+ const properties = {};
506
+ try {
507
+ for (const attr of attributes) {
508
+ if (!attr) continue;
509
+ for (const key of APP_GIT_KEYS) {
510
+ if (attr.key === key || attr.key === `ai.telemetry.metadata.${key}` || attr.key === `traceloop.association.properties.${key}`) {
511
+ properties[key] = (_c = (_b = (_a = attr.value.stringValue) != null ? _a : attr.value.boolValue) != null ? _b : attr.value.intValue) != null ? _c : null;
512
+ }
513
+ }
514
+ }
515
+ } catch (e) {
516
+ }
517
+ return properties;
518
+ }
67
519
  function getCrypto() {
68
520
  const c = globalThis.crypto;
69
521
  return c;
@@ -168,7 +620,7 @@ function runWithTracingSuppressed(fn) {
168
620
  var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
169
621
  var MAX_RETRY_DELAY_MS = 3e4;
170
622
  function wait(ms) {
171
- return new Promise((resolve) => setTimeout(resolve, ms));
623
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
172
624
  }
173
625
  function formatEndpoint(endpoint) {
174
626
  if (!endpoint) return void 0;
@@ -205,9 +657,9 @@ async function raceWithTimeout(promise, timeoutMs) {
205
657
  () => true,
206
658
  () => true
207
659
  ),
208
- new Promise((resolve) => {
660
+ new Promise((resolve2) => {
209
661
  var _a;
210
- timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
662
+ timer = setTimeout(() => resolve2(false), Math.max(0, timeoutMs));
211
663
  (_a = timer.unref) == null ? void 0 : _a.call(timer);
212
664
  })
213
665
  ]);
@@ -380,55 +832,6 @@ function buildExportTraceServiceRequest(spans, serviceName = "raindrop.core", se
380
832
  ]
381
833
  };
382
834
  }
383
- var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
384
- function state() {
385
- const holder = globalThis;
386
- let current = holder[STATE_KEY];
387
- if (!current) {
388
- let storage = null;
389
- try {
390
- const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
391
- if (Ctor) storage = new Ctor();
392
- } catch (e) {
393
- storage = null;
394
- }
395
- current = {
396
- storage,
397
- syncDestination: void 0,
398
- spanDestinations: /* @__PURE__ */ new WeakMap()
399
- };
400
- holder[STATE_KEY] = current;
401
- }
402
- return current;
403
- }
404
- function currentReplayTraceDestination() {
405
- var _a, _b;
406
- const routing = state();
407
- return (_b = (_a = routing.storage) == null ? void 0 : _a.getStore()) != null ? _b : routing.syncDestination;
408
- }
409
- function rememberReplayTraceDestination(span) {
410
- const destination = currentReplayTraceDestination();
411
- if (destination) state().spanDestinations.set(span, destination);
412
- }
413
- function copyReplayTraceDestination(source, target) {
414
- const destination = state().spanDestinations.get(source);
415
- if (destination) state().spanDestinations.set(target, destination);
416
- }
417
- function replayTraceDestinationFor(value) {
418
- if (typeof value !== "object" && typeof value !== "function" || value === null) {
419
- return void 0;
420
- }
421
- return state().spanDestinations.get(value);
422
- }
423
- function recordReplayExportedSpan(destination, spanId, traceId) {
424
- var _a, _b;
425
- if (!destination || !spanId || !traceId) return;
426
- (_a = destination.exportedSpanIds) == null ? void 0 : _a.add(spanId);
427
- (_b = destination.exportedTraceIds) == null ? void 0 : _b.add(traceId);
428
- }
429
- function replayTraceUrl(baseUrl) {
430
- return `${baseUrl.replace(/\/+$/, "")}/v1/traces`;
431
- }
432
835
  var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
433
836
  var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
434
837
  var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
@@ -538,6 +941,13 @@ function projectIdHeaders(projectId) {
538
941
  }
539
942
  var SHUTDOWN_DEADLINE_MS = 1e4;
540
943
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
944
+ function appGitOwnerId(prefix) {
945
+ try {
946
+ return `${prefix}:${randomUUID()}`;
947
+ } catch (e) {
948
+ return `${prefix}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
949
+ }
950
+ }
541
951
  function mergePatches(target, source) {
542
952
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
543
953
  const out = { ...target, ...source };
@@ -587,22 +997,25 @@ function eventTelemetryProperties(patch) {
587
997
  }
588
998
  var EventShipper = class {
589
999
  constructor(opts) {
1000
+ this.appGitOwner = appGitOwnerId("event");
590
1001
  this.buffers = /* @__PURE__ */ new Map();
591
1002
  this.sticky = /* @__PURE__ */ new Map();
592
1003
  this.timers = /* @__PURE__ */ new Map();
593
1004
  this.inFlight = /* @__PURE__ */ new Set();
594
1005
  this.hasShutdown = false;
595
- var _a, _b, _c, _d, _e, _f, _g, _h;
596
- this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
597
- this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
1006
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1007
+ this.appGitContext = (_a = opts.appGitContext) != null ? _a : createAppGitContext(opts.appGit);
1008
+ this.ownsAppGitContext = opts.appGitContext === void 0;
1009
+ this.writeKey = (_b = opts.writeKey) == null ? void 0 : _b.trim();
1010
+ this.baseUrl = (_c = formatEndpoint(opts.endpoint)) != null ? _c : "https://api.raindrop.ai/v1/";
598
1011
  this.enabled = opts.enabled !== false;
599
1012
  this.debug = opts.debug;
600
- this.partialFlushMs = (_c = opts.partialFlushMs) != null ? _c : 1e3;
601
- this.sdkName = (_d = opts.sdkName) != null ? _d : "core";
1013
+ this.partialFlushMs = (_d = opts.partialFlushMs) != null ? _d : 1e3;
1014
+ this.sdkName = (_e = opts.sdkName) != null ? _e : "core";
602
1015
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
603
- this.defaultEventName = (_e = opts.defaultEventName) != null ? _e : "ai_generation";
1016
+ this.defaultEventName = (_f = opts.defaultEventName) != null ? _f : "ai_generation";
604
1017
  this.maxTextFieldCharsOpt = opts.maxTextFieldChars;
605
- this.localDebuggerUrl = (_f = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _f : void 0;
1018
+ this.localDebuggerUrl = (_g = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _g : void 0;
606
1019
  if (this.debug && this.localDebuggerUrl) {
607
1020
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
608
1021
  }
@@ -613,8 +1026,8 @@ var EventShipper = class {
613
1026
  const isNode = typeof process !== "undefined" && typeof process.version === "string";
614
1027
  this.context = {
615
1028
  library: {
616
- name: (_g = opts.libraryName) != null ? _g : "@raindrop-ai/core",
617
- version: (_h = opts.libraryVersion) != null ? _h : "0.0.0"
1029
+ name: (_h = opts.libraryName) != null ? _h : "@raindrop-ai/core",
1030
+ version: (_i = opts.libraryVersion) != null ? _i : "0.0.0"
618
1031
  },
619
1032
  metadata: {
620
1033
  jsRuntime: isNode ? "node" : "web",
@@ -622,6 +1035,9 @@ var EventShipper = class {
622
1035
  }
623
1036
  };
624
1037
  }
1038
+ ownAppGitContextForShutdown() {
1039
+ this.ownsAppGitContext = true;
1040
+ }
625
1041
  isDebugEnabled() {
626
1042
  return this.debug;
627
1043
  }
@@ -700,6 +1116,13 @@ var EventShipper = class {
700
1116
  }
701
1117
  const sticky = (_c = this.sticky.get(eventId)) != null ? _c : {};
702
1118
  const existing = (_d = this.buffers.get(eventId)) != null ? _d : {};
1119
+ patch = {
1120
+ ...patch,
1121
+ properties: withAppGitProperties(
1122
+ patch.properties,
1123
+ this.appGitContext.forOperation(eventId, patch.properties, this.appGitOwner)
1124
+ )
1125
+ };
703
1126
  const merged = mergePatches(existing, patch);
704
1127
  merged.isPending = (_g = (_f = (_e = patch.isPending) != null ? _e : existing.isPending) != null ? _f : sticky.isPending) != null ? _g : true;
705
1128
  this.buffers.set(eventId, merged);
@@ -713,6 +1136,8 @@ var EventShipper = class {
713
1136
  if (t) clearTimeout(t);
714
1137
  if (merged.isPending === false) {
715
1138
  await this.flushOne(eventId);
1139
+ if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(eventId, this.appGitOwner);
1140
+ else this.appGitContext.finishOperation(eventId);
716
1141
  return;
717
1142
  }
718
1143
  const timeout = setTimeout(() => {
@@ -744,6 +1169,7 @@ var EventShipper = class {
744
1169
  if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
745
1170
  } finally {
746
1171
  this.shutdownDeadlineAt = void 0;
1172
+ if (this.ownsAppGitContext) this.appGitContext.dispose();
747
1173
  }
748
1174
  }
749
1175
  async trackSignal(signal) {
@@ -1171,6 +1597,13 @@ function redactJsonAttributeValue(key, value) {
1171
1597
  if (scrubbedJson === json) return void 0;
1172
1598
  return { stringValue: scrubbedJson };
1173
1599
  }
1600
+ function appGitOwnerId2(prefix) {
1601
+ try {
1602
+ return `${prefix}:${randomUUID()}`;
1603
+ } catch (e) {
1604
+ return `${prefix}:${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`;
1605
+ }
1606
+ }
1174
1607
  function identityAttrs(args) {
1175
1608
  return [
1176
1609
  attrString("ai.telemetry.metadata.raindrop.ai.userId", args.userId),
@@ -1193,23 +1626,30 @@ function applyOtelSpanAttributeLimit(limit) {
1193
1626
  }
1194
1627
  var TraceShipper = class {
1195
1628
  constructor(opts) {
1629
+ this.appGitOwner = appGitOwnerId2("trace");
1630
+ this.appGitEventIds = /* @__PURE__ */ new WeakMap();
1631
+ this.appGitActiveSpans = /* @__PURE__ */ new Map();
1632
+ this.appGitCaptured = /* @__PURE__ */ new WeakSet();
1633
+ this.appGitAdded = /* @__PURE__ */ new WeakMap();
1196
1634
  this.queue = [];
1197
1635
  this.inFlight = /* @__PURE__ */ new Set();
1198
1636
  this.hasShutdown = false;
1199
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1200
- this.writeKey = (_a = opts.writeKey) == null ? void 0 : _a.trim();
1201
- this.baseUrl = (_b = formatEndpoint(opts.endpoint)) != null ? _b : "https://api.raindrop.ai/v1/";
1637
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1638
+ this.appGitContext = (_a = opts.appGitContext) != null ? _a : createAppGitContext(opts.appGit);
1639
+ this.ownsAppGitContext = opts.appGitContext === void 0;
1640
+ this.writeKey = (_b = opts.writeKey) == null ? void 0 : _b.trim();
1641
+ this.baseUrl = (_c = formatEndpoint(opts.endpoint)) != null ? _c : "https://api.raindrop.ai/v1/";
1202
1642
  this.enabled = opts.enabled !== false;
1203
1643
  this.debug = opts.debug;
1204
1644
  this.debugSpans = opts.debugSpans === true;
1205
- this.flushIntervalMs = (_c = opts.flushIntervalMs) != null ? _c : 1e3;
1206
- this.maxBatchSize = (_d = opts.maxBatchSize) != null ? _d : 50;
1207
- this.maxQueueSize = (_e = opts.maxQueueSize) != null ? _e : 5e3;
1208
- this.sdkName = (_f = opts.sdkName) != null ? _f : "core";
1645
+ this.flushIntervalMs = (_d = opts.flushIntervalMs) != null ? _d : 1e3;
1646
+ this.maxBatchSize = (_e = opts.maxBatchSize) != null ? _e : 50;
1647
+ this.maxQueueSize = (_f = opts.maxQueueSize) != null ? _f : 5e3;
1648
+ this.sdkName = (_g = opts.sdkName) != null ? _g : "core";
1209
1649
  this.prefix = `[raindrop-ai/${this.sdkName}]`;
1210
- this.serviceName = (_g = opts.serviceName) != null ? _g : "raindrop.core";
1211
- this.serviceVersion = (_h = opts.serviceVersion) != null ? _h : "0.0.0";
1212
- this.localDebuggerUrl = (_i = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _i : void 0;
1650
+ this.serviceName = (_h = opts.serviceName) != null ? _h : "raindrop.core";
1651
+ this.serviceVersion = (_i = opts.serviceVersion) != null ? _i : "0.0.0";
1652
+ this.localDebuggerUrl = (_j = resolveLocalDebuggerBaseUrl(opts.localDebuggerUrl)) != null ? _j : void 0;
1213
1653
  if (this.debug && this.localDebuggerUrl) {
1214
1654
  console.log(`${this.prefix} Local debugger mirroring: ${this.localDebuggerUrl}`);
1215
1655
  }
@@ -1310,7 +1750,7 @@ var TraceShipper = class {
1310
1750
  return { ...this.authHeaders(), ...projectIdHeaders(this.projectId) };
1311
1751
  }
1312
1752
  startSpan(args) {
1313
- var _a, _b;
1753
+ var _a, _b, _c;
1314
1754
  const ids = createSpanIds(args.parent);
1315
1755
  const started = (_a = args.startTimeUnixNano) != null ? _a : nowUnixNanoString();
1316
1756
  const attrs = [
@@ -1320,13 +1760,24 @@ var TraceShipper = class {
1320
1760
  ...Object.entries(evalScopeAttributes()).map(([key, value]) => attrString(key, value))
1321
1761
  ];
1322
1762
  if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
1763
+ const tracksAppGit = this.appGitActiveSpans.has(args.eventId) || this.appGitActiveSpans.size < 1e4;
1764
+ const appGitOverrides = appGitPropertiesFromAttributes(attrs);
1323
1765
  const span = {
1324
1766
  ids,
1325
1767
  name: args.name,
1326
1768
  startTimeUnixNano: started,
1327
- attributes: attrs
1769
+ attributes: withAppGitAttributes(
1770
+ attrs,
1771
+ this.appGitContext.forOperation(args.eventId, appGitOverrides, this.appGitOwner)
1772
+ )
1328
1773
  };
1329
1774
  rememberReplayTraceDestination(span);
1775
+ this.appGitCaptured.add(span);
1776
+ this.appGitEventIds.set(span, args.eventId);
1777
+ if (tracksAppGit) {
1778
+ this.appGitActiveSpans.set(args.eventId, ((_c = this.appGitActiveSpans.get(args.eventId)) != null ? _c : 0) + 1);
1779
+ }
1780
+ this.appGitAdded.set(span, new Set(span.attributes.filter((attr) => !attrs.includes(attr))));
1330
1781
  const openSpan = buildOtlpSpan({
1331
1782
  ids: span.ids,
1332
1783
  name: span.name,
@@ -1366,11 +1817,17 @@ var TraceShipper = class {
1366
1817
  });
1367
1818
  }
1368
1819
  endSpan(span, extra) {
1369
- var _a, _b;
1820
+ var _a, _b, _c;
1370
1821
  if (span.endTimeUnixNano) return;
1371
1822
  span.endTimeUnixNano = (_a = extra == null ? void 0 : extra.endTimeUnixNano) != null ? _a : nowUnixNanoString();
1372
1823
  if ((_b = extra == null ? void 0 : extra.attributes) == null ? void 0 : _b.length) {
1824
+ const overrides = appGitPropertiesFromAttributes(extra.attributes);
1825
+ const eventId2 = this.appGitEventIds.get(span);
1826
+ const snapshot = eventId2 ? this.appGitContext.forOperation(eventId2, overrides, this.appGitOwner) : overrides;
1827
+ const added = this.appGitAdded.get(span);
1828
+ 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));
1373
1829
  span.attributes.push(...extra.attributes);
1830
+ span.attributes = withAppGitAttributes(span.attributes, snapshot);
1374
1831
  }
1375
1832
  let status = extra == null ? void 0 : extra.status;
1376
1833
  if (!status && (extra == null ? void 0 : extra.error) !== void 0) {
@@ -1386,8 +1843,19 @@ var TraceShipper = class {
1386
1843
  status
1387
1844
  });
1388
1845
  copyReplayTraceDestination(span, otlp);
1846
+ this.appGitCaptured.add(otlp);
1389
1847
  this.enqueue(otlp);
1390
1848
  this.mirrorToLocalDebugger(otlp, true);
1849
+ const eventId = this.appGitEventIds.get(span);
1850
+ if (eventId && this.appGitActiveSpans.has(eventId)) {
1851
+ const remaining = ((_c = this.appGitActiveSpans.get(eventId)) != null ? _c : 1) - 1;
1852
+ if (remaining > 0) this.appGitActiveSpans.set(eventId, remaining);
1853
+ else {
1854
+ this.appGitActiveSpans.delete(eventId);
1855
+ if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(eventId, this.appGitOwner);
1856
+ else if (this.ownsAppGitContext) this.appGitContext.finishOperation(eventId);
1857
+ }
1858
+ }
1391
1859
  }
1392
1860
  createSpan(args) {
1393
1861
  var _a;
@@ -1398,20 +1866,41 @@ var TraceShipper = class {
1398
1866
  ...Object.entries(evalScopeAttributes()).map(([key, value]) => attrString(key, value))
1399
1867
  ];
1400
1868
  if ((_a = args.attributes) == null ? void 0 : _a.length) attrs.push(...args.attributes);
1869
+ const tracksAppGit = !this.appGitActiveSpans.has(args.eventId);
1401
1870
  const otlp = buildOtlpSpan({
1402
1871
  ids,
1403
1872
  name: args.name,
1404
1873
  startTimeUnixNano: args.startTimeUnixNano,
1405
1874
  endTimeUnixNano: args.endTimeUnixNano,
1406
- attributes: attrs,
1875
+ attributes: withAppGitAttributes(
1876
+ attrs,
1877
+ this.appGitContext.forOperation(
1878
+ args.eventId,
1879
+ appGitPropertiesFromAttributes(attrs),
1880
+ this.appGitOwner
1881
+ )
1882
+ ),
1407
1883
  status: args.status
1408
1884
  });
1409
1885
  rememberReplayTraceDestination(otlp);
1886
+ this.appGitCaptured.add(otlp);
1887
+ if (tracksAppGit) {
1888
+ if (this.appGitContext.releaseOperation) this.appGitContext.releaseOperation(args.eventId, this.appGitOwner);
1889
+ else if (this.ownsAppGitContext) this.appGitContext.finishOperation(args.eventId);
1890
+ }
1410
1891
  this.enqueue(otlp);
1411
1892
  this.mirrorToLocalDebugger(otlp, true);
1412
1893
  }
1413
1894
  enqueue(span) {
1895
+ var _a;
1414
1896
  if (!this.enabled) return;
1897
+ const attributes = (_a = span.attributes) != null ? _a : [];
1898
+ const enriched = this.appGitCaptured.has(span) ? attributes : withAppGitAttributes(attributes, this.appGitContext.snapshot(appGitPropertiesFromAttributes(attributes)));
1899
+ if (enriched !== attributes) {
1900
+ const original = span;
1901
+ span = { ...span, attributes: enriched.filter((attr) => attr !== void 0) };
1902
+ copyReplayTraceDestination(original, span);
1903
+ }
1415
1904
  if (this.debugSpans) {
1416
1905
  const short = (s) => s ? s.slice(-8) : "none";
1417
1906
  console.log(
@@ -1557,12 +2046,158 @@ var TraceShipper = class {
1557
2046
  }
1558
2047
  } finally {
1559
2048
  this.shutdownDeadlineAt = void 0;
2049
+ if (this.ownsAppGitContext) this.appGitContext.dispose();
1560
2050
  }
1561
2051
  }
1562
2052
  };
1563
2053
 
1564
2054
  // ../core/dist/index.node.js
1565
2055
  import { AsyncLocalStorage } from "async_hooks";
2056
+ import { execFile } from "child_process";
2057
+ import { resolve } from "path";
2058
+ var resolveLocal = (sourceDirectory, detectBranch, completed) => {
2059
+ const cwd = sourceDirectory ? resolve(sourceDirectory) : process.cwd();
2060
+ let cancelled = false;
2061
+ let active;
2062
+ let deadlineTimer;
2063
+ const deadline = Date.now() + 1e3;
2064
+ const run = (args, done) => {
2065
+ if (cancelled) return;
2066
+ const remaining = deadline - Date.now();
2067
+ if (remaining <= 0) {
2068
+ done(void 0);
2069
+ return;
2070
+ }
2071
+ try {
2072
+ active = execFile(
2073
+ "git",
2074
+ args,
2075
+ {
2076
+ cwd,
2077
+ encoding: "utf8",
2078
+ maxBuffer: 4096,
2079
+ windowsHide: true,
2080
+ env: {
2081
+ ...process.env,
2082
+ // The selected application cwd, not the invoking shell's Git plumbing, owns identity.
2083
+ GIT_DIR: void 0,
2084
+ GIT_WORK_TREE: void 0,
2085
+ GIT_COMMON_DIR: void 0,
2086
+ GIT_NAMESPACE: void 0,
2087
+ GIT_INDEX_FILE: void 0,
2088
+ GIT_OBJECT_DIRECTORY: void 0,
2089
+ GIT_ALTERNATE_OBJECT_DIRECTORIES: void 0,
2090
+ GIT_CONFIG_PARAMETERS: void 0,
2091
+ GIT_CONFIG_COUNT: "0",
2092
+ GIT_TERMINAL_PROMPT: "0",
2093
+ GIT_OPTIONAL_LOCKS: "0"
2094
+ }
2095
+ },
2096
+ (error, stdout) => {
2097
+ clearTimeout(deadlineTimer);
2098
+ deadlineTimer = void 0;
2099
+ active = void 0;
2100
+ if (!cancelled) {
2101
+ try {
2102
+ done(error ? void 0 : stdout.trim());
2103
+ } catch (e) {
2104
+ }
2105
+ }
2106
+ }
2107
+ );
2108
+ active.unref();
2109
+ if (active.stdout && "unref" in active.stdout && typeof active.stdout.unref === "function")
2110
+ active.stdout.unref();
2111
+ if (active.stderr && "unref" in active.stderr && typeof active.stderr.unref === "function")
2112
+ active.stderr.unref();
2113
+ deadlineTimer = setTimeout(() => {
2114
+ try {
2115
+ active == null ? void 0 : active.kill("SIGKILL");
2116
+ } catch (e) {
2117
+ }
2118
+ }, remaining);
2119
+ deadlineTimer.unref();
2120
+ } catch (e) {
2121
+ try {
2122
+ done(void 0);
2123
+ } catch (e2) {
2124
+ }
2125
+ }
2126
+ };
2127
+ const scheduled = setTimeout(() => {
2128
+ try {
2129
+ run(["rev-parse", "HEAD"], (rawSha) => {
2130
+ const commitSha = validAppCommitSha(rawSha);
2131
+ if (!commitSha) {
2132
+ completed({});
2133
+ return;
2134
+ }
2135
+ run(["-c", "core.fsmonitor=false", "status", "--porcelain"], (status) => {
2136
+ const finish = (branch) => {
2137
+ run(["rev-parse", "HEAD"], (finalSha) => {
2138
+ const coherent = validAppCommitSha(finalSha) === commitSha;
2139
+ completed({
2140
+ commitSha,
2141
+ ...coherent && status !== void 0 ? { commitDirty: status !== "" } : {},
2142
+ ...coherent && branch && branch !== "HEAD" ? { branch } : {}
2143
+ });
2144
+ });
2145
+ };
2146
+ if (detectBranch) run(["symbolic-ref", "--quiet", "--short", "HEAD"], finish);
2147
+ else finish();
2148
+ });
2149
+ });
2150
+ } catch (e) {
2151
+ completed({});
2152
+ }
2153
+ }, 0);
2154
+ scheduled.unref();
2155
+ return () => {
2156
+ cancelled = true;
2157
+ clearTimeout(scheduled);
2158
+ clearTimeout(deadlineTimer);
2159
+ try {
2160
+ active == null ? void 0 : active.kill("SIGKILL");
2161
+ } catch (e) {
2162
+ }
2163
+ };
2164
+ };
2165
+ function createAppGitContext2(options, runtime = {}) {
2166
+ return new AppGitContext(options, { ...runtime, resolveLocal });
2167
+ }
2168
+ function hasDestination(options) {
2169
+ var _a;
2170
+ if (options.enabled === false) return false;
2171
+ return Boolean(((_a = options.writeKey) == null ? void 0 : _a.trim()) || resolveLocalDebuggerBaseUrl(options.localDebuggerUrl));
2172
+ }
2173
+ var EventShipper2 = class extends EventShipper {
2174
+ constructor(options) {
2175
+ var _a;
2176
+ const ownsAppGitContext = options.appGitContext === void 0;
2177
+ const appGitContext = (_a = options.appGitContext) != null ? _a : createAppGitContext2(options.appGit, { allowDiscovery: hasDestination(options) });
2178
+ try {
2179
+ super({ ...options, appGitContext });
2180
+ } catch (error) {
2181
+ if (ownsAppGitContext) appGitContext.dispose();
2182
+ throw error;
2183
+ }
2184
+ if (ownsAppGitContext) this.ownAppGitContextForShutdown();
2185
+ }
2186
+ };
2187
+ var TraceShipper2 = class extends TraceShipper {
2188
+ constructor(options) {
2189
+ var _a;
2190
+ const ownsAppGitContext = options.appGitContext === void 0;
2191
+ const appGitContext = (_a = options.appGitContext) != null ? _a : createAppGitContext2(options.appGit, { allowDiscovery: hasDestination(options) });
2192
+ try {
2193
+ super({ ...options, appGitContext });
2194
+ } catch (error) {
2195
+ if (ownsAppGitContext) appGitContext.dispose();
2196
+ throw error;
2197
+ }
2198
+ this.ownsAppGitContext = ownsAppGitContext;
2199
+ }
2200
+ };
1566
2201
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
1567
2202
  var SUPPRESS_TRACING_KEY = /* @__PURE__ */ Symbol.for(
1568
2203
  "OpenTelemetry SDK Context Key SUPPRESS_TRACING"
@@ -1590,6 +2225,468 @@ function installTracingSuppressionHook() {
1590
2225
  }
1591
2226
  installTracingSuppressionHook();
1592
2227
 
2228
+ // src/conversation.ts
2229
+ import {
2230
+ isAIMessage,
2231
+ isBaseMessage,
2232
+ isChatMessage,
2233
+ isToolMessage
2234
+ } from "@langchain/core/messages";
2235
+
2236
+ // src/truncation.ts
2237
+ var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
2238
+ var DEFAULT_MAX_TEXT_FIELD_CHARS2 = 1e6;
2239
+ var MAX_DEPTH = 12;
2240
+ var configuredMaxTextFieldChars;
2241
+ function setMaxTextFieldChars(limit) {
2242
+ if (limit === void 0) return;
2243
+ if (typeof limit === "number" && Number.isFinite(limit) && limit > 0) {
2244
+ configuredMaxTextFieldChars = Math.floor(limit);
2245
+ } else {
2246
+ console.warn(`[raindrop-ai] maxTextFieldChars=${String(limit)} ignored; must be > 0`);
2247
+ }
2248
+ }
2249
+ function otelEnvLimit() {
2250
+ if (typeof process === "undefined") return void 0;
2251
+ const raw = process.env?.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT;
2252
+ if (!raw) return void 0;
2253
+ const parsed = Number.parseInt(raw, 10);
2254
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
2255
+ }
2256
+ function eventMaxTextFieldChars() {
2257
+ return configuredMaxTextFieldChars ?? DEFAULT_MAX_TEXT_FIELD_CHARS2;
2258
+ }
2259
+ function spanAttributeLimit(limit) {
2260
+ const env = otelEnvLimit();
2261
+ return env !== void 0 && env < limit ? env : limit;
2262
+ }
2263
+ function effectiveMaxTextFieldChars() {
2264
+ return spanAttributeLimit(eventMaxTextFieldChars());
2265
+ }
2266
+ function truncateToLimit2(text, limit) {
2267
+ if (limit > TRUNCATION_MARKER2.length) {
2268
+ return text.slice(0, limit - TRUNCATION_MARKER2.length) + TRUNCATION_MARKER2;
2269
+ }
2270
+ return text.slice(0, limit);
2271
+ }
2272
+ function capText2(value, limit) {
2273
+ if (typeof value !== "string") return value;
2274
+ const max = limit ?? effectiveMaxTextFieldChars();
2275
+ if (value.length <= max) return value;
2276
+ return truncateToLimit2(value, max);
2277
+ }
2278
+ function boundedClone2(value, budget, depth) {
2279
+ if (budget.remaining <= 0) return TRUNCATION_MARKER2;
2280
+ if (typeof value === "string") {
2281
+ if (value.length > budget.remaining) {
2282
+ const taken = value.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
2283
+ budget.remaining = 0;
2284
+ return taken;
2285
+ }
2286
+ budget.remaining -= Math.max(value.length, 1);
2287
+ return value;
2288
+ }
2289
+ if (value === null || typeof value === "number" || typeof value === "boolean") {
2290
+ budget.remaining -= 8;
2291
+ return value;
2292
+ }
2293
+ if (typeof value === "bigint") return boundedClone2(value.toString(), budget, depth);
2294
+ if (typeof value !== "object") {
2295
+ budget.remaining -= 1;
2296
+ return value;
2297
+ }
2298
+ if (value instanceof Uint8Array) {
2299
+ const maxBytes = Math.max(0, Math.ceil(budget.remaining * 3 / 4));
2300
+ if (value.byteLength > maxBytes) {
2301
+ const taken = base64Encode(value.subarray(0, maxBytes)) + TRUNCATION_MARKER2;
2302
+ budget.remaining = 0;
2303
+ return taken;
2304
+ }
2305
+ const encoded = base64Encode(value);
2306
+ budget.remaining -= Math.max(encoded.length, 1);
2307
+ return encoded;
2308
+ }
2309
+ if (depth >= MAX_DEPTH) {
2310
+ budget.remaining -= 16;
2311
+ const name = value.constructor?.name ?? "object";
2312
+ return `<max depth: ${name}>`;
2313
+ }
2314
+ const toJSON = value.toJSON;
2315
+ if (typeof toJSON === "function") {
2316
+ budget.remaining -= 2;
2317
+ return boundedClone2(toJSON.call(value), budget, depth + 1);
2318
+ }
2319
+ if (Array.isArray(value)) {
2320
+ budget.remaining -= 2;
2321
+ const out2 = [];
2322
+ for (const item of value) {
2323
+ if (budget.remaining <= 0) {
2324
+ out2.push(TRUNCATION_MARKER2);
2325
+ break;
2326
+ }
2327
+ out2.push(boundedClone2(item, budget, depth + 1));
2328
+ }
2329
+ return out2;
2330
+ }
2331
+ budget.remaining -= 2;
2332
+ const out = {};
2333
+ for (const key in value) {
2334
+ if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
2335
+ if (budget.remaining <= 0) {
2336
+ out["..."] = TRUNCATION_MARKER2;
2337
+ break;
2338
+ }
2339
+ let cappedKey = key;
2340
+ if (key.length > budget.remaining) {
2341
+ cappedKey = key.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
2342
+ budget.remaining = 0;
2343
+ out[cappedKey] = TRUNCATION_MARKER2;
2344
+ break;
2345
+ }
2346
+ budget.remaining -= Math.max(key.length, 1);
2347
+ out[cappedKey] = boundedClone2(value[key], budget, depth + 1);
2348
+ }
2349
+ return out;
2350
+ }
2351
+ function boundedStringify(value, limit) {
2352
+ const max = limit ?? effectiveMaxTextFieldChars();
2353
+ try {
2354
+ if (typeof value === "string") {
2355
+ const text2 = JSON.stringify(capText2(value, max));
2356
+ return text2.length <= max ? text2 : truncateToLimit2(text2, max);
2357
+ }
2358
+ const pruned = boundedClone2(value, { remaining: max + TRUNCATION_MARKER2.length + 256 }, 0);
2359
+ const text = JSON.stringify(pruned);
2360
+ if (text === void 0) return void 0;
2361
+ return text.length <= max ? text : truncateToLimit2(text, max);
2362
+ } catch {
2363
+ return void 0;
2364
+ }
2365
+ }
2366
+
2367
+ // src/conversation.ts
2368
+ var CAPTURE_TRUNCATED_ROLE = "raindrop";
2369
+ var MESSAGE_OVERHEAD = 32;
2370
+ var PART_OVERHEAD = 24;
2371
+ var MAX_DEPTH2 = 12;
2372
+ function isRecord(value) {
2373
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2374
+ }
2375
+ function stringField(record, key) {
2376
+ const value = record[key];
2377
+ return typeof value === "string" ? value : void 0;
2378
+ }
2379
+ function takeText(text, budget) {
2380
+ if (text.length > budget.remaining) {
2381
+ const kept = text.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
2382
+ budget.remaining = 0;
2383
+ budget.truncated = true;
2384
+ return kept;
2385
+ }
2386
+ budget.remaining -= Math.max(text.length, 1);
2387
+ return text;
2388
+ }
2389
+ function reserve(text, budget) {
2390
+ if (text !== void 0) budget.remaining -= Math.max(text.length, 1);
2391
+ return text;
2392
+ }
2393
+ function takeValue(value, budget, depth) {
2394
+ if (typeof value === "string") return takeText(value, budget);
2395
+ if (value === null || typeof value === "number" || typeof value === "boolean") {
2396
+ budget.remaining -= 8;
2397
+ return value;
2398
+ }
2399
+ if (typeof value !== "object") {
2400
+ budget.remaining -= 1;
2401
+ return value;
2402
+ }
2403
+ if (depth >= MAX_DEPTH2) {
2404
+ budget.remaining -= TRUNCATION_MARKER2.length;
2405
+ budget.truncated = true;
2406
+ return TRUNCATION_MARKER2;
2407
+ }
2408
+ if ("toJSON" in value && typeof value.toJSON === "function") {
2409
+ return takeValue(value.toJSON(), budget, depth + 1);
2410
+ }
2411
+ if (Array.isArray(value)) {
2412
+ budget.remaining -= 2;
2413
+ const out2 = [];
2414
+ for (const item of value) {
2415
+ if (budget.remaining <= 0) {
2416
+ budget.truncated = true;
2417
+ out2.push(TRUNCATION_MARKER2);
2418
+ break;
2419
+ }
2420
+ out2.push(takeValue(item, budget, depth + 1));
2421
+ }
2422
+ return out2;
2423
+ }
2424
+ budget.remaining -= 2;
2425
+ const out = {};
2426
+ if (!isRecord(value)) return out;
2427
+ for (const key in value) {
2428
+ if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
2429
+ if (budget.remaining <= 0) {
2430
+ budget.truncated = true;
2431
+ out["..."] = TRUNCATION_MARKER2;
2432
+ break;
2433
+ }
2434
+ budget.remaining -= Math.max(key.length, 1);
2435
+ out[key] = takeValue(value[key], budget, depth + 1);
2436
+ }
2437
+ return out;
2438
+ }
2439
+ function messageType(message) {
2440
+ return typeof message._getType === "function" ? message._getType() : void 0;
2441
+ }
2442
+ function textPart(text, budget) {
2443
+ return { type: "text", text: takeText(text, budget) };
2444
+ }
2445
+ function joinedText(content, budget) {
2446
+ const texts = [];
2447
+ for (const block of content) {
2448
+ if (budget.remaining <= 0) {
2449
+ budget.truncated = true;
2450
+ texts.push(TRUNCATION_MARKER2);
2451
+ break;
2452
+ }
2453
+ budget.remaining -= 1;
2454
+ if (!isRecord(block)) continue;
2455
+ const text = stringField(block, "text");
2456
+ if (text !== void 0) texts.push(takeText(text, budget));
2457
+ }
2458
+ return texts.join("\n");
2459
+ }
2460
+ function contentPart(block, budget) {
2461
+ budget.remaining -= PART_OVERHEAD;
2462
+ if (!isRecord(block)) return takeValue(block, budget, 0);
2463
+ const type = stringField(block, "type");
2464
+ const text = stringField(block, "text");
2465
+ if (type === "text" && text !== void 0) return textPart(text, budget);
2466
+ if (type === "image_url") {
2467
+ const imageUrl = block["image_url"];
2468
+ const url = typeof imageUrl === "string" ? imageUrl : isRecord(imageUrl) ? stringField(imageUrl, "url") : void 0;
2469
+ if (url !== void 0) return { type: "image", image: takeText(url, budget) };
2470
+ }
2471
+ if (type === "thinking") {
2472
+ const thinking = stringField(block, "thinking");
2473
+ if (thinking !== void 0) return { type: "reasoning", text: takeText(thinking, budget) };
2474
+ }
2475
+ if (type === "image" || type === "file" || type === "audio") {
2476
+ const mediaType = stringField(block, "mime_type");
2477
+ const sourceType = stringField(block, "source_type");
2478
+ const source = block["source"];
2479
+ if (sourceType === "text" && text !== void 0) return textPart(text, budget);
2480
+ const data = sourceType === "url" ? stringField(block, "url") : sourceType === "base64" ? stringField(block, "data") : isRecord(source) ? stringField(source, "url") ?? stringField(source, "data") : void 0;
2481
+ const sourceMediaType = mediaType ?? (isRecord(source) ? stringField(source, "media_type") : void 0);
2482
+ if (data !== void 0) {
2483
+ const filename = stringField(block, "filename");
2484
+ return type === "image" ? { type: "image", image: takeText(data, budget), mediaType: reserve(sourceMediaType, budget) } : {
2485
+ type: "file",
2486
+ data: takeText(data, budget),
2487
+ mediaType: reserve(sourceMediaType, budget),
2488
+ filename: reserve(filename, budget)
2489
+ };
2490
+ }
2491
+ }
2492
+ return takeValue(block, budget, 0);
2493
+ }
2494
+ function contentParts(content, budget, skipToolUseIds) {
2495
+ const parts = [];
2496
+ for (const block of content) {
2497
+ if (budget.remaining <= 0) {
2498
+ budget.truncated = true;
2499
+ parts.push({ type: "text", text: TRUNCATION_MARKER2 });
2500
+ break;
2501
+ }
2502
+ if (isRecord(block) && (block["type"] === "tool_use" || block["type"] === "tool_call")) {
2503
+ const id = stringField(block, "id");
2504
+ if (id !== void 0 && skipToolUseIds.has(id)) continue;
2505
+ }
2506
+ parts.push(contentPart(block, budget));
2507
+ }
2508
+ return parts;
2509
+ }
2510
+ function assistantMessage(message, budget, toolNames) {
2511
+ const toolCalls = isAIMessage(message) ? message.tool_calls ?? [] : [];
2512
+ if (toolCalls.length === 0 && typeof message.content === "string") {
2513
+ return { role: "assistant", content: takeText(message.content, budget) };
2514
+ }
2515
+ const ids = /* @__PURE__ */ new Set();
2516
+ const callParts = [];
2517
+ for (const call of toolCalls) {
2518
+ if (budget.remaining <= 0) {
2519
+ budget.truncated = true;
2520
+ callParts.push({ type: "text", text: TRUNCATION_MARKER2 });
2521
+ break;
2522
+ }
2523
+ budget.remaining -= PART_OVERHEAD;
2524
+ if (call.id !== void 0) {
2525
+ ids.add(call.id);
2526
+ toolNames.set(call.id, call.name);
2527
+ }
2528
+ callParts.push({
2529
+ type: "tool-call",
2530
+ toolCallId: reserve(call.id, budget),
2531
+ toolName: reserve(call.name, budget),
2532
+ input: takeValue(call.args, budget, 0)
2533
+ });
2534
+ }
2535
+ const content = typeof message.content === "string" ? message.content.length > 0 ? [textPart(message.content, budget)] : [] : contentParts(message.content, budget, ids);
2536
+ content.push(...callParts);
2537
+ return { role: "assistant", content };
2538
+ }
2539
+ function toolResultOutput(message, budget) {
2540
+ const failed = isToolMessage(message) && message.status === "error";
2541
+ if (typeof message.content === "string") {
2542
+ return { type: failed ? "error-text" : "text", value: takeText(message.content, budget) };
2543
+ }
2544
+ return { type: "content", value: contentParts(message.content, budget, /* @__PURE__ */ new Set()) };
2545
+ }
2546
+ function toolMessage(message, budget, toolNames) {
2547
+ const toolCallId = isToolMessage(message) ? message.tool_call_id : void 0;
2548
+ const toolName = message.name ?? (toolCallId !== void 0 ? toolNames.get(toolCallId) : void 0);
2549
+ budget.remaining -= PART_OVERHEAD;
2550
+ return {
2551
+ role: "tool",
2552
+ content: [{
2553
+ type: "tool-result",
2554
+ toolCallId: reserve(toolCallId, budget),
2555
+ toolName: reserve(toolName, budget),
2556
+ output: toolResultOutput(message, budget)
2557
+ }]
2558
+ };
2559
+ }
2560
+ function modelMessage(message, budget, toolNames) {
2561
+ budget.remaining -= MESSAGE_OVERHEAD;
2562
+ switch (messageType(message)) {
2563
+ case "system":
2564
+ return {
2565
+ role: "system",
2566
+ content: typeof message.content === "string" ? takeText(message.content, budget) : joinedText(message.content, budget)
2567
+ };
2568
+ case "ai":
2569
+ return assistantMessage(message, budget, toolNames);
2570
+ case "tool":
2571
+ case "function":
2572
+ return toolMessage(message, budget, toolNames);
2573
+ case "generic": {
2574
+ const role = isChatMessage(message) ? message.role : void 0;
2575
+ if (role === "assistant" || role === "system") {
2576
+ return {
2577
+ role,
2578
+ content: typeof message.content === "string" ? takeText(message.content, budget) : joinedText(message.content, budget)
2579
+ };
2580
+ }
2581
+ return userMessage(message, budget);
2582
+ }
2583
+ default:
2584
+ return userMessage(message, budget);
2585
+ }
2586
+ }
2587
+ function userMessage(message, budget) {
2588
+ return {
2589
+ role: "user",
2590
+ content: typeof message.content === "string" ? takeText(message.content, budget) : contentParts(message.content, budget, /* @__PURE__ */ new Set())
2591
+ };
2592
+ }
2593
+ function truncationRecord(omittedMessages) {
2594
+ return { role: CAPTURE_TRUNCATED_ROLE, truncated: true, marker: TRUNCATION_MARKER2, omittedMessages };
2595
+ }
2596
+ function encodeWithinLimit(items, limit, overflow, convert) {
2597
+ let textBudget = limit;
2598
+ for (let attempt = 0; attempt < 8 && textBudget >= 64; attempt++) {
2599
+ const budget = { remaining: textBudget, truncated: false };
2600
+ const out = [];
2601
+ let omitted = 0;
2602
+ for (let i = 0; i < items.length; i++) {
2603
+ if (overflow === "omit" && budget.remaining <= 0) {
2604
+ omitted = items.length - i;
2605
+ break;
2606
+ }
2607
+ out.push(convert(items[i], budget));
2608
+ }
2609
+ const truncated = budget.truncated || omitted > 0;
2610
+ if (truncated && overflow === "omit") out.push(truncationRecord(omitted));
2611
+ const json2 = JSON.stringify(out);
2612
+ if (json2.length <= limit) return { json: json2, truncated };
2613
+ const excess = json2.length - limit + 128;
2614
+ textBudget = Math.max(textBudget - excess, Math.floor(textBudget / 2));
2615
+ }
2616
+ if (overflow === "keep") return void 0;
2617
+ const json = JSON.stringify([truncationRecord(items.length)]);
2618
+ return json.length <= limit ? { json, truncated: true } : void 0;
2619
+ }
2620
+ function captureConversation(messages, limit) {
2621
+ if (messages.length === 0) return void 0;
2622
+ try {
2623
+ const toolNames = /* @__PURE__ */ new Map();
2624
+ return encodeWithinLimit(
2625
+ messages,
2626
+ limit,
2627
+ "omit",
2628
+ (message, budget) => modelMessage(message, budget, toolNames)
2629
+ );
2630
+ } catch {
2631
+ return void 0;
2632
+ }
2633
+ }
2634
+ function capturePrompt(prompt, limit) {
2635
+ try {
2636
+ return encodeWithinLimit([prompt], limit, "omit", (text, budget) => {
2637
+ budget.remaining -= MESSAGE_OVERHEAD;
2638
+ return { role: "user", content: takeText(text, budget) };
2639
+ });
2640
+ } catch {
2641
+ return void 0;
2642
+ }
2643
+ }
2644
+ function captureToolCalls(message, limit) {
2645
+ if (!isRecord(message) || !Array.isArray(message["tool_calls"])) return void 0;
2646
+ const calls = [];
2647
+ let structural = 0;
2648
+ for (const call of message["tool_calls"]) {
2649
+ structural += PART_OVERHEAD;
2650
+ if (isRecord(call)) {
2651
+ structural += (stringField(call, "id")?.length ?? 0) + (stringField(call, "name")?.length ?? 0);
2652
+ calls.push(call);
2653
+ }
2654
+ if (structural > limit) return void 0;
2655
+ }
2656
+ if (calls.length === 0) return void 0;
2657
+ try {
2658
+ return encodeWithinLimit(calls, limit, "keep", (call, budget) => {
2659
+ budget.remaining -= PART_OVERHEAD;
2660
+ return {
2661
+ toolCallId: reserve(stringField(call, "id"), budget),
2662
+ toolName: reserve(stringField(call, "name"), budget),
2663
+ input: takeValue(call["args"], budget, 0)
2664
+ };
2665
+ });
2666
+ } catch {
2667
+ return void 0;
2668
+ }
2669
+ }
2670
+ function parseToolCallInput(input) {
2671
+ try {
2672
+ const parsed = JSON.parse(input);
2673
+ if (isRecord(parsed) && parsed["type"] === "tool_call" && "args" in parsed) {
2674
+ return { id: stringField(parsed, "id"), args: JSON.stringify(parsed["args"]) };
2675
+ }
2676
+ } catch {
2677
+ }
2678
+ return { args: input };
2679
+ }
2680
+ function unwrapToolOutput(output) {
2681
+ if (isBaseMessage(output)) {
2682
+ return {
2683
+ content: output.content,
2684
+ toolCallId: isToolMessage(output) ? output.tool_call_id : void 0
2685
+ };
2686
+ }
2687
+ return { content: output };
2688
+ }
2689
+
1593
2690
  // src/callback-handler.ts
1594
2691
  function resolveEventId(source) {
1595
2692
  if (typeof source !== "function") return randomUUID();
@@ -1607,32 +2704,38 @@ function isInternalName(name) {
1607
2704
  if (name.startsWith("__") && name.endsWith("__")) return true;
1608
2705
  return false;
1609
2706
  }
1610
- var CHAIN_TEXT_MAX_LEN = 8192;
1611
- function extractTextFromContent(content) {
2707
+ function extractTextFromContent(content, limit) {
1612
2708
  if (content == null) return void 0;
1613
2709
  if (typeof content === "string") return content;
1614
2710
  if (Array.isArray(content)) {
1615
2711
  const parts = [];
2712
+ let length = 0;
1616
2713
  for (const part of content) {
2714
+ if (length > limit) break;
2715
+ length += 1;
2716
+ let text;
1617
2717
  if (typeof part === "string") {
1618
- parts.push(part);
2718
+ text = part;
1619
2719
  } else if (part && typeof part === "object") {
1620
2720
  const p = part;
1621
2721
  if (typeof p.text === "string") {
1622
- parts.push(p.text);
2722
+ text = p.text;
1623
2723
  } else if (p.type === "text" && typeof p.content === "string") {
1624
- parts.push(p.content);
2724
+ text = p.content;
1625
2725
  }
1626
2726
  }
2727
+ if (text === void 0) continue;
2728
+ parts.push(text.length > limit ? text.slice(0, limit + 1) : text);
2729
+ length += Math.max(text.length, 1);
1627
2730
  }
1628
2731
  if (parts.length > 0) return parts.join("\n");
1629
- return safeStringify(content, CHAIN_TEXT_MAX_LEN);
2732
+ return boundedStringify(content, limit);
1630
2733
  }
1631
2734
  if (typeof content === "object") {
1632
2735
  const c = content;
1633
2736
  if (typeof c.text === "string") return c.text;
1634
2737
  if (typeof c.content === "string") return c.content;
1635
- return safeStringify(content, CHAIN_TEXT_MAX_LEN);
2738
+ return boundedStringify(content, limit);
1636
2739
  }
1637
2740
  return String(content);
1638
2741
  }
@@ -1651,6 +2754,11 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
1651
2754
  this.eventName = opts.eventName;
1652
2755
  this.eventIdSource = opts.eventId;
1653
2756
  this.traceChains = opts.traceChains ?? true;
2757
+ this.configuredSpanMaxChars = opts.maxTextFieldChars ?? eventMaxTextFieldChars();
2758
+ this.eventMaxChars = opts.eventMaxTextFieldChars ?? eventMaxTextFieldChars();
2759
+ }
2760
+ get spanMaxChars() {
2761
+ return spanAttributeLimit(this.configuredSpanMaxChars);
1654
2762
  }
1655
2763
  getEventId(runId, parentRunId) {
1656
2764
  const rootId = parentRunId ? this.rootRunIds.get(parentRunId) ?? parentRunId : runId;
@@ -1716,13 +2824,21 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
1716
2824
  if (this.spans.has(runId)) return;
1717
2825
  const eventId = this.getEventId(runId, parentRunId);
1718
2826
  const modelId = getName(llm, runName);
2827
+ const prompt = prompts[0] ?? "";
1719
2828
  const span = this.traceShipper.startSpan({
1720
2829
  name: "llm",
1721
2830
  parent: this.getParent(parentRunId),
1722
2831
  eventId,
2832
+ // `ai.generateText` classifies the span as an LLM generation at ingest
2833
+ // (a bare `llm`/`chat` operationId maps to INTERNAL and is skipped by
2834
+ // the rich-interaction extractor). `ai.prompt.messages` carries the
2835
+ // per-step trajectory so replay can see the history each step ran on.
2836
+ // A batch is a list of independent prompts; the span describes the
2837
+ // first one, whose response `handleLLMEnd` reports (`generations[0][0]`).
1723
2838
  attributes: [
1724
2839
  attrString("ai.model.provider", modelId),
1725
- attrString("ai.operationId", "llm")
2840
+ attrString("ai.operationId", "ai.generateText"),
2841
+ ...promptAttributes(capturePrompt(prompt, this.spanMaxChars))
1726
2842
  ]
1727
2843
  });
1728
2844
  this.spans.set(runId, span);
@@ -1730,7 +2846,7 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
1730
2846
  userId: this.userId,
1731
2847
  convoId: this.convoId,
1732
2848
  eventName: this.eventName,
1733
- input: truncate(prompts.join("\n"), CHAIN_TEXT_MAX_LEN),
2849
+ input: capText2(prompt, this.eventMaxChars),
1734
2850
  properties: buildProperties(tags, metadata),
1735
2851
  isPending: true
1736
2852
  });
@@ -1742,24 +2858,31 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
1742
2858
  if (this.spans.has(runId)) return;
1743
2859
  const eventId = this.getEventId(runId, parentRunId);
1744
2860
  const modelId = getName(llm, runName);
2861
+ const conversation = messages[0] ?? [];
1745
2862
  const span = this.traceShipper.startSpan({
1746
2863
  name: "llm",
1747
2864
  parent: this.getParent(parentRunId),
1748
2865
  eventId,
2866
+ // See handleLLMStart: the full history (historical tool calls and
2867
+ // their results included) goes on the span; the event `input` below
2868
+ // stays the latest human message. LangChain batches are a list of
2869
+ // independent conversations; the span describes the first one, the
2870
+ // same member `handleLLMEnd` reports the response of
2871
+ // (`generations[0][0]`), so history and response stay paired.
1749
2872
  attributes: [
1750
2873
  attrString("ai.model.provider", modelId),
1751
- attrString("ai.operationId", "chat")
2874
+ attrString("ai.operationId", "ai.generateText"),
2875
+ ...promptAttributes(captureConversation(conversation, this.spanMaxChars))
1752
2876
  ]
1753
2877
  });
1754
2878
  this.spans.set(runId, span);
1755
- const allMessages = messages.flat();
1756
- const userMessages = allMessages.filter((m) => {
2879
+ const userMessages = conversation.filter((m) => {
1757
2880
  const role = m._getType?.() ?? "unknown";
1758
2881
  return role === "human";
1759
2882
  });
1760
- const lastMsg = userMessages.length > 0 ? userMessages[userMessages.length - 1] : allMessages[allMessages.length - 1];
1761
- const extracted = lastMsg ? extractTextFromContent(lastMsg.content) : void 0;
1762
- const inputText = extracted !== void 0 ? truncate(extracted, CHAIN_TEXT_MAX_LEN) : void 0;
2883
+ const lastMsg = userMessages.length > 0 ? userMessages[userMessages.length - 1] : conversation[conversation.length - 1];
2884
+ const extracted = lastMsg ? extractTextFromContent(lastMsg.content, this.eventMaxChars) : void 0;
2885
+ const inputText = capText2(extracted, this.eventMaxChars);
1763
2886
  await this.eventShipper.patch(eventId, {
1764
2887
  userId: this.userId,
1765
2888
  convoId: this.convoId,
@@ -1782,7 +2905,7 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
1782
2905
  await this.finalizeEventIfRoot(runId);
1783
2906
  return;
1784
2907
  }
1785
- const meta = extractLLMMetadata(output);
2908
+ const meta = extractLLMMetadata(output, this.spanMaxChars);
1786
2909
  const {
1787
2910
  model,
1788
2911
  promptTokens,
@@ -1790,11 +2913,17 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
1790
2913
  cachedTokens,
1791
2914
  reasoningTokens,
1792
2915
  finishReason,
1793
- outputText
2916
+ outputText,
2917
+ toolCalls
1794
2918
  } = meta;
1795
2919
  this.traceShipper.endSpan(span, {
1796
2920
  attributes: [
1797
2921
  attrString("ai.response.model", model),
2922
+ attrString("ai.response.text", capText2(outputText, this.spanMaxChars)),
2923
+ // Tool calls requested by this step, with the ids the matching
2924
+ // `ai.toolCall` spans and the next step's history carry.
2925
+ attrString("ai.response.toolCalls", toolCalls?.json),
2926
+ attrString("ai.response.toolCalls.truncated", toolCalls?.truncated ? "true" : void 0),
1798
2927
  attrInt("ai.usage.prompt_tokens", promptTokens),
1799
2928
  attrInt("ai.usage.completion_tokens", completionTokens),
1800
2929
  attrInt("ai.usage.cached_tokens", cachedTokens),
@@ -1806,9 +2935,8 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
1806
2935
  if (cachedTokens != null) properties["ai.usage.cached_tokens"] = cachedTokens;
1807
2936
  if (reasoningTokens != null) properties["ai.usage.thoughts_tokens"] = reasoningTokens;
1808
2937
  if (finishReason != null) properties["ai.finish_reason"] = finishReason;
1809
- const cappedOutput = outputText !== void 0 ? truncate(outputText, CHAIN_TEXT_MAX_LEN) : void 0;
1810
2938
  await this.finalizeEventIfRoot(runId, {
1811
- output: cappedOutput,
2939
+ output: capText2(outputText, this.eventMaxChars),
1812
2940
  model,
1813
2941
  usage: { promptTokens, completionTokens },
1814
2942
  properties: Object.keys(properties).length > 0 ? properties : void 0
@@ -1904,14 +3032,16 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
1904
3032
  try {
1905
3033
  const eventId = this.getEventId(runId, parentRunId);
1906
3034
  const toolName = getName(tool, runName);
3035
+ const toolCall = parseToolCallInput(input);
1907
3036
  const span = this.traceShipper.startSpan({
1908
3037
  name: "tool",
1909
3038
  parent: this.getParent(parentRunId),
1910
3039
  eventId,
1911
3040
  operationId: "ai.toolCall",
1912
3041
  attributes: [
3042
+ attrString("ai.toolCall.id", toolCall.id),
1913
3043
  attrString("ai.toolCall.name", toolName),
1914
- attrString("tool.input", truncate(input, 4096))
3044
+ attrString("ai.toolCall.args", capText2(toolCall.args, this.spanMaxChars))
1915
3045
  ]
1916
3046
  });
1917
3047
  this.spans.set(runId, span);
@@ -1926,9 +3056,14 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
1926
3056
  try {
1927
3057
  const span = this.spans.get(runId);
1928
3058
  if (span) {
1929
- const outputStr = typeof output === "string" ? output : safeStringify(output, 4096);
3059
+ const { content, toolCallId } = unwrapToolOutput(output);
3060
+ const result = typeof content === "string" ? capText2(content, this.spanMaxChars) : boundedStringify(content, this.spanMaxChars);
3061
+ const hasId = span.attributes.some((attr) => attr?.key === "ai.toolCall.id");
1930
3062
  this.traceShipper.endSpan(span, {
1931
- attributes: [attrString("tool.output", truncate(outputStr, 4096))]
3063
+ attributes: [
3064
+ attrString("ai.toolCall.id", hasId ? void 0 : toolCallId),
3065
+ attrString("ai.toolCall.result", result)
3066
+ ]
1932
3067
  });
1933
3068
  }
1934
3069
  await this.finalizeEventIfRoot(runId);
@@ -1964,10 +3099,10 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
1964
3099
  operationId: "ai.toolCall",
1965
3100
  attributes: [
1966
3101
  attrString("ai.toolCall.name", action.tool),
1967
- attrString("agent.tool_input", truncate(
1968
- typeof action.toolInput === "string" ? action.toolInput : safeStringify(action.toolInput, 4096),
1969
- 4096
1970
- ))
3102
+ attrString(
3103
+ "agent.tool_input",
3104
+ typeof action.toolInput === "string" ? capText2(action.toolInput, this.spanMaxChars) : boundedStringify(action.toolInput, this.spanMaxChars)
3105
+ )
1971
3106
  ]
1972
3107
  });
1973
3108
  this.spans.set(`${runId}:agent_action`, span);
@@ -2000,75 +3135,13 @@ function buildProperties(tags, metadata) {
2000
3135
  if (metadata) Object.assign(props, metadata);
2001
3136
  return props;
2002
3137
  }
2003
- var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
2004
- var MAX_TEXT_FIELD_CHARS = 1e6;
2005
- var MAX_BOUNDED_DEPTH = 12;
2006
- function boundedClone2(value, budget, depth) {
2007
- if (budget.remaining <= 0) return TRUNCATION_MARKER2;
2008
- if (typeof value === "string") {
2009
- if (value.length > budget.remaining) {
2010
- const taken = value.slice(0, Math.max(0, budget.remaining)) + TRUNCATION_MARKER2;
2011
- budget.remaining = 0;
2012
- return taken;
2013
- }
2014
- budget.remaining -= Math.max(value.length, 1);
2015
- return value;
2016
- }
2017
- if (value === null || typeof value === "number" || typeof value === "boolean") {
2018
- budget.remaining -= 8;
2019
- return value;
2020
- }
2021
- if (typeof value !== "object") {
2022
- budget.remaining -= 8;
2023
- return value;
2024
- }
2025
- if (depth >= MAX_BOUNDED_DEPTH) {
2026
- budget.remaining -= 16;
2027
- return `<max depth: ${TRUNCATION_MARKER2}>`;
2028
- }
2029
- const withToJson = value;
2030
- if (typeof withToJson.toJSON === "function") {
2031
- try {
2032
- return boundedClone2(withToJson.toJSON(), budget, depth + 1);
2033
- } catch {
2034
- }
2035
- }
2036
- if (Array.isArray(value)) {
2037
- const out2 = [];
2038
- for (const item of value) {
2039
- if (budget.remaining <= 0) {
2040
- out2.push(TRUNCATION_MARKER2);
2041
- break;
2042
- }
2043
- out2.push(boundedClone2(item, budget, depth + 1));
2044
- }
2045
- return out2;
2046
- }
2047
- const out = {};
2048
- for (const key of Object.keys(value)) {
2049
- if (budget.remaining <= 0) {
2050
- out["..."] = TRUNCATION_MARKER2;
2051
- break;
2052
- }
2053
- budget.remaining -= Math.max(key.length, 1);
2054
- out[key] = boundedClone2(value[key], budget, depth + 1);
2055
- }
2056
- return out;
2057
- }
2058
- function safeStringify(value, limit = MAX_TEXT_FIELD_CHARS) {
2059
- try {
2060
- const pruned = boundedClone2(value, { remaining: limit + TRUNCATION_MARKER2.length + 256 }, 0);
2061
- return JSON.stringify(pruned) ?? "";
2062
- } catch {
2063
- const fallback = String(value);
2064
- return fallback.length > limit ? fallback.slice(0, limit) : fallback;
2065
- }
2066
- }
2067
- function truncate(str, maxLen) {
2068
- if (str.length <= maxLen) return str;
2069
- return str.slice(0, maxLen - 3) + "...";
3138
+ function promptAttributes(capture) {
3139
+ return [
3140
+ attrString("ai.prompt.messages", capture?.json),
3141
+ attrString("ai.prompt.messages.truncated", capture?.truncated ? "true" : void 0)
3142
+ ];
2070
3143
  }
2071
- function extractLLMMetadata(output) {
3144
+ function extractLLMMetadata(output, limit) {
2072
3145
  const gen = output.generations?.[0]?.[0];
2073
3146
  const message = gen?.message;
2074
3147
  const responseMeta = message?.response_metadata;
@@ -2094,7 +3167,7 @@ function extractLLMMetadata(output) {
2094
3167
  outputDetails?.reasoning ?? usage?.reasoning_tokens ?? usage?.thoughts_tokens
2095
3168
  );
2096
3169
  const finishReason = generationInfo?.finish_reason ?? responseMeta?.finish_reason ?? responseMeta?.stop_reason;
2097
- const outputText = gen?.text ?? (typeof message?.content === "string" ? message.content : void 0) ?? (message?.content ? extractTextFromContent(message.content) : void 0);
3170
+ const outputText = gen?.text ?? (typeof message?.content === "string" ? message.content : void 0) ?? (message?.content ? extractTextFromContent(message.content, limit) : void 0);
2098
3171
  return {
2099
3172
  model,
2100
3173
  promptTokens,
@@ -2102,7 +3175,8 @@ function extractLLMMetadata(output) {
2102
3175
  cachedTokens,
2103
3176
  reasoningTokens,
2104
3177
  finishReason,
2105
- outputText
3178
+ outputText,
3179
+ toolCalls: captureToolCalls(message, limit)
2106
3180
  };
2107
3181
  }
2108
3182
  function asNumber(val) {
@@ -2111,20 +3185,91 @@ function asNumber(val) {
2111
3185
  return Number.isFinite(n) ? n : void 0;
2112
3186
  }
2113
3187
 
3188
+ // package.json
3189
+ var package_default = {
3190
+ name: "@raindrop-ai/deep-agents",
3191
+ version: "0.1.3",
3192
+ description: "Raindrop integration for LangChain Deep Agents",
3193
+ main: "dist/index.js",
3194
+ module: "dist/index.mjs",
3195
+ types: "dist/index.d.ts",
3196
+ license: "MIT",
3197
+ homepage: "https://www.raindrop.ai/docs/integrations/deepagents/",
3198
+ bugs: {
3199
+ url: "https://www.raindrop.ai/docs/support/"
3200
+ },
3201
+ exports: {
3202
+ ".": {
3203
+ types: "./dist/index.d.ts",
3204
+ import: "./dist/index.mjs",
3205
+ require: "./dist/index.js"
3206
+ }
3207
+ },
3208
+ sideEffects: false,
3209
+ files: [
3210
+ "dist/**"
3211
+ ],
3212
+ scripts: {
3213
+ build: "tsup",
3214
+ dev: "tsup --watch",
3215
+ clean: "rm -rf dist",
3216
+ test: "vitest run"
3217
+ },
3218
+ peerDependencies: {
3219
+ "@langchain/core": ">=0.3.0",
3220
+ deepagents: ">=0.5.0"
3221
+ },
3222
+ devDependencies: {
3223
+ "@langchain/anthropic": "~0.3.34",
3224
+ "@langchain/core": ">=0.3.0",
3225
+ "@langchain/openai": "~0.3.17",
3226
+ "@raindrop-ai/core": "workspace:*",
3227
+ "@types/node": "^20.11.17",
3228
+ deepagents: "1.0.0",
3229
+ msw: "^2.12.7",
3230
+ tsup: "^8.4.0",
3231
+ typescript: "^5.3.3",
3232
+ vitest: "^2.1.9",
3233
+ zod: "^3.23.0"
3234
+ },
3235
+ tsup: {
3236
+ entry: [
3237
+ "src/index.ts"
3238
+ ],
3239
+ format: [
3240
+ "cjs",
3241
+ "esm"
3242
+ ],
3243
+ dts: {
3244
+ resolve: true
3245
+ },
3246
+ clean: true,
3247
+ noExternal: [
3248
+ "@raindrop-ai/core"
3249
+ ]
3250
+ },
3251
+ publishConfig: {
3252
+ access: "public"
3253
+ }
3254
+ };
3255
+
2114
3256
  // src/version.ts
2115
- var libraryName = "@raindrop-ai/deep-agents";
2116
- var libraryVersion = "0.0.1";
3257
+ var libraryName = package_default.name;
3258
+ var libraryVersion = package_default.version;
2117
3259
 
2118
3260
  // src/index.ts
2119
3261
  function createRaindropDeepAgents(opts) {
2120
3262
  const writeKey = opts.writeKey;
2121
3263
  const enabled = !!writeKey;
3264
+ setMaxTextFieldChars(opts.maxTextFieldChars);
3265
+ const maxChars = eventMaxTextFieldChars();
2122
3266
  if (!writeKey) {
2123
3267
  console.warn(
2124
3268
  "[raindrop-ai/deep-agents] writeKey not provided; telemetry shipping is disabled"
2125
3269
  );
2126
3270
  }
2127
- const eventShipper = new EventShipper({
3271
+ const appGitContext = createAppGitContext2(writeKey?.trim() ? opts.appGit : false, { allowLocalGit: true });
3272
+ const eventShipper = new EventShipper2({
2128
3273
  writeKey,
2129
3274
  endpoint: opts.endpoint,
2130
3275
  enabled,
@@ -2132,9 +3277,11 @@ function createRaindropDeepAgents(opts) {
2132
3277
  projectId: opts.projectId,
2133
3278
  sdkName: "deep-agents",
2134
3279
  libraryName,
2135
- libraryVersion
3280
+ libraryVersion,
3281
+ appGitContext,
3282
+ maxTextFieldChars: maxChars
2136
3283
  });
2137
- const traceShipper = new TraceShipper({
3284
+ const traceShipper = new TraceShipper2({
2138
3285
  writeKey,
2139
3286
  endpoint: opts.endpoint,
2140
3287
  enabled,
@@ -2142,7 +3289,9 @@ function createRaindropDeepAgents(opts) {
2142
3289
  projectId: opts.projectId,
2143
3290
  sdkName: "deep-agents",
2144
3291
  serviceName: "raindrop.deep-agents",
2145
- serviceVersion: libraryVersion
3292
+ serviceVersion: libraryVersion,
3293
+ appGitContext,
3294
+ maxTextFieldChars: maxChars
2146
3295
  });
2147
3296
  const handler = new RaindropDeepAgentsHandler({
2148
3297
  eventShipper,
@@ -2151,7 +3300,9 @@ function createRaindropDeepAgents(opts) {
2151
3300
  convoId: opts.convoId,
2152
3301
  eventName: opts.eventName,
2153
3302
  eventId: opts.eventId,
2154
- traceChains: opts.traceChains
3303
+ traceChains: opts.traceChains,
3304
+ maxTextFieldChars: maxChars,
3305
+ eventMaxTextFieldChars: maxChars
2155
3306
  });
2156
3307
  return {
2157
3308
  handler,
@@ -2162,7 +3313,14 @@ function createRaindropDeepAgents(opts) {
2162
3313
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
2163
3314
  },
2164
3315
  async shutdown() {
2165
- await Promise.all([eventShipper.shutdown(), traceShipper.shutdown()]);
3316
+ try {
3317
+ await Promise.all([eventShipper.shutdown(), traceShipper.shutdown()]);
3318
+ } finally {
3319
+ try {
3320
+ appGitContext.dispose();
3321
+ } catch {
3322
+ }
3323
+ }
2166
3324
  },
2167
3325
  events: {
2168
3326
  async patch(eventId, patch) {