@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.mjs CHANGED
@@ -1,7 +1,69 @@
1
1
  // src/callback-handler.ts
2
2
  import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
3
3
 
4
- // ../core/dist/chunk-267IXHO5.js
4
+ // ../core/dist/chunk-BYBMRLZM.js
5
+ var EVAL_CORRELATION_ID_ATTRIBUTE = "raindrop.eval_correlation_id";
6
+ var PROBE_SCOPE = { correlationId: "" };
7
+ var EvalScopeStorage = class {
8
+ constructor() {
9
+ this._als = null;
10
+ this._probed = false;
11
+ }
12
+ maybeAdoptAsyncLocalStorage() {
13
+ if (this._probed) return;
14
+ this._probed = true;
15
+ try {
16
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
17
+ if (!Ctor) return;
18
+ const als = new Ctor();
19
+ als.run(PROBE_SCOPE, () => {
20
+ });
21
+ this._als = als;
22
+ } catch (e) {
23
+ this._als = null;
24
+ }
25
+ }
26
+ run(scope, callback) {
27
+ this.maybeAdoptAsyncLocalStorage();
28
+ if (this._als) return this._als.run(scope, callback);
29
+ const previous = this._syncScope;
30
+ this._syncScope = scope;
31
+ try {
32
+ return callback();
33
+ } finally {
34
+ this._syncScope = previous;
35
+ }
36
+ }
37
+ current() {
38
+ this.maybeAdoptAsyncLocalStorage();
39
+ if (this._als) return this._als.getStore();
40
+ return this._syncScope;
41
+ }
42
+ };
43
+ var EVAL_SCOPE_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.evalScopeStorage");
44
+ function evalScopeStorage() {
45
+ const holder = globalThis;
46
+ let storage = holder[EVAL_SCOPE_STORAGE_KEY];
47
+ if (!storage) {
48
+ storage = new EvalScopeStorage();
49
+ holder[EVAL_SCOPE_STORAGE_KEY] = storage;
50
+ }
51
+ return storage;
52
+ }
53
+ function currentEvalScope() {
54
+ try {
55
+ return evalScopeStorage().current();
56
+ } catch (e) {
57
+ return void 0;
58
+ }
59
+ }
60
+ function evalScopeAttributes() {
61
+ const scope = currentEvalScope();
62
+ if (!scope) return {};
63
+ return { [EVAL_CORRELATION_ID_ATTRIBUTE]: scope.correlationId };
64
+ }
65
+
66
+ // ../core/dist/chunk-T53LQAH7.js
5
67
  function getCrypto() {
6
68
  const c = globalThis.crypto;
7
69
  return c;
@@ -56,6 +118,39 @@ function base64Encode(bytes) {
56
118
  }
57
119
  return out;
58
120
  }
121
+ function base64ToHex(value) {
122
+ if (!value) return "";
123
+ const maybeBuffer = globalThis.Buffer;
124
+ if (maybeBuffer) {
125
+ return maybeBuffer.from(value, "base64").toString("hex");
126
+ }
127
+ const atobFn = globalThis.atob;
128
+ if (typeof atobFn !== "function") return "";
129
+ try {
130
+ const binary = atobFn(value);
131
+ let hex = "";
132
+ for (let i = 0; i < binary.length; i++) {
133
+ hex += (binary.charCodeAt(i) & 255).toString(16).padStart(2, "0");
134
+ }
135
+ return hex;
136
+ } catch (e) {
137
+ return "";
138
+ }
139
+ }
140
+ var HANDOFF_ATTRIBUTE_SUFFIXES = {
141
+ mode: "raindrop.handoff.mode",
142
+ childEventId: "raindrop.handoff.childEventId",
143
+ parentEventId: "raindrop.handoff.parentEventId",
144
+ parentSpanId: "raindrop.handoff.parentSpanId",
145
+ name: "raindrop.handoff.name",
146
+ terminal: "raindrop.handoff.terminal",
147
+ agentRole: "raindrop.agent.role"
148
+ };
149
+ var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
150
+ var TRACEPARENT_HEADER = "traceparent";
151
+ var BAGGAGE_HEADER = "baggage";
152
+ var HANDOFF_HEADER = "x-raindrop-handoff";
153
+ 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.`;
59
154
  function runWithTracingSuppressed(fn) {
60
155
  const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
61
156
  if (typeof hook !== "function") return fn();
@@ -183,6 +278,7 @@ async function postJson(url, body, headers, opts) {
183
278
  const resp = await runWithTracingSuppressed(
184
279
  () => fetch(url, {
185
280
  method: "POST",
281
+ redirect: headers["X-Raindrop-Replay-Id"] !== void 0 ? "error" : "follow",
186
282
  headers: {
187
283
  "Content-Type": "application/json",
188
284
  ...headers
@@ -284,6 +380,55 @@ function buildExportTraceServiceRequest(spans, serviceName = "raindrop.core", se
284
380
  ]
285
381
  };
286
382
  }
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
+ }
287
432
  var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
288
433
  var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
289
434
  var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
@@ -346,26 +491,29 @@ function resolveLocalDebuggerBaseUrl(baseUrl) {
346
491
  if (shouldAutoEnableLocalWorkshop()) return DEFAULT_LOCAL_WORKSHOP_URL;
347
492
  return null;
348
493
  }
494
+ var LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS = 2e3;
349
495
  function mirrorTraceExportToLocalDebugger(body, options = {}) {
350
496
  var _a;
351
497
  const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
352
- if (!baseUrl) return;
353
- void postJson(`${baseUrl}traces`, body, {}, {
498
+ if (!baseUrl) return Promise.resolve();
499
+ return postJson(`${baseUrl}traces`, body, {}, {
354
500
  maxAttempts: 1,
355
501
  debug: (_a = options.debug) != null ? _a : false,
356
- sdkName: options.sdkName
502
+ sdkName: options.sdkName,
503
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
357
504
  }).catch(() => {
358
505
  });
359
506
  }
360
507
  function mirrorPartialEventToLocalDebugger(event, options = {}) {
361
508
  var _a;
362
509
  const baseUrl = resolveLocalDebuggerBaseUrl(options.baseUrl);
363
- if (!baseUrl) return;
510
+ if (!baseUrl) return Promise.resolve();
364
511
  const headers = options.writeKey ? { Authorization: `Bearer ${options.writeKey}` } : {};
365
- void postJson(`${baseUrl}events/track_partial`, event, headers, {
512
+ return postJson(`${baseUrl}events/track_partial`, event, headers, {
366
513
  maxAttempts: 1,
367
514
  debug: (_a = options.debug) != null ? _a : false,
368
- sdkName: options.sdkName
515
+ sdkName: options.sdkName,
516
+ timeoutMs: LOCAL_DEBUGGER_MIRROR_TIMEOUT_MS
369
517
  }).catch(() => {
370
518
  });
371
519
  }
@@ -391,19 +539,52 @@ function projectIdHeaders(projectId) {
391
539
  var SHUTDOWN_DEADLINE_MS = 1e4;
392
540
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
393
541
  function mergePatches(target, source) {
394
- var _a, _b, _c, _d, _e, _f;
542
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
395
543
  const out = { ...target, ...source };
396
544
  if (target.properties || source.properties) {
397
545
  out.properties = { ...(_a = target.properties) != null ? _a : {}, ...(_b = source.properties) != null ? _b : {} };
398
546
  }
547
+ if (target.usage || source.usage) {
548
+ out.usage = { ...(_c = target.usage) != null ? _c : {} };
549
+ if (((_d = source.usage) == null ? void 0 : _d.promptTokens) !== void 0) {
550
+ out.usage.promptTokens = source.usage.promptTokens;
551
+ }
552
+ if (((_e = source.usage) == null ? void 0 : _e.completionTokens) !== void 0) {
553
+ out.usage.completionTokens = source.usage.completionTokens;
554
+ }
555
+ }
556
+ if (source.error === void 0) {
557
+ out.error = target.error;
558
+ }
399
559
  if (target.featureFlags || source.featureFlags) {
400
- out.featureFlags = { ...(_c = target.featureFlags) != null ? _c : {}, ...(_d = source.featureFlags) != null ? _d : {} };
560
+ out.featureFlags = { ...(_f = target.featureFlags) != null ? _f : {}, ...(_g = source.featureFlags) != null ? _g : {} };
401
561
  }
402
562
  if (target.attachments || source.attachments) {
403
- out.attachments = [...(_e = target.attachments) != null ? _e : [], ...(_f = source.attachments) != null ? _f : []];
563
+ out.attachments = [...(_h = target.attachments) != null ? _h : [], ...(_i = source.attachments) != null ? _i : []];
404
564
  }
405
565
  return out;
406
566
  }
567
+ function isErrorLike(value) {
568
+ return value !== null && typeof value === "object";
569
+ }
570
+ function eventTelemetryProperties(patch) {
571
+ var _a, _b;
572
+ const properties = {};
573
+ if (((_a = patch.usage) == null ? void 0 : _a.promptTokens) !== void 0 && Number.isFinite(patch.usage.promptTokens)) {
574
+ properties["ai.usage.prompt_tokens"] = patch.usage.promptTokens;
575
+ }
576
+ if (((_b = patch.usage) == null ? void 0 : _b.completionTokens) !== void 0 && Number.isFinite(patch.usage.completionTokens)) {
577
+ properties["ai.usage.completion_tokens"] = patch.usage.completionTokens;
578
+ }
579
+ if (patch.error !== void 0 && patch.error !== null) {
580
+ const constructorName = patch.error instanceof Error && patch.error.constructor !== Error ? patch.error.constructor.name : void 0;
581
+ 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";
582
+ const errorMessage = patch.error instanceof Error ? patch.error.message : isErrorLike(patch.error) && typeof patch.error.message === "string" ? patch.error.message : String(patch.error);
583
+ properties["error.type"] = errorType;
584
+ properties["error.message"] = errorMessage;
585
+ }
586
+ return properties;
587
+ }
407
588
  var EventShipper = class {
408
589
  constructor(opts) {
409
590
  this.buffers = /* @__PURE__ */ new Map();
@@ -489,6 +670,15 @@ var EventShipper = class {
489
670
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
490
671
  if (!this.enabled) return;
491
672
  if (!eventId || !eventId.trim()) return;
673
+ const capturedSticky = this.sticky.get(eventId);
674
+ const eventAlreadyKnown = capturedSticky !== void 0 || this.buffers.has(eventId);
675
+ const destination = currentReplayTraceDestination();
676
+ const cloudReplay = destination !== void 0 && destination.kind !== "workshop";
677
+ if ((capturedSticky == null ? void 0 : capturedSticky.replayOnly) === true || !eventAlreadyKnown && cloudReplay) {
678
+ if (patch.isPending === false) this.sticky.delete(eventId);
679
+ else this.sticky.set(eventId, { ...capturedSticky, replayOnly: true });
680
+ return;
681
+ }
492
682
  const maxChars = resolveMaxTextFieldChars(this.maxTextFieldCharsOpt);
493
683
  if (typeof patch.input === "string" && patch.input.length > maxChars) {
494
684
  patch = { ...patch, input: capText(patch.input, maxChars) };
@@ -547,6 +737,9 @@ var EventShipper = class {
547
737
  try {
548
738
  for (const t of this.timers.values()) clearTimeout(t);
549
739
  this.timers.clear();
740
+ for (const [eventId, sticky] of this.sticky) {
741
+ if (sticky.replayOnly) this.sticky.delete(eventId);
742
+ }
550
743
  const settled = await raceWithTimeout(this.flush(), SHUTDOWN_DEADLINE_MS);
551
744
  if (!settled) this.warnShutdownDrop("in-flight request(s) at shutdown");
552
745
  } finally {
@@ -556,6 +749,8 @@ var EventShipper = class {
556
749
  async trackSignal(signal) {
557
750
  var _a, _b;
558
751
  if (!this.enabled) return;
752
+ const destination = currentReplayTraceDestination();
753
+ if (destination && destination.kind !== "workshop") return;
559
754
  const body = [
560
755
  {
561
756
  event_id: signal.eventId,
@@ -590,6 +785,8 @@ var EventShipper = class {
590
785
  }
591
786
  async identify(users) {
592
787
  if (!this.enabled) return;
788
+ const destination = currentReplayTraceDestination();
789
+ if (destination && destination.kind !== "workshop") return;
593
790
  const list = Array.isArray(users) ? users : [users];
594
791
  const body = list.filter((user) => {
595
792
  if (!(user == null ? void 0 : user.userId) || !user.userId.trim()) {
@@ -642,8 +839,9 @@ var EventShipper = class {
642
839
  this.buffers.delete(eventId);
643
840
  if (!accumulated) return;
644
841
  const sticky = (_a = this.sticky.get(eventId)) != null ? _a : {};
645
- const eventName = (_c = (_b = accumulated.eventName) != null ? _b : sticky.eventName) != null ? _c : this.defaultEventName;
646
- const userId = (_d = accumulated.userId) != null ? _d : sticky.userId;
842
+ const isPending = (_c = (_b = accumulated.isPending) != null ? _b : sticky.isPending) != null ? _c : true;
843
+ const eventName = (_e = (_d = accumulated.eventName) != null ? _d : sticky.eventName) != null ? _e : this.defaultEventName;
844
+ const userId = (_f = accumulated.userId) != null ? _f : sticky.userId;
647
845
  if (!userId) {
648
846
  if (this.debug) {
649
847
  console.warn(`${this.prefix} skipping track_partial for ${eventId}: missing userId`);
@@ -651,9 +849,13 @@ var EventShipper = class {
651
849
  this.sticky.delete(eventId);
652
850
  return;
653
851
  }
654
- const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
655
- const convoId = (_f = accumulated.convoId) != null ? _f : sticky.convoId;
656
- const isPending = (_h = (_g = accumulated.isPending) != null ? _g : sticky.isPending) != null ? _h : true;
852
+ const { wizardSession, ...restProperties } = (_g = accumulated.properties) != null ? _g : {};
853
+ let telemetryProperties = {};
854
+ try {
855
+ telemetryProperties = eventTelemetryProperties(accumulated);
856
+ } catch (e) {
857
+ }
858
+ const convoId = (_h = accumulated.convoId) != null ? _h : sticky.convoId;
657
859
  const payload = {
658
860
  event_id: eventId,
659
861
  user_id: userId,
@@ -661,12 +863,13 @@ var EventShipper = class {
661
863
  timestamp: (_i = accumulated.timestamp) != null ? _i : (/* @__PURE__ */ new Date()).toISOString(),
662
864
  ai_data: {
663
865
  input: accumulated.input,
664
- output: accumulated.output,
866
+ output: accumulated.error !== void 0 && accumulated.error !== null ? void 0 : accumulated.output,
665
867
  model: accumulated.model,
666
868
  convo_id: convoId
667
869
  },
668
870
  properties: {
669
871
  ...restProperties,
872
+ ...telemetryProperties,
670
873
  ...wizardSession ? { "raindrop.wizardSession": wizardSession } : {},
671
874
  $context: this.context
672
875
  },
@@ -694,29 +897,30 @@ var EventShipper = class {
694
897
  endpoint: url
695
898
  });
696
899
  }
697
- if (this.localDebuggerUrl) {
698
- mirrorPartialEventToLocalDebugger(payload, {
699
- baseUrl: this.localDebuggerUrl,
700
- writeKey: this.writeKey,
701
- debug: this.debug,
702
- sdkName: this.sdkName
703
- });
704
- }
900
+ const mirror = this.localDebuggerUrl ? mirrorPartialEventToLocalDebugger(payload, {
901
+ baseUrl: this.localDebuggerUrl,
902
+ writeKey: this.writeKey,
903
+ debug: this.debug,
904
+ sdkName: this.sdkName
905
+ }) : void 0;
705
906
  if (!this.writeKey) {
706
907
  if (!isPending) this.sticky.delete(eventId);
908
+ if (mirror) await mirror;
707
909
  return;
708
910
  }
709
911
  const opts = this.requestOpts();
710
912
  if (!opts) {
711
913
  this.warnShutdownDrop(`track_partial ${eventId}`);
712
914
  if (!isPending) this.sticky.delete(eventId);
915
+ if (mirror) await mirror;
713
916
  return;
714
917
  }
715
918
  const p = postJson(url, payload, this.requestHeaders(), opts);
716
- this.inFlight.add(p);
919
+ const combined = mirror ? Promise.all([mirror, p]).then(() => void 0) : p;
920
+ this.inFlight.add(combined);
717
921
  try {
718
922
  try {
719
- await p;
923
+ await combined;
720
924
  if (this.debug) {
721
925
  console.log(`${this.prefix} sent track_partial ${eventId} (${eventName})`);
722
926
  }
@@ -728,13 +932,156 @@ var EventShipper = class {
728
932
  );
729
933
  }
730
934
  } finally {
731
- this.inFlight.delete(p);
935
+ this.inFlight.delete(combined);
732
936
  }
733
937
  if (!isPending) {
734
938
  this.sticky.delete(eventId);
735
939
  }
736
940
  }
737
941
  };
942
+ var MODEL_USAGE_ATTRIBUTES = {
943
+ usageSource: "raindrop.usage.source",
944
+ providerName: "gen_ai.provider.name",
945
+ requestModel: "gen_ai.request.model",
946
+ responseModel: "gen_ai.response.model",
947
+ inputTokens: "gen_ai.usage.input_tokens",
948
+ outputTokens: "gen_ai.usage.output_tokens",
949
+ reasoningTokens: "gen_ai.usage.reasoning_tokens",
950
+ cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
951
+ cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
952
+ providerReportedCost: "gen_ai.usage.provider_reported_cost",
953
+ nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
954
+ nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
955
+ totalOutputTokens: "raindrop.usage.output_tokens.total",
956
+ reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
957
+ cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
958
+ cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
959
+ };
960
+ var MODEL_PROVIDER_NAMES = [
961
+ ["google.vertex.anthropic", "google-vertex"],
962
+ ["google.vertex_ai", "google-vertex"],
963
+ ["google.vertex", "google-vertex"],
964
+ ["gcp.vertex_ai", "google-vertex"],
965
+ ["gcp.gemini", "google"],
966
+ ["google_vertexai", "google-vertex"],
967
+ ["vertex.anthropic", "google-vertex"],
968
+ ["amazon-bedrock", "amazon-bedrock"],
969
+ ["aws.bedrock", "amazon-bedrock"],
970
+ ["bedrock", "amazon-bedrock"],
971
+ ["anthropic", "anthropic"],
972
+ ["openai", "openai"],
973
+ ["google", "google"],
974
+ ["az.ai.openai", "azure"],
975
+ ["azure", "azure"],
976
+ ["openrouter", "openrouter"]
977
+ ];
978
+ function canonicalModelProvider(provider) {
979
+ var _a;
980
+ const normalizedProvider = provider.trim().toLowerCase();
981
+ if (normalizedProvider === "gateway") return "vercel";
982
+ const match = MODEL_PROVIDER_NAMES.find(
983
+ ([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
984
+ );
985
+ return (_a = match == null ? void 0 : match[1]) != null ? _a : normalizedProvider;
986
+ }
987
+ var STRING_ALIASES = {
988
+ [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
989
+ };
990
+ var NUMBER_ALIASES = {
991
+ [MODEL_USAGE_ATTRIBUTES.inputTokens]: [
992
+ "gen_ai.usage.prompt_tokens",
993
+ "ai.usage.prompt_tokens",
994
+ "ai.usage.promptTokens",
995
+ "ai.usage.input_tokens",
996
+ "ai.usage.inputTokens"
997
+ ],
998
+ [MODEL_USAGE_ATTRIBUTES.outputTokens]: [
999
+ "gen_ai.usage.completion_tokens",
1000
+ "ai.usage.completion_tokens",
1001
+ "ai.usage.completionTokens",
1002
+ "ai.usage.output_tokens",
1003
+ "ai.usage.outputTokens"
1004
+ ],
1005
+ [MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
1006
+ "gen_ai.usage.reasoning.output_tokens",
1007
+ "ai.usage.reasoningTokens",
1008
+ "ai.usage.thoughts_tokens"
1009
+ ],
1010
+ [MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
1011
+ "gen_ai.usage.cache_read_tokens",
1012
+ "gen_ai.usage.cache_read.input_tokens",
1013
+ "ai.usage.cachedInputTokens",
1014
+ "ai.usage.cached_tokens",
1015
+ "ai.usage.cache_read_tokens",
1016
+ "ai.usage.cache_read_input_tokens",
1017
+ "ai.usage.cacheReadInputTokens"
1018
+ ],
1019
+ [MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
1020
+ "gen_ai.usage.cache_creation_input_tokens",
1021
+ "gen_ai.usage.cache_creation.input_tokens",
1022
+ "ai.usage.cacheWriteInputTokens",
1023
+ "ai.usage.cache_creation_input_tokens",
1024
+ "ai.usage.cacheCreationInputTokens"
1025
+ ]
1026
+ };
1027
+ function hasAttribute(attributes, key) {
1028
+ return attributes.some((attribute) => attribute.key === key);
1029
+ }
1030
+ function findStringAttribute(attributes, keys) {
1031
+ return attributes.find(
1032
+ (attribute) => keys.includes(attribute.key) && typeof attribute.value.stringValue === "string" && attribute.value.stringValue.length > 0
1033
+ );
1034
+ }
1035
+ function findNumberAttribute(attributes, keys) {
1036
+ return attributes.find((attribute) => {
1037
+ if (!keys.includes(attribute.key)) return false;
1038
+ if (typeof attribute.value.doubleValue === "number" && Number.isSafeInteger(attribute.value.doubleValue) && attribute.value.doubleValue >= 0) {
1039
+ return true;
1040
+ }
1041
+ const intValue = attribute.value.intValue;
1042
+ return integerValue(intValue) !== void 0;
1043
+ });
1044
+ }
1045
+ function integerValue(value) {
1046
+ if (typeof value === "number") {
1047
+ return Number.isSafeInteger(value) && value >= 0 ? value : void 0;
1048
+ }
1049
+ if (typeof value !== "string") return void 0;
1050
+ const parsed = Number(value);
1051
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
1052
+ }
1053
+ function appendAlias(attributes, target, source) {
1054
+ if (!source || hasAttribute(attributes, target)) return attributes;
1055
+ return [...attributes, { key: target, value: { ...source.value } }];
1056
+ }
1057
+ function appendProviderAlias(attributes) {
1058
+ if (hasAttribute(attributes, MODEL_USAGE_ATTRIBUTES.providerName)) return attributes;
1059
+ const source = findStringAttribute(attributes, ["gen_ai.system", "ai.model.provider"]);
1060
+ if (!(source == null ? void 0 : source.value.stringValue)) return attributes;
1061
+ const attribute = attrString(
1062
+ MODEL_USAGE_ATTRIBUTES.providerName,
1063
+ canonicalModelProvider(source.value.stringValue)
1064
+ );
1065
+ return attribute ? [...attributes, attribute] : attributes;
1066
+ }
1067
+ function normalizeModelUsageSpan(span) {
1068
+ const original = span.attributes;
1069
+ if (!original || original.length === 0) return span;
1070
+ const responseModel = findStringAttribute(original, [
1071
+ MODEL_USAGE_ATTRIBUTES.responseModel,
1072
+ "ai.response.model"
1073
+ ]);
1074
+ if (!responseModel) return span;
1075
+ let attributes = appendAlias(original, MODEL_USAGE_ATTRIBUTES.responseModel, responseModel);
1076
+ attributes = appendProviderAlias(attributes);
1077
+ for (const [target, aliases] of Object.entries(STRING_ALIASES)) {
1078
+ attributes = appendAlias(attributes, target, findStringAttribute(attributes, aliases));
1079
+ }
1080
+ for (const [target, aliases] of Object.entries(NUMBER_ALIASES)) {
1081
+ attributes = appendAlias(attributes, target, findNumberAttribute(attributes, aliases));
1082
+ }
1083
+ return attributes === original ? span : { ...span, attributes };
1084
+ }
738
1085
  var DEFAULT_SECRET_KEY_NAMES = [
739
1086
  "apikey",
740
1087
  "apisecret",
@@ -824,6 +1171,13 @@ function redactJsonAttributeValue(key, value) {
824
1171
  if (scrubbedJson === json) return void 0;
825
1172
  return { stringValue: scrubbedJson };
826
1173
  }
1174
+ function identityAttrs(args) {
1175
+ return [
1176
+ attrString("ai.telemetry.metadata.raindrop.ai.userId", args.userId),
1177
+ attrString("ai.telemetry.metadata.raindrop.convoId", args.convoId),
1178
+ attrString("ai.telemetry.metadata.raindrop.eventName", args.eventName)
1179
+ ];
1180
+ }
827
1181
  function applyOtelSpanAttributeLimit(limit) {
828
1182
  var _a, _b;
829
1183
  try {
@@ -919,7 +1273,10 @@ var TraceShipper = class {
919
1273
  try {
920
1274
  const result = this.transformSpanHook(current);
921
1275
  if (result === null) return null;
922
- if (result !== void 0) current = result;
1276
+ if (result !== void 0) {
1277
+ copyReplayTraceDestination(current, result);
1278
+ current = result;
1279
+ }
923
1280
  } catch (err) {
924
1281
  if (this.debug) {
925
1282
  const msg = err instanceof Error ? err.message : String(err);
@@ -928,10 +1285,20 @@ var TraceShipper = class {
928
1285
  return null;
929
1286
  }
930
1287
  }
1288
+ try {
1289
+ const normalized = normalizeModelUsageSpan(current);
1290
+ copyReplayTraceDestination(current, normalized);
1291
+ current = normalized;
1292
+ } catch (e) {
1293
+ }
931
1294
  if (!this.disableDefaultRedaction) {
932
- current = defaultTransformSpan(current);
1295
+ const redacted = defaultTransformSpan(current);
1296
+ copyReplayTraceDestination(current, redacted);
1297
+ current = redacted;
933
1298
  }
934
- return this.capSpanAttributes(current);
1299
+ const capped = this.capSpanAttributes(current);
1300
+ copyReplayTraceDestination(current, capped);
1301
+ return capped;
935
1302
  }
936
1303
  isDebugEnabled() {
937
1304
  return this.debug;
@@ -948,33 +1315,55 @@ var TraceShipper = class {
948
1315
  const started = (_a = args.startTimeUnixNano) != null ? _a : nowUnixNanoString();
949
1316
  const attrs = [
950
1317
  attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
951
- attrString("ai.operationId", args.operationId)
1318
+ ...identityAttrs(args),
1319
+ attrString("ai.operationId", args.operationId),
1320
+ ...Object.entries(evalScopeAttributes()).map(([key, value]) => attrString(key, value))
952
1321
  ];
953
1322
  if ((_b = args.attributes) == null ? void 0 : _b.length) attrs.push(...args.attributes);
954
- const span = { ids, name: args.name, startTimeUnixNano: started, attributes: attrs };
955
- this.mirrorToLocalDebugger(
956
- buildOtlpSpan({
957
- ids: span.ids,
958
- name: span.name,
959
- startTimeUnixNano: span.startTimeUnixNano,
960
- endTimeUnixNano: span.startTimeUnixNano,
961
- // placeholder — will be updated on endSpan
962
- attributes: span.attributes,
963
- status: { code: SpanStatusCode.UNSET }
964
- })
965
- );
1323
+ const span = {
1324
+ ids,
1325
+ name: args.name,
1326
+ startTimeUnixNano: started,
1327
+ attributes: attrs
1328
+ };
1329
+ rememberReplayTraceDestination(span);
1330
+ const openSpan = buildOtlpSpan({
1331
+ ids: span.ids,
1332
+ name: span.name,
1333
+ startTimeUnixNano: span.startTimeUnixNano,
1334
+ endTimeUnixNano: span.startTimeUnixNano,
1335
+ // placeholder — will be updated on endSpan
1336
+ attributes: span.attributes,
1337
+ status: { code: SpanStatusCode.UNSET }
1338
+ });
1339
+ copyReplayTraceDestination(span, openSpan);
1340
+ this.mirrorToLocalDebugger(openSpan);
966
1341
  return span;
967
1342
  }
968
- mirrorToLocalDebugger(span) {
969
- if (!this.localDebuggerUrl) return;
1343
+ mirrorToLocalDebugger(span, completed = false) {
1344
+ const destination = replayTraceDestinationFor(span);
1345
+ if (destination && destination.kind !== "workshop") return;
1346
+ const localDebuggerUrl = (destination == null ? void 0 : destination.kind) === "workshop" ? destination.url : this.localDebuggerUrl;
1347
+ if (!localDebuggerUrl) return;
970
1348
  const redacted = this.redactSpan(span);
971
1349
  if (redacted === null) return;
1350
+ if (completed && (destination == null ? void 0 : destination.kind) === "workshop") {
1351
+ recordReplayExportedSpan(
1352
+ destination,
1353
+ base64ToHex(redacted.spanId),
1354
+ base64ToHex(redacted.traceId)
1355
+ );
1356
+ }
972
1357
  const body = buildExportTraceServiceRequest([redacted], this.serviceName, this.serviceVersion);
973
- mirrorTraceExportToLocalDebugger(body, {
974
- baseUrl: this.localDebuggerUrl,
1358
+ const p = mirrorTraceExportToLocalDebugger(body, {
1359
+ baseUrl: localDebuggerUrl,
975
1360
  debug: false,
976
1361
  sdkName: this.sdkName
977
1362
  });
1363
+ this.inFlight.add(p);
1364
+ void p.finally(() => {
1365
+ this.inFlight.delete(p);
1366
+ });
978
1367
  }
979
1368
  endSpan(span, extra) {
980
1369
  var _a, _b;
@@ -996,14 +1385,17 @@ var TraceShipper = class {
996
1385
  attributes: span.attributes,
997
1386
  status
998
1387
  });
1388
+ copyReplayTraceDestination(span, otlp);
999
1389
  this.enqueue(otlp);
1000
- this.mirrorToLocalDebugger(otlp);
1390
+ this.mirrorToLocalDebugger(otlp, true);
1001
1391
  }
1002
1392
  createSpan(args) {
1003
1393
  var _a;
1004
1394
  const ids = createSpanIds(args.parent);
1005
1395
  const attrs = [
1006
- attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId)
1396
+ attrString("ai.telemetry.metadata.raindrop.eventId", args.eventId),
1397
+ ...identityAttrs(args),
1398
+ ...Object.entries(evalScopeAttributes()).map(([key, value]) => attrString(key, value))
1007
1399
  ];
1008
1400
  if ((_a = args.attributes) == null ? void 0 : _a.length) attrs.push(...args.attributes);
1009
1401
  const otlp = buildOtlpSpan({
@@ -1014,8 +1406,9 @@ var TraceShipper = class {
1014
1406
  attributes: attrs,
1015
1407
  status: args.status
1016
1408
  });
1409
+ rememberReplayTraceDestination(otlp);
1017
1410
  this.enqueue(otlp);
1018
- this.mirrorToLocalDebugger(otlp);
1411
+ this.mirrorToLocalDebugger(otlp, true);
1019
1412
  }
1020
1413
  enqueue(span) {
1021
1414
  if (!this.enabled) return;
@@ -1029,6 +1422,13 @@ var TraceShipper = class {
1029
1422
  }
1030
1423
  const redacted = this.redactSpan(span);
1031
1424
  if (redacted === null) return;
1425
+ const destination = replayTraceDestinationFor(redacted);
1426
+ recordReplayExportedSpan(
1427
+ destination,
1428
+ base64ToHex(redacted.spanId),
1429
+ base64ToHex(redacted.traceId)
1430
+ );
1431
+ if ((destination == null ? void 0 : destination.kind) === "workshop") return;
1032
1432
  if (this.queue.length >= this.maxQueueSize) {
1033
1433
  this.queue.shift();
1034
1434
  }
@@ -1047,47 +1447,67 @@ var TraceShipper = class {
1047
1447
  }
1048
1448
  }
1049
1449
  async flush() {
1050
- if (!this.enabled) return;
1450
+ var _a;
1451
+ if (!this.enabled) {
1452
+ await Promise.all([...this.inFlight].map((p) => p.catch(() => {
1453
+ })));
1454
+ return;
1455
+ }
1051
1456
  if (this.timer) {
1052
1457
  clearTimeout(this.timer);
1053
1458
  this.timer = void 0;
1054
1459
  }
1055
1460
  while (this.queue.length > 0) {
1056
1461
  const batch = this.queue.splice(0, this.maxBatchSize);
1057
- if (!this.writeKey) continue;
1058
- const opts = this.requestOpts();
1059
- if (!opts) {
1060
- rateLimitedLog(
1061
- `${this.prefix}.shutdown_deadline`,
1062
- () => console.warn(
1063
- `${this.prefix} shutdown flush deadline exceeded; dropping ${batch.length} spans`
1064
- )
1065
- );
1066
- continue;
1067
- }
1068
- const body = buildExportTraceServiceRequest(batch, this.serviceName, this.serviceVersion);
1069
- const url = `${this.baseUrl}traces`;
1070
- if (this.debug) {
1071
- console.log(`${this.prefix} sending traces batch`, {
1072
- spans: batch.length,
1073
- endpoint: url
1074
- });
1462
+ const groups = /* @__PURE__ */ new Map();
1463
+ for (const span of batch) {
1464
+ const destination = replayTraceDestinationFor(span);
1465
+ const spans = groups.get(destination);
1466
+ if (spans) spans.push(span);
1467
+ else groups.set(destination, [span]);
1075
1468
  }
1076
- const p = postJson(url, body, this.requestHeaders(), opts);
1077
- this.inFlight.add(p);
1078
- try {
1079
- try {
1080
- await p;
1081
- if (this.debug) console.log(`${this.prefix} sent ${batch.length} spans`);
1082
- } catch (err) {
1083
- const msg = err instanceof Error ? err.message : String(err);
1469
+ for (const [destination, spans] of groups) {
1470
+ if (!destination && !this.writeKey) continue;
1471
+ if ((destination == null ? void 0 : destination.kind) === "workshop") continue;
1472
+ const opts = this.requestOpts();
1473
+ if (!opts) {
1084
1474
  rateLimitedLog(
1085
- `${this.prefix}.send_spans_failed`,
1086
- () => console.warn(`${this.prefix} failed to send ${batch.length} spans: ${msg}`)
1475
+ `${this.prefix}.shutdown_deadline`,
1476
+ () => console.warn(
1477
+ `${this.prefix} shutdown flush deadline exceeded; dropping ${spans.length} spans`
1478
+ )
1087
1479
  );
1480
+ continue;
1481
+ }
1482
+ const body = buildExportTraceServiceRequest(spans, this.serviceName, this.serviceVersion);
1483
+ const url = destination ? replayTraceUrl(destination.url) : `${this.baseUrl}traces`;
1484
+ const headers = destination ? {
1485
+ Authorization: `Bearer ${destination.apiKey}`,
1486
+ "X-Raindrop-Replay-Id": destination.replayId,
1487
+ "X-Raindrop-Project-Id": (_a = destination.projectId) != null ? _a : "default"
1488
+ } : this.requestHeaders();
1489
+ if (this.debug) {
1490
+ console.log(`${this.prefix} sending traces batch`, {
1491
+ spans: spans.length,
1492
+ endpoint: url
1493
+ });
1494
+ }
1495
+ const p = postJson(url, body, headers, opts);
1496
+ this.inFlight.add(p);
1497
+ try {
1498
+ try {
1499
+ await p;
1500
+ if (this.debug) console.log(`${this.prefix} sent ${spans.length} spans`);
1501
+ } catch (err) {
1502
+ const msg = err instanceof Error ? err.message : String(err);
1503
+ rateLimitedLog(
1504
+ `${this.prefix}.send_spans_failed`,
1505
+ () => console.warn(`${this.prefix} failed to send ${spans.length} spans: ${msg}`)
1506
+ );
1507
+ }
1508
+ } finally {
1509
+ this.inFlight.delete(p);
1088
1510
  }
1089
- } finally {
1090
- this.inFlight.delete(p);
1091
1511
  }
1092
1512
  }
1093
1513
  }
@@ -1130,7 +1550,9 @@ var TraceShipper = class {
1130
1550
  if (!settled) {
1131
1551
  rateLimitedLog(
1132
1552
  `${this.prefix}.shutdown_deadline`,
1133
- () => console.warn(`${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`)
1553
+ () => console.warn(
1554
+ `${this.prefix} shutdown flush deadline exceeded; abandoning in-flight spans`
1555
+ )
1134
1556
  );
1135
1557
  }
1136
1558
  } finally {
@@ -1282,6 +1704,10 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
1282
1704
  });
1283
1705
  }
1284
1706
  }
1707
+ rootErrorPatch(runId, error) {
1708
+ const rootId = this.rootRunIds.get(runId) ?? runId;
1709
+ return rootId === runId ? { error } : void 0;
1710
+ }
1285
1711
  // ---------------------------------------------------------------------------
1286
1712
  // LLM events
1287
1713
  // ---------------------------------------------------------------------------
@@ -1377,8 +1803,6 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
1377
1803
  ]
1378
1804
  });
1379
1805
  const properties = {};
1380
- if (promptTokens != null) properties["ai.usage.prompt_tokens"] = promptTokens;
1381
- if (completionTokens != null) properties["ai.usage.completion_tokens"] = completionTokens;
1382
1806
  if (cachedTokens != null) properties["ai.usage.cached_tokens"] = cachedTokens;
1383
1807
  if (reasoningTokens != null) properties["ai.usage.thoughts_tokens"] = reasoningTokens;
1384
1808
  if (finishReason != null) properties["ai.finish_reason"] = finishReason;
@@ -1386,6 +1810,7 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
1386
1810
  await this.finalizeEventIfRoot(runId, {
1387
1811
  output: cappedOutput,
1388
1812
  model,
1813
+ usage: { promptTokens, completionTokens },
1389
1814
  properties: Object.keys(properties).length > 0 ? properties : void 0
1390
1815
  });
1391
1816
  } catch {
@@ -1403,14 +1828,7 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
1403
1828
  if (span) {
1404
1829
  this.traceShipper.endSpan(span, { error: err });
1405
1830
  }
1406
- const errorInfo = extractErrorInfo(err);
1407
- await this.finalizeEventIfRoot(runId, {
1408
- output: `[Error] ${errorInfo.type}: ${errorInfo.message}`,
1409
- properties: {
1410
- "error.type": errorInfo.type,
1411
- "error.message": errorInfo.message
1412
- }
1413
- });
1831
+ await this.finalizeEventIfRoot(runId, this.rootErrorPatch(runId, err));
1414
1832
  } catch {
1415
1833
  } finally {
1416
1834
  this.cleanup(runId);
@@ -1472,14 +1890,7 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
1472
1890
  const span = this.spans.get(runId);
1473
1891
  if (span) this.traceShipper.endSpan(span, { error: err });
1474
1892
  }
1475
- const errorInfo = extractErrorInfo(err);
1476
- await this.finalizeEventIfRoot(runId, {
1477
- output: `[Error] ${errorInfo.type}: ${errorInfo.message}`,
1478
- properties: {
1479
- "error.type": errorInfo.type,
1480
- "error.message": errorInfo.message
1481
- }
1482
- });
1893
+ await this.finalizeEventIfRoot(runId, this.rootErrorPatch(runId, err));
1483
1894
  } catch {
1484
1895
  } finally {
1485
1896
  this.cleanup(runId);
@@ -1534,14 +1945,7 @@ var RaindropDeepAgentsHandler = class extends BaseCallbackHandler {
1534
1945
  try {
1535
1946
  const span = this.spans.get(runId);
1536
1947
  if (span) this.traceShipper.endSpan(span, { error: err });
1537
- const errorInfo = extractErrorInfo(err);
1538
- await this.finalizeEventIfRoot(runId, {
1539
- output: `[Error] ${errorInfo.type}: ${errorInfo.message}`,
1540
- properties: {
1541
- "error.type": errorInfo.type,
1542
- "error.message": errorInfo.message
1543
- }
1544
- });
1948
+ await this.finalizeEventIfRoot(runId, this.rootErrorPatch(runId, err));
1545
1949
  } catch {
1546
1950
  } finally {
1547
1951
  this.cleanup(runId);
@@ -1706,18 +2110,6 @@ function asNumber(val) {
1706
2110
  const n = typeof val === "number" ? val : Number(val);
1707
2111
  return Number.isFinite(n) ? n : void 0;
1708
2112
  }
1709
- function extractErrorInfo(err) {
1710
- if (err instanceof Error) {
1711
- return { type: err.name || err.constructor.name, message: err.message };
1712
- }
1713
- if (typeof err === "object" && err !== null) {
1714
- const e = err;
1715
- const type = e.name ?? e.constructor?.name ?? "Error";
1716
- const message = e.message ?? safeStringify(err);
1717
- return { type, message };
1718
- }
1719
- return { type: "Error", message: String(err) };
1720
- }
1721
2113
 
1722
2114
  // src/version.ts
1723
2115
  var libraryName = "@raindrop-ai/deep-agents";