@raindrop-ai/deep-agents 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -28,7 +28,69 @@ module.exports = __toCommonJS(index_exports);
28
28
  // src/callback-handler.ts
29
29
  var import_base = require("@langchain/core/callbacks/base");
30
30
 
31
- // ../core/dist/chunk-267IXHO5.js
31
+ // ../core/dist/chunk-BYBMRLZM.js
32
+ var EVAL_CORRELATION_ID_ATTRIBUTE = "raindrop.eval_correlation_id";
33
+ var PROBE_SCOPE = { correlationId: "" };
34
+ var EvalScopeStorage = class {
35
+ constructor() {
36
+ this._als = null;
37
+ this._probed = false;
38
+ }
39
+ maybeAdoptAsyncLocalStorage() {
40
+ if (this._probed) return;
41
+ this._probed = true;
42
+ try {
43
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
44
+ if (!Ctor) return;
45
+ const als = new Ctor();
46
+ als.run(PROBE_SCOPE, () => {
47
+ });
48
+ this._als = als;
49
+ } catch (e) {
50
+ this._als = null;
51
+ }
52
+ }
53
+ run(scope, callback) {
54
+ this.maybeAdoptAsyncLocalStorage();
55
+ if (this._als) return this._als.run(scope, callback);
56
+ const previous = this._syncScope;
57
+ this._syncScope = scope;
58
+ try {
59
+ return callback();
60
+ } finally {
61
+ this._syncScope = previous;
62
+ }
63
+ }
64
+ current() {
65
+ this.maybeAdoptAsyncLocalStorage();
66
+ if (this._als) return this._als.getStore();
67
+ return this._syncScope;
68
+ }
69
+ };
70
+ var EVAL_SCOPE_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.evalScopeStorage");
71
+ function evalScopeStorage() {
72
+ const holder = globalThis;
73
+ let storage = holder[EVAL_SCOPE_STORAGE_KEY];
74
+ if (!storage) {
75
+ storage = new EvalScopeStorage();
76
+ holder[EVAL_SCOPE_STORAGE_KEY] = storage;
77
+ }
78
+ return storage;
79
+ }
80
+ function currentEvalScope() {
81
+ try {
82
+ return evalScopeStorage().current();
83
+ } catch (e) {
84
+ return void 0;
85
+ }
86
+ }
87
+ function evalScopeAttributes() {
88
+ const scope = currentEvalScope();
89
+ if (!scope) return {};
90
+ return { [EVAL_CORRELATION_ID_ATTRIBUTE]: scope.correlationId };
91
+ }
92
+
93
+ // ../core/dist/chunk-T53LQAH7.js
32
94
  function getCrypto() {
33
95
  const c = globalThis.crypto;
34
96
  return c;
@@ -83,6 +145,39 @@ function base64Encode(bytes) {
83
145
  }
84
146
  return out;
85
147
  }
148
+ function base64ToHex(value) {
149
+ if (!value) return "";
150
+ const maybeBuffer = globalThis.Buffer;
151
+ if (maybeBuffer) {
152
+ return maybeBuffer.from(value, "base64").toString("hex");
153
+ }
154
+ const atobFn = globalThis.atob;
155
+ if (typeof atobFn !== "function") return "";
156
+ try {
157
+ const binary = atobFn(value);
158
+ let hex = "";
159
+ for (let i = 0; i < binary.length; i++) {
160
+ hex += (binary.charCodeAt(i) & 255).toString(16).padStart(2, "0");
161
+ }
162
+ return hex;
163
+ } catch (e) {
164
+ return "";
165
+ }
166
+ }
167
+ var HANDOFF_ATTRIBUTE_SUFFIXES = {
168
+ mode: "raindrop.handoff.mode",
169
+ childEventId: "raindrop.handoff.childEventId",
170
+ parentEventId: "raindrop.handoff.parentEventId",
171
+ parentSpanId: "raindrop.handoff.parentSpanId",
172
+ name: "raindrop.handoff.name",
173
+ terminal: "raindrop.handoff.terminal",
174
+ agentRole: "raindrop.agent.role"
175
+ };
176
+ var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
177
+ var TRACEPARENT_HEADER = "traceparent";
178
+ var BAGGAGE_HEADER = "baggage";
179
+ var HANDOFF_HEADER = "x-raindrop-handoff";
180
+ var UNRESOLVED_CARRIER_WARNING = `[raindrop] resume: headers were supplied but no hand-off carrier was found on them (searched ${HANDOFF_HEADER}, ${TRACEPARENT_HEADER}, ${BAGGAGE_HEADER}). This run reports as an UNLINKED event, and the launcher that dispatched it will stay on "queued" forever because nothing ever references its child. Passing headers means a parent handed off, so no carrier on them is almost certainly a defect: a gateway or proxy stripping ${HANDOFF_HEADER}, a middleware overwriting ${BAGGAGE_HEADER}, a request forwarded without its headers, or a hand-built carrier whose field names do not match. Send every header the launcher's dispatch returned. Logged once per process; pass no headers for a directly-invoked sub-agent to silence it.`;
86
181
  function runWithTracingSuppressed(fn) {
87
182
  const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
88
183
  if (typeof hook !== "function") return fn();
@@ -210,6 +305,7 @@ async function postJson(url, body, headers, opts) {
210
305
  const resp = await runWithTracingSuppressed(
211
306
  () => fetch(url, {
212
307
  method: "POST",
308
+ redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
213
309
  headers: {
214
310
  "Content-Type": "application/json",
215
311
  ...headers
@@ -311,6 +407,55 @@ function buildExportTraceServiceRequest(spans, serviceName = "raindrop.core", se
311
407
  ]
312
408
  };
313
409
  }
410
+ var STATE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.replayTraceRoutingState");
411
+ function state() {
412
+ const holder = globalThis;
413
+ let current = holder[STATE_KEY];
414
+ if (!current) {
415
+ let storage = null;
416
+ try {
417
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
418
+ if (Ctor) storage = new Ctor();
419
+ } catch (e) {
420
+ storage = null;
421
+ }
422
+ current = {
423
+ storage,
424
+ syncDestination: void 0,
425
+ spanDestinations: /* @__PURE__ */ new WeakMap()
426
+ };
427
+ holder[STATE_KEY] = current;
428
+ }
429
+ return current;
430
+ }
431
+ function currentReplayTraceDestination() {
432
+ var _a, _b;
433
+ const routing = state();
434
+ return (_b = (_a = routing.storage) == null ? void 0 : _a.getStore()) != null ? _b : routing.syncDestination;
435
+ }
436
+ function rememberReplayTraceDestination(span) {
437
+ const destination = currentReplayTraceDestination();
438
+ if (destination) state().spanDestinations.set(span, destination);
439
+ }
440
+ function copyReplayTraceDestination(source, target) {
441
+ const destination = state().spanDestinations.get(source);
442
+ if (destination) state().spanDestinations.set(target, destination);
443
+ }
444
+ function replayTraceDestinationFor(value) {
445
+ if (typeof value !== "object" && typeof value !== "function" || value === null) {
446
+ return void 0;
447
+ }
448
+ return state().spanDestinations.get(value);
449
+ }
450
+ function recordReplayExportedSpan(destination, spanId, traceId) {
451
+ var _a, _b;
452
+ if (!destination || !spanId || !traceId) return;
453
+ (_a = destination.exportedSpanIds) == null ? void 0 : _a.add(spanId);
454
+ (_b = destination.exportedTraceIds) == null ? void 0 : _b.add(traceId);
455
+ }
456
+ function replayTraceUrl(baseUrl) {
457
+ return `${baseUrl.replace(/\/+$/, "")}/v1/traces`;
458
+ }
314
459
  var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
315
460
  var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
316
461
  var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
@@ -373,26 +518,29 @@ function resolveLocalDebuggerBaseUrl(baseUrl) {
373
518
  if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
374
519
  return null;
375
520
  }
521
+ var LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS = 2e3;
376
522
  function mirrorTraceExportToLocalDebugger(body, options = {}) {
377
523
  var _a;
378
524
  const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
379
- if (!baseUrl) return;
380
- void postJson(`${baseUrl}traces`, body, {}, {
525
+ if (!baseUrl) return Promise.resolve();
526
+ return postJson(`${baseUrl}traces`, body, {}, {
381
527
  maxAttempts: 1,
382
528
  debug: (_a = options.debug) != null ? _a : false,
383
- sdkName: options.sdkName
529
+ sdkName: options.sdkName,
530
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
384
531
  }).catch(() => {
385
532
  });
386
533
  }
387
534
  function mirrorPartialEventToLocalDebugger(event, options = {}) {
388
535
  var _a;
389
536
  const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
390
- if (!baseUrl) return;
537
+ if (!baseUrl) return Promise.resolve();
391
538
  const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
392
- void postJson(`${baseUrl}events/track_partial`, event, headers, {
539
+ return postJson(`${baseUrl}events/track_partial`, event, headers, {
393
540
  maxAttempts: 1,
394
541
  debug: (_a = options.debug) != null ? _a : false,
395
- sdkName: options.sdkName
542
+ sdkName: options.sdkName,
543
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
396
544
  }).catch(() => {
397
545
  });
398
546
  }
@@ -418,19 +566,52 @@ function projectIdHeaders(projectId) {
418
566
  var SHUTDOWN_DEADLINE_MS = 1e4;
419
567
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
420
568
  function mergePatches(target, source) {
421
- var _a, _b, _c, _d, _e, _f;
569
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
422
570
  const out = { ...target, ...source };
423
571
  if (target.properties || source.properties) {
424
572
  out.properties = { ...(_a = target.properties) != null ? _a : {}, ...(_b = source.properties) != null ? _b : {} };
425
573
  }
574
+ if (target.usage || source.usage) {
575
+ out.usage = { ...(_c = target.usage) != null ? _c : {} };
576
+ if (((_d = source.usage) == null ? void 0 : _d.promptTokens) !== void 0) {
577
+ out.usage.promptTokens = source.usage.promptTokens;
578
+ }
579
+ if (((_e = source.usage) == null ? void 0 : _e.completionTokens) !== void 0) {
580
+ out.usage.completionTokens = source.usage.completionTokens;
581
+ }
582
+ }
583
+ if (source.error === void 0) {
584
+ out.error = target.error;
585
+ }
426
586
  if (target.featureFlags || source.featureFlags) {
427
- out.featureFlags = { ...(_c = target.featureFlags) != null ? _c : {}, ...(_d = source.featureFlags) != null ? _d : {} };
587
+ out.featureFlags = { ...(_f = target.featureFlags) != null ? _f : {}, ...(_g = source.featureFlags) != null ? _g : {} };
428
588
  }
429
589
  if (target.attachments || source.attachments) {
430
- out.attachments = [...(_e = target.attachments) != null ? _e : [], ...(_f = source.attachments) != null ? _f : []];
590
+ out.attachments = [...(_h = target.attachments) != null ? _h : [], ...(_i = source.attachments) != null ? _i : []];
431
591
  }
432
592
  return out;
433
593
  }
594
+ function isErrorLike(value) {
595
+ return value !== null && typeof value === "object";
596
+ }
597
+ function eventTelemetryProperties(patch) {
598
+ var _a, _b;
599
+ const properties = {};
600
+ if (((_a = patch.usage) == null ? void 0 : _a.promptTokens) !== void 0 && Number.isFinite(patch.usage.promptTokens)) {
601
+ properties["ai.usage.prompt_tokens"] = patch.usage.promptTokens;
602
+ }
603
+ if (((_b = patch.usage) == null ? void 0 : _b.completionTokens) !== void 0 && Number.isFinite(patch.usage.completionTokens)) {
604
+ properties["ai.usage.completion_tokens"] = patch.usage.completionTokens;
605
+ }
606
+ if (patch.error !== void 0 && patch.error !== null) {
607
+ const constructorName = patch.error instanceof Error && patch.error.constructor !== Error ? patch.error.constructor.name : void 0;
608
+ const errorType = patch.error instanceof Error ? patch.error.name && patch.error.name !== "Error" ? patch.error.name : constructorName || patch.error.name || "Error" : isErrorLike(patch.error) && typeof patch.error.name === "string" && patch.error.name.length > 0 ? patch.error.name : "Error";
609
+ const errorMessage = patch.error instanceof Error ? patch.error.message : isErrorLike(patch.error) && typeof patch.error.message === "string" ? patch.error.message : String(patch.error);
610
+ properties["error.type"] = errorType;
611
+ properties["error.message"] = errorMessage;
612
+ }
613
+ return properties;
614
+ }
434
615
  var EventShipper = class {
435
616
  constructor(opts) {
436
617
  this.buffers = /* @__PURE__ */ new Map();
@@ -516,6 +697,15 @@ var EventShipper = class {
516
697
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
517
698
  if (!this.enabled) return;
518
699
  if (!eventId || !eventId.trim()) return;
700
+ const capturedSticky = this.sticky.get(eventId);
701
+ const eventAlreadyKnown = capturedSticky !== void 0 || this.buffers.has(eventId);
702
+ const destination = currentReplayTraceDestination();
703
+ const cloudReplay = destination !== void 0 && destination.kind !== "workshop";
704
+ if ((capturedSticky == null ? void 0 : capturedSticky.replayOnly) === true || !eventAlreadyKnown && cloudReplay) {
705
+ if (patch.isPending === false) this.sticky.delete(eventId);
706
+ else this.sticky.set(eventId, { ...capturedSticky, replayOnly: true });
707
+ return;
708
+ }
519
709
  const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
520
710
  if (typeof patch.input === "string" && patch.input.length > maxChars) {
521
711
  patch = { ...patch, input: capText(patch.input, maxChars) };
@@ -574,6 +764,9 @@ var EventShipper = class {
574
764
  try {
575
765
  for (const t of this.timers.values()) clearTimeout(t);
576
766
  this.timers.clear();
767
+ for (const [eventId, sticky] of this.sticky) {
768
+ if (sticky.replayOnly) this.sticky.delete(eventId);
769
+ }
577
770
  const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
578
771
  if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
579
772
  } finally {
@@ -583,6 +776,8 @@ var EventShipper = class {
583
776
  async trackSignal(signal) {
584
777
  var _a, _b;
585
778
  if (!this.enabled) return;
779
+ const destination = currentReplayTraceDestination();
780
+ if (destination && destination.kind !== "workshop") return;
586
781
  const body = [
587
782
  {
588
783
  event_id: signal.eventId,
@@ -617,6 +812,8 @@ var EventShipper = class {
617
812
  }
618
813
  async identify(users) {
619
814
  if (!this.enabled) return;
815
+ const destination = currentReplayTraceDestination();
816
+ if (destination && destination.kind !== "workshop") return;
620
817
  const list = Array.isArray(users) ? users : [users];
621
818
  const body = list.filter((user) => {
622
819
  if (!(user == null ? void 0 : user.userId) || !user.userId.trim()) {
@@ -669,8 +866,9 @@ var EventShipper = class {
669
866
  this.buffers.delete(eventId);
670
867
  if (!accumulated) return;
671
868
  const sticky = (_a = this.sticky.get(eventId)) != null ? _a : {};
672
- const eventName = (_c = (_b = accumulated.eventName) != null ? _b : sticky.eventName) != null ? _c : this.defaultEventName;
673
- const userId = (_d = accumulated.userId) != null ? _d : sticky.userId;
869
+ const isPending = (_c = (_b = accumulated.isPending) != null ? _b : sticky.isPending) != null ? _c : true;
870
+ const eventName = (_e = (_d = accumulated.eventName) != null ? _d : sticky.eventName) != null ? _e : this.defaultEventName;
871
+ const userId = (_f = accumulated.userId) != null ? _f : sticky.userId;
674
872
  if (!userId) {
675
873
  if (this.debug) {
676
874
  console.warn(`${this.prefix} skipping track_partial for ${eventId}: missing userId`);
@@ -678,9 +876,13 @@ var EventShipper = class {
678
876
  this.sticky.delete(eventId);
679
877
  return;
680
878
  }
681
- const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
682
- const convoId = (_f = accumulated.convoId) != null ? _f : sticky.convoId;
683
- const isPending = (_h = (_g = accumulated.isPending) != null ? _g : sticky.isPending) != null ? _h : true;
879
+ const { wizardSession, ...restProperties } = (_g = accumulated.properties) != null ? _g : {};
880
+ let telemetryProperties = {};
881
+ try {
882
+ telemetryProperties = eventTelemetryProperties(accumulated);
883
+ } catch (e) {
884
+ }
885
+ const convoId = (_h = accumulated.convoId) != null ? _h : sticky.convoId;
684
886
  const payload = {
685
887
  event_id: eventId,
686
888
  user_id: userId,
@@ -688,12 +890,13 @@ var EventShipper = class {
688
890
  timestamp: (_i = accumulated.timestamp) != null ? _i : (/* @__PURE__ */ new Date()).toISOString(),
689
891
  ai_data: {
690
892
  input: accumulated.input,
691
- output: accumulated.output,
893
+ output: accumulated.error !== void 0 && accumulated.error !== null ? void 0 : accumulated.output,
692
894
  model: accumulated.model,
693
895
  convo_id: convoId
694
896
  },
695
897
  properties: {
696
898
  ...restProperties,
899
+ ...telemetryProperties,
697
900
  ...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
698
901
  $context: this.context
699
902
  },
@@ -721,29 +924,30 @@ var EventShipper = class {
721
924
  endpoint: url
722
925
  });
723
926
  }
724
- if (this.localDebuggerUrl) {
725
- mirrorPartialEventToLocalDebugger(payload, {
726
- baseUrl: this.localDebuggerUrl,
727
- writeKey: this.writeKey,
728
- debug: this.debug,
729
- sdkName: this.sdkName
730
- });
731
- }
927
+ const mirror = this.localDebuggerUrl ? mirrorPartialEventToLocalDebugger(payload, {
928
+ baseUrl: this.localDebuggerUrl,
929
+ writeKey: this.writeKey,
930
+ debug: this.debug,
931
+ sdkName: this.sdkName
932
+ }) : void 0;
732
933
  if (!this.writeKey) {
733
934
  if (!isPending) this.sticky.delete(eventId);
935
+ if (mirror) await mirror;
734
936
  return;
735
937
  }
736
938
  const opts = this.requestOpts();
737
939
  if (!opts) {
738
940
  this.warnShutdownDrop(`track_partial ${eventId}`);
739
941
  if (!isPending) this.sticky.delete(eventId);
942
+ if (mirror) await mirror;
740
943
  return;
741
944
  }
742
945
  const p = postJson(url, payload, this.requestHeaders(), opts);
743
- this.inFlight.add(p);
946
+ const combined = mirror ? Promise.all([mirror, p]).then(() => void 0) : p;
947
+ this.inFlight.add(combined);
744
948
  try {
745
949
  try {
746
- await p;
950
+ await combined;
747
951
  if (this.debug) {
748
952
  console.log(`${this.prefix} sent track_partial ${eventId} (${eventName})`);
749
953
  }
@@ -755,13 +959,156 @@ var EventShipper = class {
755
959
  );
756
960
  }
757
961
  } finally {
758
- this.inFlight.delete(p);
962
+ this.inFlight.delete(combined);
759
963
  }
760
964
  if (!isPending) {
761
965
  this.sticky.delete(eventId);
762
966
  }
763
967
  }
764
968
  };
969
+ var MODEL_USAGE_ATTRIBUTES = {
970
+ usageSource: "raindrop.usage.source",
971
+ providerName: "gen_ai.provider.name",
972
+ requestModel: "gen_ai.request.model",
973
+ responseModel: "gen_ai.response.model",
974
+ inputTokens: "gen_ai.usage.input_tokens",
975
+ outputTokens: "gen_ai.usage.output_tokens",
976
+ reasoningTokens: "gen_ai.usage.reasoning_tokens",
977
+ cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
978
+ cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
979
+ providerReportedCost: "gen_ai.usage.provider_reported_cost",
980
+ nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
981
+ nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
982
+ totalOutputTokens: "raindrop.usage.output_tokens.total",
983
+ reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
984
+ cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
985
+ cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
986
+ };
987
+ var MODEL_PROVIDER_NAMES = [
988
+ ["google.vertex.anthropic", "google-vertex"],
989
+ ["google.vertex_ai", "google-vertex"],
990
+ ["google.vertex", "google-vertex"],
991
+ ["gcp.vertex_ai", "google-vertex"],
992
+ ["gcp.gemini", "google"],
993
+ ["google_vertexai", "google-vertex"],
994
+ ["vertex.anthropic", "google-vertex"],
995
+ ["amazon-bedrock", "amazon-bedrock"],
996
+ ["aws.bedrock", "amazon-bedrock"],
997
+ ["bedrock", "amazon-bedrock"],
998
+ ["anthropic", "anthropic"],
999
+ ["openai", "openai"],
1000
+ ["google", "google"],
1001
+ ["az.ai.openai", "azure"],
1002
+ ["azure", "azure"],
1003
+ ["openrouter", "openrouter"]
1004
+ ];
1005
+ function canonicalModelProvider(provider) {
1006
+ var _a;
1007
+ const normalizedProvider = provider.trim().toLowerCase();
1008
+ if (normalizedProvider === "gateway") return "vercel";
1009
+ const match = MODEL_PROVIDER_NAMES.find(
1010
+ ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
1011
+ );
1012
+ return (_a = match == null ? void 0 : match[1]) != null ? _a : normalizedProvider;
1013
+ }
1014
+ var STRING_ALIASES = {
1015
+ [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
1016
+ };
1017
+ var NUMBER_ALIASES = {
1018
+ [MODEL_USAGE_ATTRIBUTES.inputTokens]: [
1019
+ "gen_ai.usage.prompt_tokens",
1020
+ "ai.usage.prompt_tokens",
1021
+ "ai.usage.promptTokens",
1022
+ "ai.usage.input_tokens",
1023
+ "ai.usage.inputTokens"
1024
+ ],
1025
+ [MODEL_USAGE_ATTRIBUTES.outputTokens]: [
1026
+ "gen_ai.usage.completion_tokens",
1027
+ "ai.usage.completion_tokens",
1028
+ "ai.usage.completionTokens",
1029
+ "ai.usage.output_tokens",
1030
+ "ai.usage.outputTokens"
1031
+ ],
1032
+ [MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
1033
+ "gen_ai.usage.reasoning.output_tokens",
1034
+ "ai.usage.reasoningTokens",
1035
+ "ai.usage.thoughts_tokens"
1036
+ ],
1037
+ [MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
1038
+ "gen_ai.usage.cache_read_tokens",
1039
+ "gen_ai.usage.cache_read.input_tokens",
1040
+ "ai.usage.cachedInputTokens",
1041
+ "ai.usage.cached_tokens",
1042
+ "ai.usage.cache_read_tokens",
1043
+ "ai.usage.cache_read_input_tokens",
1044
+ "ai.usage.cacheReadInputTokens"
1045
+ ],
1046
+ [MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
1047
+ "gen_ai.usage.cache_creation_input_tokens",
1048
+ "gen_ai.usage.cache_creation.input_tokens",
1049
+ "ai.usage.cacheWriteInputTokens",
1050
+ "ai.usage.cache_creation_input_tokens",
1051
+ "ai.usage.cacheCreationInputTokens"
1052
+ ]
1053
+ };
1054
+ function hasAttribute(attributes, key) {
1055
+ return attributes.some((attribute) => attribute.key === key);
1056
+ }
1057
+ function findStringAttribute(attributes, keys) {
1058
+ return attributes.find(
1059
+ (attribute) => keys.includes(attribute.key) && typeof attribute.value.stringValue === "string" && attribute.value.stringValue.length > 0
1060
+ );
1061
+ }
1062
+ function findNumberAttribute(attributes, keys) {
1063
+ return attributes.find((attribute) => {
1064
+ if (!keys.includes(attribute.key)) return false;
1065
+ if (typeof attribute.value.doubleValue === "number" && Number.isSafeInteger(attribute.value.doubleValue) && attribute.value.doubleValue >= 0) {
1066
+ return true;
1067
+ }
1068
+ const intValue = attribute.value.intValue;
1069
+ return integerValue(intValue) !== void 0;
1070
+ });
1071
+ }
1072
+ function integerValue(value) {
1073
+ if (typeof value === "number") {
1074
+ return Number.isSafeInteger(value) && value >= 0 ? value : void 0;
1075
+ }
1076
+ if (typeof value !== "string") return void 0;
1077
+ const parsed = Number(value);
1078
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
1079
+ }
1080
+ function appendAlias(attributes, target, source) {
1081
+ if (!source || hasAttribute(attributes, target)) return attributes;
1082
+ return [...attributes, { key: target, value: { ...source.value } }];
1083
+ }
1084
+ function appendProviderAlias(attributes) {
1085
+ if (hasAttribute(attributes, MODEL_USAGE_ATTRIBUTES.providerName)) return attributes;
1086
+ const source = findStringAttribute(attributes, ["gen_ai.system", "ai.model.provider"]);
1087
+ if (!(source == null ? void 0 : source.value.stringValue)) return attributes;
1088
+ const attribute = attrString(
1089
+ MODEL_USAGE_ATTRIBUTES.providerName,
1090
+ canonicalModelProvider(source.value.stringValue)
1091
+ );
1092
+ return attribute ? [...attributes, attribute] : attributes;
1093
+ }
1094
+ function normalizeModelUsageSpan(span) {
1095
+ const original = span.attributes;
1096
+ if (!original || original.length === 0) return span;
1097
+ const responseModel = findStringAttribute(original, [
1098
+ MODEL_USAGE_ATTRIBUTES.responseModel,
1099
+ "ai.response.model"
1100
+ ]);
1101
+ if (!responseModel) return span;
1102
+ let attributes = appendAlias(original, MODEL_USAGE_ATTRIBUTES.responseModel, responseModel);
1103
+ attributes = appendProviderAlias(attributes);
1104
+ for (const [target, aliases] of Object.entries(STRING_ALIASES)) {
1105
+ attributes = appendAlias(attributes, target, findStringAttribute(attributes, aliases));
1106
+ }
1107
+ for (const [target, aliases] of Object.entries(NUMBER_ALIASES)) {
1108
+ attributes = appendAlias(attributes, target, findNumberAttribute(attributes, aliases));
1109
+ }
1110
+ return attributes === original ? span : { ...span, attributes };
1111
+ }
765
1112
  var DEFAULT_SECRET_KEY_NAMES = [
766
1113
  "apikey",
767
1114
  "apisecret",
@@ -851,6 +1198,13 @@ function redactJsonAttributeValue(key, value) {
851
1198
  if (scrubbedJson === json) return void 0;
852
1199
  return { stringValue: scrubbedJson };
853
1200
  }
1201
+ function identityAttrs(args) {
1202
+ return [
1203
+ attrString("ai.telemetry.metadata.raindrop.ai.userId", args.userId),
1204
+ attrString("ai.telemetry.metadata.raindrop.convoId", args.convoId),
1205
+ attrString("ai.telemetry.metadata.raindrop.eventName", args.eventName)
1206
+ ];
1207
+ }
854
1208
  function applyOtelSpanAttributeLimit(limit) {
855
1209
  var _a, _b;
856
1210
  try {
@@ -946,7 +1300,10 @@ var TraceShipper = class {
946
1300
  try {
947
1301
  const result = this.transformSpanHook(current);
948
1302
  if (result === null) return null;
949
- if (result !== void 0) current = result;
1303
+ if (result !== void 0) {
1304
+ copyReplayTraceDestination(current, result);
1305
+ current = result;
1306
+ }
950
1307
  } catch (err) {
951
1308
  if (this.debug) {
952
1309
  const msg = err instanceof Error ? err.message : String(err);
@@ -955,10 +1312,20 @@ var TraceShipper = class {
955
1312
  return null;
956
1313
  }
957
1314
  }
1315
+ try {
1316
+ const normalized = normalizeModelUsageSpan(current);
1317
+ copyReplayTraceDestination(current, normalized);
1318
+ current = normalized;
1319
+ } catch (e) {
1320
+ }
958
1321
  if (!this.disableDefaultRedaction) {
959
- current = defaultTransformSpan(current);
1322
+ const redacted = defaultTransformSpan(current);
1323
+ copyReplayTraceDestination(current, redacted);
1324
+ current = redacted;
960
1325
  }
961
- return this.capSpanAttributes(current);
1326
+ const capped = this.capSpanAttributes(current);
1327
+ copyReplayTraceDestination(current, capped);
1328
+ return capped;
962
1329
  }
963
1330
  isDebugEnabled() {
964
1331
  return this.debug;
@@ -975,33 +1342,55 @@ var TraceShipper = class {
975
1342
  const started = (_a = args.startTimeUnixNano) != null ? _a : nowUnixNanoString();
976
1343
  const attrs = [
977
1344
  attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
978
- attrString("ai.operationId", args.operationId)
1345
+ ...identityAttrs(args),
1346
+ attrString("ai.operationId", args.operationId),
1347
+ ...Object.entries(evalScopeAttributes()).map(([key, value]) => attrString(key, value))
979
1348
  ];
980
1349
  if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
981
- const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
982
- this.mirrorToLocalDebugger(
983
- buildOtlpSpan({
984
- ids: span.ids,
985
- name: span.name,
986
- startTimeUnixNano: span.startTimeUnixNano,
987
- endTimeUnixNano: span.startTimeUnixNano,
988
- // placeholder — will be updated on endSpan
989
- attributes: span.attributes,
990
- status: { code: SpanStatusCode.UNSET }
991
- })
992
- );
1350
+ const span = {
1351
+ ids,
1352
+ name: args.name,
1353
+ startTimeUnixNano: started,
1354
+ attributes: attrs
1355
+ };
1356
+ rememberReplayTraceDestination(span);
1357
+ const openSpan = buildOtlpSpan({
1358
+ ids: span.ids,
1359
+ name: span.name,
1360
+ startTimeUnixNano: span.startTimeUnixNano,
1361
+ endTimeUnixNano: span.startTimeUnixNano,
1362
+ // placeholder — will be updated on endSpan
1363
+ attributes: span.attributes,
1364
+ status: { code: SpanStatusCode.UNSET }
1365
+ });
1366
+ copyReplayTraceDestination(span, openSpan);
1367
+ this.mirrorToLocalDebugger(openSpan);
993
1368
  return span;
994
1369
  }
995
- mirrorToLocalDebugger(span) {
996
- if (!this.localDebuggerUrl) return;
1370
+ mirrorToLocalDebugger(span, completed = false) {
1371
+ const destination = replayTraceDestinationFor(span);
1372
+ if (destination && destination.kind !== "workshop") return;
1373
+ const localDebuggerUrl = (destination == null ? void 0 : destination.kind) === "workshop" ? destination.url : this.localDebuggerUrl;
1374
+ if (!localDebuggerUrl) return;
997
1375
  const redacted = this.redactSpan(span);
998
1376
  if (redacted === null) return;
1377
+ if (completed && (destination == null ? void 0 : destination.kind) === "workshop") {
1378
+ recordReplayExportedSpan(
1379
+ destination,
1380
+ base64ToHex(redacted.spanId),
1381
+ base64ToHex(redacted.traceId)
1382
+ );
1383
+ }
999
1384
  const body = buildExportTraceServiceRequest([redacted], this.serviceName, this.serviceVersion);
1000
- mirrorTraceExportToLocalDebugger(body, {
1001
- baseUrl: this.localDebuggerUrl,
1385
+ const p = mirrorTraceExportToLocalDebugger(body, {
1386
+ baseUrl: localDebuggerUrl,
1002
1387
  debug: false,
1003
1388
  sdkName: this.sdkName
1004
1389
  });
1390
+ this.inFlight.add(p);
1391
+ void p.finally(() => {
1392
+ this.inFlight.delete(p);
1393
+ });
1005
1394
  }
1006
1395
  endSpan(span, extra) {
1007
1396
  var _a, _b;
@@ -1023,14 +1412,17 @@ var TraceShipper = class {
1023
1412
  attributes: span.attributes,
1024
1413
  status
1025
1414
  });
1415
+ copyReplayTraceDestination(span, otlp);
1026
1416
  this.enqueue(otlp);
1027
- this.mirrorToLocalDebugger(otlp);
1417
+ this.mirrorToLocalDebugger(otlp, true);
1028
1418
  }
1029
1419
  createSpan(args) {
1030
1420
  var _a;
1031
1421
  const ids = createSpanIds(args.parent);
1032
1422
  const attrs = [
1033
- attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId)
1423
+ attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
1424
+ ...identityAttrs(args),
1425
+ ...Object.entries(evalScopeAttributes()).map(([key, value]) => attrString(key, value))
1034
1426
  ];
1035
1427
  if ((_a = args.attributes) == null ? void 0 : _a.length) attrs.push(...args.attributes);
1036
1428
  const otlp = buildOtlpSpan({
@@ -1041,8 +1433,9 @@ var TraceShipper = class {
1041
1433
  attributes: attrs,
1042
1434
  status: args.status
1043
1435
  });
1436
+ rememberReplayTraceDestination(otlp);
1044
1437
  this.enqueue(otlp);
1045
- this.mirrorToLocalDebugger(otlp);
1438
+ this.mirrorToLocalDebugger(otlp, true);
1046
1439
  }
1047
1440
  enqueue(span) {
1048
1441
  if (!this.enabled) return;
@@ -1056,6 +1449,13 @@ var TraceShipper = class {
1056
1449
  }
1057
1450
  const redacted = this.redactSpan(span);
1058
1451
  if (redacted === null) return;
1452
+ const destination = replayTraceDestinationFor(redacted);
1453
+ recordReplayExportedSpan(
1454
+ destination,
1455
+ base64ToHex(redacted.spanId),
1456
+ base64ToHex(redacted.traceId)
1457
+ );
1458
+ if ((destination == null ? void 0 : destination.kind) === "workshop") return;
1059
1459
  if (this.queue.length >= this.maxQueueSize) {
1060
1460
  this.queue.shift();
1061
1461
  }
@@ -1074,47 +1474,67 @@ var TraceShipper = class {
1074
1474
  }
1075
1475
  }
1076
1476
  async flush() {
1077
- if (!this.enabled) return;
1477
+ var _a;
1478
+ if (!this.enabled) {
1479
+ await Promise.all([...this.inFlight].map((p) => p.catch(() => {
1480
+ })));
1481
+ return;
1482
+ }
1078
1483
  if (this.timer) {
1079
1484
  clearTimeout(this.timer);
1080
1485
  this.timer = void 0;
1081
1486
  }
1082
1487
  while (this.queue.length > 0) {
1083
1488
  const batch = this.queue.splice(0, this.maxBatchSize);
1084
- if (!this.writeKey) continue;
1085
- const opts = this.requestOpts();
1086
- if (!opts) {
1087
- rateLimitedLog(
1088
- `${this.prefix}.shutdown_deadline`,
1089
- () => console.warn(
1090
- `${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
1091
- )
1092
- );
1093
- continue;
1094
- }
1095
- const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
1096
- const url = `${this.baseUrl}traces`;
1097
- if (this.debug) {
1098
- console.log(`${this.prefix} sending traces batch`, {
1099
- spans: batch.length,
1100
- endpoint: url
1101
- });
1489
+ const groups = /* @__PURE__ */ new Map();
1490
+ for (const span of batch) {
1491
+ const destination = replayTraceDestinationFor(span);
1492
+ const spans = groups.get(destination);
1493
+ if (spans) spans.push(span);
1494
+ else groups.set(destination, [span]);
1102
1495
  }
1103
- const p = postJson(url, body, this.requestHeaders(), opts);
1104
- this.inFlight.add(p);
1105
- try {
1106
- try {
1107
- await p;
1108
- if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
1109
- } catch (err) {
1110
- const msg = err instanceof Error ? err.message : String(err);
1496
+ for (const [destination, spans] of groups) {
1497
+ if (!destination && !this.writeKey) continue;
1498
+ if ((destination == null ? void 0 : destination.kind) === "workshop") continue;
1499
+ const opts = this.requestOpts();
1500
+ if (!opts) {
1111
1501
  rateLimitedLog(
1112
- `${this.prefix}.send_spans_failed`,
1113
- () => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
1502
+ `${this.prefix}.shutdown_deadline`,
1503
+ () => console.warn(
1504
+ `${this.prefix} shutdown flush deadline exceeded; dropping ${spans.length} spans`
1505
+ )
1114
1506
  );
1507
+ continue;
1508
+ }
1509
+ const body = buildExportTraceServiceRequest(spans, this.serviceName, this.serviceVersion);
1510
+ const url = destination ? replayTraceUrl(destination.url) : `${this.baseUrl}traces`;
1511
+ const headers = destination ? {
1512
+ Authorization: `Bearer ${destination.apiKey}`,
1513
+ "X-Raindrop-Replay-Id": destination.replayId,
1514
+ "X-Raindrop-Project-Id": (_a = destination.projectId) != null ? _a : "default"
1515
+ } : this.requestHeaders();
1516
+ if (this.debug) {
1517
+ console.log(`${this.prefix} sending traces batch`, {
1518
+ spans: spans.length,
1519
+ endpoint: url
1520
+ });
1521
+ }
1522
+ const p = postJson(url, body, headers, opts);
1523
+ this.inFlight.add(p);
1524
+ try {
1525
+ try {
1526
+ await p;
1527
+ if (this.debug) console.log(`${this.prefix} sent ${spans.length} spans`);
1528
+ } catch (err) {
1529
+ const msg = err instanceof Error ? err.message : String(err);
1530
+ rateLimitedLog(
1531
+ `${this.prefix}.send_spans_failed`,
1532
+ () => console.warn(`${this.prefix} failed to send ${spans.length} spans: ${msg}`)
1533
+ );
1534
+ }
1535
+ } finally {
1536
+ this.inFlight.delete(p);
1115
1537
  }
1116
- } finally {
1117
- this.inFlight.delete(p);
1118
1538
  }
1119
1539
  }
1120
1540
  }
@@ -1157,7 +1577,9 @@ var TraceShipper = class {
1157
1577
  if (!settled) {
1158
1578
  rateLimitedLog(
1159
1579
  `${this.prefix}.shutdown_deadline`,
1160
- () => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
1580
+ () => console.warn(
1581
+ `${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`
1582
+ )
1161
1583
  );
1162
1584
  }
1163
1585
  } finally {
@@ -1309,6 +1731,10 @@ var RaindropDeepAgentsHandler = class extends import_base.BaseCallbackHandler {
1309
1731
  });
1310
1732
  }
1311
1733
  }
1734
+ rootErrorPatch(runId, error) {
1735
+ const rootId = this.rootRunIds.get(runId) ?? runId;
1736
+ return rootId === runId ? { error } : void 0;
1737
+ }
1312
1738
  // ---------------------------------------------------------------------------
1313
1739
  // LLM events
1314
1740
  // ---------------------------------------------------------------------------
@@ -1404,8 +1830,6 @@ var RaindropDeepAgentsHandler = class extends import_base.BaseCallbackHandler {
1404
1830
  ]
1405
1831
  });
1406
1832
  const properties = {};
1407
- if (promptTokens != null) properties["ai.usage.prompt_tokens"] = promptTokens;
1408
- if (completionTokens != null) properties["ai.usage.completion_tokens"] = completionTokens;
1409
1833
  if (cachedTokens != null) properties["ai.usage.cached_tokens"] = cachedTokens;
1410
1834
  if (reasoningTokens != null) properties["ai.usage.thoughts_tokens"] = reasoningTokens;
1411
1835
  if (finishReason != null) properties["ai.finish_reason"] = finishReason;
@@ -1413,6 +1837,7 @@ var RaindropDeepAgentsHandler = class extends import_base.BaseCallbackHandler {
1413
1837
  await this.finalizeEventIfRoot(runId, {
1414
1838
  output: cappedOutput,
1415
1839
  model,
1840
+ usage: { promptTokens, completionTokens },
1416
1841
  properties: Object.keys(properties).length > 0 ? properties : void 0
1417
1842
  });
1418
1843
  } catch {
@@ -1430,14 +1855,7 @@ var RaindropDeepAgentsHandler = class extends import_base.BaseCallbackHandler {
1430
1855
  if (span) {
1431
1856
  this.traceShipper.endSpan(span, { error: err });
1432
1857
  }
1433
- const errorInfo = extractErrorInfo(err);
1434
- await this.finalizeEventIfRoot(runId, {
1435
- output: `[Error] ${errorInfo.type}: ${errorInfo.message}`,
1436
- properties: {
1437
- "error.type": errorInfo.type,
1438
- "error.message": errorInfo.message
1439
- }
1440
- });
1858
+ await this.finalizeEventIfRoot(runId, this.rootErrorPatch(runId, err));
1441
1859
  } catch {
1442
1860
  } finally {
1443
1861
  this.cleanup(runId);
@@ -1499,14 +1917,7 @@ var RaindropDeepAgentsHandler = class extends import_base.BaseCallbackHandler {
1499
1917
  const span = this.spans.get(runId);
1500
1918
  if (span) this.traceShipper.endSpan(span, { error: err });
1501
1919
  }
1502
- const errorInfo = extractErrorInfo(err);
1503
- await this.finalizeEventIfRoot(runId, {
1504
- output: `[Error] ${errorInfo.type}: ${errorInfo.message}`,
1505
- properties: {
1506
- "error.type": errorInfo.type,
1507
- "error.message": errorInfo.message
1508
- }
1509
- });
1920
+ await this.finalizeEventIfRoot(runId, this.rootErrorPatch(runId, err));
1510
1921
  } catch {
1511
1922
  } finally {
1512
1923
  this.cleanup(runId);
@@ -1561,14 +1972,7 @@ var RaindropDeepAgentsHandler = class extends import_base.BaseCallbackHandler {
1561
1972
  try {
1562
1973
  const span = this.spans.get(runId);
1563
1974
  if (span) this.traceShipper.endSpan(span, { error: err });
1564
- const errorInfo = extractErrorInfo(err);
1565
- await this.finalizeEventIfRoot(runId, {
1566
- output: `[Error] ${errorInfo.type}: ${errorInfo.message}`,
1567
- properties: {
1568
- "error.type": errorInfo.type,
1569
- "error.message": errorInfo.message
1570
- }
1571
- });
1975
+ await this.finalizeEventIfRoot(runId, this.rootErrorPatch(runId, err));
1572
1976
  } catch {
1573
1977
  } finally {
1574
1978
  this.cleanup(runId);
@@ -1733,18 +2137,6 @@ function asNumber(val) {
1733
2137
  const n = typeof val === "number" ? val : Number(val);
1734
2138
  return Number.isFinite(n) ? n : void 0;
1735
2139
  }
1736
- function extractErrorInfo(err) {
1737
- if (err instanceof Error) {
1738
- return { type: err.name || err.constructor.name, message: err.message };
1739
- }
1740
- if (typeof err === "object" && err !== null) {
1741
- const e = err;
1742
- const type = e.name ?? e.constructor?.name ?? "Error";
1743
- const message = e.message ?? safeStringify(err);
1744
- return { type, message };
1745
- }
1746
- return { type: "Error", message: String(err) };
1747
- }
1748
2140
 
1749
2141
  // src/version.ts
1750
2142
  var libraryName = "@raindrop-ai/deep-agents";