braintrust 3.5.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dev/dist/index.d.mts +4 -2
  2. package/dev/dist/index.d.ts +4 -2
  3. package/dev/dist/index.js +2453 -612
  4. package/dev/dist/index.mjs +2150 -309
  5. package/dist/auto-instrumentations/bundler/esbuild.cjs +115 -6
  6. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  7. package/dist/auto-instrumentations/bundler/rollup.cjs +115 -6
  8. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  9. package/dist/auto-instrumentations/bundler/vite.cjs +115 -6
  10. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  11. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +955 -0
  12. package/dist/auto-instrumentations/bundler/webpack-loader.d.ts +53 -0
  13. package/dist/auto-instrumentations/bundler/webpack.cjs +115 -6
  14. package/dist/auto-instrumentations/bundler/webpack.mjs +2 -2
  15. package/dist/auto-instrumentations/{chunk-DQTPSXJB.mjs → chunk-AKEXR4AL.mjs} +116 -7
  16. package/dist/auto-instrumentations/{chunk-F3TJZ3Z2.mjs → chunk-ZK2IYER2.mjs} +3 -1
  17. package/dist/auto-instrumentations/hook.mjs +199 -55
  18. package/dist/auto-instrumentations/index.cjs +116 -6
  19. package/dist/auto-instrumentations/index.d.mts +3 -1
  20. package/dist/auto-instrumentations/index.d.ts +3 -1
  21. package/dist/auto-instrumentations/index.mjs +3 -1
  22. package/dist/browser.d.mts +17 -4
  23. package/dist/browser.d.ts +17 -4
  24. package/dist/browser.js +2386 -440
  25. package/dist/browser.mjs +2386 -440
  26. package/dist/cli.js +2118 -273
  27. package/dist/edge-light.d.mts +1 -1
  28. package/dist/edge-light.d.ts +1 -1
  29. package/dist/edge-light.js +2348 -485
  30. package/dist/edge-light.mjs +2348 -485
  31. package/dist/index.d.mts +30 -17
  32. package/dist/index.d.ts +30 -17
  33. package/dist/index.js +2709 -761
  34. package/dist/index.mjs +2392 -444
  35. package/dist/instrumentation/index.d.mts +3 -0
  36. package/dist/instrumentation/index.d.ts +3 -0
  37. package/dist/instrumentation/index.js +2030 -274
  38. package/dist/instrumentation/index.mjs +2030 -274
  39. package/dist/workerd.d.mts +1 -1
  40. package/dist/workerd.d.ts +1 -1
  41. package/dist/workerd.js +2348 -485
  42. package/dist/workerd.mjs +2348 -485
  43. package/package.json +5 -1
package/dist/cli.js CHANGED
@@ -1232,7 +1232,7 @@ var require_package = __commonJS({
1232
1232
  "package.json"(exports2, module2) {
1233
1233
  module2.exports = {
1234
1234
  name: "braintrust",
1235
- version: "3.5.0",
1235
+ version: "3.7.0",
1236
1236
  description: "SDK for integrating Braintrust",
1237
1237
  repository: {
1238
1238
  type: "git",
@@ -1319,6 +1319,10 @@ var require_package = __commonJS({
1319
1319
  module: "./dist/auto-instrumentations/bundler/webpack.mjs",
1320
1320
  require: "./dist/auto-instrumentations/bundler/webpack.js"
1321
1321
  },
1322
+ "./webpack-loader": {
1323
+ types: "./dist/auto-instrumentations/bundler/webpack-loader.d.ts",
1324
+ require: "./dist/auto-instrumentations/bundler/webpack-loader.cjs"
1325
+ },
1322
1326
  "./esbuild": {
1323
1327
  types: "./dist/auto-instrumentations/bundler/esbuild.d.ts",
1324
1328
  import: "./dist/auto-instrumentations/bundler/esbuild.mjs",
@@ -5775,6 +5779,9 @@ var BRAINTRUST_ATTACHMENT = BraintrustAttachmentReference.shape.type.value;
5775
5779
  var EXTERNAL_ATTACHMENT = ExternalAttachmentReference.shape.type.value;
5776
5780
  var LOGS3_OVERFLOW_REFERENCE_TYPE = "logs3_overflow";
5777
5781
  var BRAINTRUST_PARAMS = Object.keys(BraintrustModelParams.shape);
5782
+ var RESET_CONTEXT_MANAGER_STATE = Symbol.for(
5783
+ "braintrust.resetContextManagerState"
5784
+ );
5778
5785
  var DEFAULT_MAX_REQUEST_SIZE = 6 * 1024 * 1024;
5779
5786
  var parametersRowSchema = import_v38.z.object({
5780
5787
  id: import_v38.z.string().uuid(),
@@ -5837,13 +5844,18 @@ function applyMaskingToField(maskingFunction, data, fieldName) {
5837
5844
  return `ERROR: Failed to mask field '${fieldName}' - ${errorType}`;
5838
5845
  }
5839
5846
  }
5847
+ var BRAINTRUST_CURRENT_SPAN_STORE = Symbol.for(
5848
+ "braintrust.currentSpanStore"
5849
+ );
5840
5850
  var ContextManager = class {
5841
5851
  };
5842
5852
  var BraintrustContextManager = class extends ContextManager {
5843
5853
  _currentSpan;
5854
+ [BRAINTRUST_CURRENT_SPAN_STORE];
5844
5855
  constructor() {
5845
5856
  super();
5846
5857
  this._currentSpan = isomorph_default.newAsyncLocalStorage();
5858
+ this[BRAINTRUST_CURRENT_SPAN_STORE] = this._currentSpan;
5847
5859
  }
5848
5860
  getParentSpanIds() {
5849
5861
  const currentSpan2 = this._currentSpan.getStore();
@@ -6050,6 +6062,9 @@ var BraintrustState = class _BraintrustState {
6050
6062
  resetIdGenState() {
6051
6063
  this._idGenerator = null;
6052
6064
  }
6065
+ [RESET_CONTEXT_MANAGER_STATE]() {
6066
+ this._contextManager = null;
6067
+ }
6053
6068
  get idGenerator() {
6054
6069
  if (this._idGenerator === null) {
6055
6070
  this._idGenerator = getIdGenerator();
@@ -12596,6 +12611,90 @@ var fancyReporter = {
12596
12611
  var import_node_async_hooks = require("node:async_hooks");
12597
12612
  var diagnostics_channel = __toESM(require("node:diagnostics_channel"));
12598
12613
  var path = __toESM(require("node:path"));
12614
+
12615
+ // src/auto-instrumentations/patch-tracing-channel.ts
12616
+ function patchTracingChannel(tracingChannelFn) {
12617
+ const dummyChannel = tracingChannelFn("__braintrust_probe__");
12618
+ const TracingChannel = dummyChannel?.constructor;
12619
+ if (!TracingChannel?.prototype) {
12620
+ return;
12621
+ }
12622
+ if (!Object.getOwnPropertyDescriptor(TracingChannel.prototype, "hasSubscribers")) {
12623
+ Object.defineProperty(TracingChannel.prototype, "hasSubscribers", {
12624
+ configurable: true,
12625
+ enumerable: false,
12626
+ get() {
12627
+ return Boolean(
12628
+ this.start?.hasSubscribers || this.end?.hasSubscribers || this.asyncStart?.hasSubscribers || this.asyncEnd?.hasSubscribers || this.error?.hasSubscribers
12629
+ );
12630
+ }
12631
+ });
12632
+ }
12633
+ if (TracingChannel.prototype.tracePromise) {
12634
+ TracingChannel.prototype.tracePromise = function(fn, context2 = {}, thisArg, ...args) {
12635
+ const { start, end, asyncStart, asyncEnd, error: error2 } = this;
12636
+ function publishRejected(err) {
12637
+ context2.error = err;
12638
+ error2?.publish(context2);
12639
+ asyncStart?.publish(context2);
12640
+ asyncEnd?.publish(context2);
12641
+ }
12642
+ function publishResolved(result) {
12643
+ context2.result = result;
12644
+ asyncStart?.publish(context2);
12645
+ asyncEnd?.publish(context2);
12646
+ }
12647
+ return start.runStores(context2, () => {
12648
+ try {
12649
+ const result = Reflect.apply(fn, thisArg, args);
12650
+ end?.publish(context2);
12651
+ if (result && (typeof result === "object" || typeof result === "function") && typeof result.then === "function") {
12652
+ if (result.constructor === Promise) {
12653
+ return result.then(
12654
+ (res) => {
12655
+ publishResolved(res);
12656
+ return res;
12657
+ },
12658
+ (err) => {
12659
+ publishRejected(err);
12660
+ return Promise.reject(err);
12661
+ }
12662
+ );
12663
+ }
12664
+ void result.then(
12665
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
12666
+ (resolved) => {
12667
+ try {
12668
+ publishResolved(resolved);
12669
+ } catch {
12670
+ }
12671
+ },
12672
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
12673
+ (err) => {
12674
+ try {
12675
+ publishRejected(err);
12676
+ } catch {
12677
+ }
12678
+ }
12679
+ );
12680
+ return result;
12681
+ }
12682
+ context2.result = result;
12683
+ asyncStart?.publish(context2);
12684
+ asyncEnd?.publish(context2);
12685
+ return result;
12686
+ } catch (err) {
12687
+ context2.error = err;
12688
+ error2?.publish(context2);
12689
+ end?.publish(context2);
12690
+ throw err;
12691
+ }
12692
+ });
12693
+ };
12694
+ }
12695
+ }
12696
+
12697
+ // src/node/config.ts
12599
12698
  var fs = __toESM(require("node:fs/promises"));
12600
12699
  var os = __toESM(require("node:os"));
12601
12700
  var fsSync = __toESM(require("node:fs"));
@@ -13352,6 +13451,36 @@ function startSpanForEvent(config3, event, channelName) {
13352
13451
  }
13353
13452
  return { span, startTime };
13354
13453
  }
13454
+ function ensureSpanStateForEvent(states, config3, event, channelName) {
13455
+ const key = event;
13456
+ const existing = states.get(key);
13457
+ if (existing) {
13458
+ return existing;
13459
+ }
13460
+ const created = startSpanForEvent(config3, event, channelName);
13461
+ states.set(key, created);
13462
+ return created;
13463
+ }
13464
+ function bindCurrentSpanStoreToStart(tracingChannel2, states, config3, channelName) {
13465
+ const state = _internalGetGlobalState();
13466
+ const startChannel = tracingChannel2.start;
13467
+ const currentSpanStore = state?.contextManager ? state.contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
13468
+ if (!currentSpanStore || !startChannel) {
13469
+ return void 0;
13470
+ }
13471
+ startChannel.bindStore(
13472
+ currentSpanStore,
13473
+ (event) => ensureSpanStateForEvent(
13474
+ states,
13475
+ config3,
13476
+ event,
13477
+ channelName
13478
+ ).span
13479
+ );
13480
+ return () => {
13481
+ startChannel.unbindStore(currentSpanStore);
13482
+ };
13483
+ }
13355
13484
  function logErrorAndEnd(states, event) {
13356
13485
  const spanData = states.get(event);
13357
13486
  if (!spanData) {
@@ -13367,15 +13496,19 @@ function traceAsyncChannel(channel2, config3) {
13367
13496
  const tracingChannel2 = channel2.tracingChannel();
13368
13497
  const states = /* @__PURE__ */ new WeakMap();
13369
13498
  const channelName = channel2.channelName;
13499
+ const unbindCurrentSpanStore = bindCurrentSpanStoreToStart(
13500
+ tracingChannel2,
13501
+ states,
13502
+ config3,
13503
+ channelName
13504
+ );
13370
13505
  const handlers = {
13371
13506
  start: (event) => {
13372
- states.set(
13507
+ ensureSpanStateForEvent(
13508
+ states,
13509
+ config3,
13373
13510
  event,
13374
- startSpanForEvent(
13375
- config3,
13376
- event,
13377
- channelName
13378
- )
13511
+ channelName
13379
13512
  );
13380
13513
  },
13381
13514
  asyncEnd: (event) => {
@@ -13417,6 +13550,7 @@ function traceAsyncChannel(channel2, config3) {
13417
13550
  };
13418
13551
  tracingChannel2.subscribe(handlers);
13419
13552
  return () => {
13553
+ unbindCurrentSpanStore?.();
13420
13554
  tracingChannel2.unsubscribe(handlers);
13421
13555
  };
13422
13556
  }
@@ -13424,15 +13558,19 @@ function traceStreamingChannel(channel2, config3) {
13424
13558
  const tracingChannel2 = channel2.tracingChannel();
13425
13559
  const states = /* @__PURE__ */ new WeakMap();
13426
13560
  const channelName = channel2.channelName;
13561
+ const unbindCurrentSpanStore = bindCurrentSpanStoreToStart(
13562
+ tracingChannel2,
13563
+ states,
13564
+ config3,
13565
+ channelName
13566
+ );
13427
13567
  const handlers = {
13428
13568
  start: (event) => {
13429
- states.set(
13569
+ ensureSpanStateForEvent(
13570
+ states,
13571
+ config3,
13430
13572
  event,
13431
- startSpanForEvent(
13432
- config3,
13433
- event,
13434
- channelName
13435
- )
13573
+ channelName
13436
13574
  );
13437
13575
  },
13438
13576
  asyncEnd: (event) => {
@@ -13548,6 +13686,7 @@ function traceStreamingChannel(channel2, config3) {
13548
13686
  };
13549
13687
  tracingChannel2.subscribe(handlers);
13550
13688
  return () => {
13689
+ unbindCurrentSpanStore?.();
13551
13690
  tracingChannel2.unsubscribe(handlers);
13552
13691
  };
13553
13692
  }
@@ -13555,15 +13694,19 @@ function traceSyncStreamChannel(channel2, config3) {
13555
13694
  const tracingChannel2 = channel2.tracingChannel();
13556
13695
  const states = /* @__PURE__ */ new WeakMap();
13557
13696
  const channelName = channel2.channelName;
13697
+ const unbindCurrentSpanStore = bindCurrentSpanStoreToStart(
13698
+ tracingChannel2,
13699
+ states,
13700
+ config3,
13701
+ channelName
13702
+ );
13558
13703
  const handlers = {
13559
13704
  start: (event) => {
13560
- states.set(
13705
+ ensureSpanStateForEvent(
13706
+ states,
13707
+ config3,
13561
13708
  event,
13562
- startSpanForEvent(
13563
- config3,
13564
- event,
13565
- channelName
13566
- )
13709
+ channelName
13567
13710
  );
13568
13711
  },
13569
13712
  end: (event) => {
@@ -13652,6 +13795,7 @@ function traceSyncStreamChannel(channel2, config3) {
13652
13795
  };
13653
13796
  tracingChannel2.subscribe(handlers);
13654
13797
  return () => {
13798
+ unbindCurrentSpanStore?.();
13655
13799
  tracingChannel2.unsubscribe(handlers);
13656
13800
  };
13657
13801
  }
@@ -14423,7 +14567,7 @@ var AnthropicPlugin = class extends BasePlugin {
14423
14567
  this.unsubscribers.push(
14424
14568
  traceStreamingChannel(anthropicChannels.betaMessagesCreate, {
14425
14569
  ...anthropicConfig,
14426
- name: "anthropic.beta.messages.create"
14570
+ name: "anthropic.messages.create"
14427
14571
  })
14428
14572
  );
14429
14573
  }
@@ -14446,9 +14590,12 @@ function parseMetricsFromUsage2(usage) {
14446
14590
  return metrics;
14447
14591
  }
14448
14592
  function aggregateAnthropicStreamChunks(chunks) {
14449
- const deltas = [];
14593
+ const fallbackTextDeltas = [];
14594
+ const contentBlocks = {};
14595
+ const contentBlockDeltas = {};
14450
14596
  let metrics = {};
14451
14597
  let metadata = {};
14598
+ let role;
14452
14599
  for (const event of chunks) {
14453
14600
  switch (event?.type) {
14454
14601
  case "message_start":
@@ -14456,15 +14603,43 @@ function aggregateAnthropicStreamChunks(chunks) {
14456
14603
  const initialMetrics = parseMetricsFromUsage2(event.message.usage);
14457
14604
  metrics = { ...metrics, ...initialMetrics };
14458
14605
  }
14606
+ if (typeof event.message?.role === "string") {
14607
+ role = event.message.role;
14608
+ }
14609
+ break;
14610
+ case "content_block_start":
14611
+ if (event.content_block) {
14612
+ contentBlocks[event.index] = event.content_block;
14613
+ contentBlockDeltas[event.index] = [];
14614
+ }
14459
14615
  break;
14460
14616
  case "content_block_delta":
14461
14617
  if (event.delta?.type === "text_delta") {
14462
14618
  const text = event.delta.text;
14463
14619
  if (text) {
14464
- deltas.push(text);
14620
+ if (contentBlocks[event.index] !== void 0 || contentBlockDeltas[event.index] !== void 0) {
14621
+ contentBlockDeltas[event.index] ??= [];
14622
+ contentBlockDeltas[event.index].push(text);
14623
+ } else {
14624
+ fallbackTextDeltas.push(text);
14625
+ }
14626
+ }
14627
+ } else if (event.delta?.type === "input_json_delta") {
14628
+ const partialJson = event.delta.partial_json;
14629
+ if (partialJson) {
14630
+ contentBlockDeltas[event.index] ??= [];
14631
+ contentBlockDeltas[event.index].push(partialJson);
14465
14632
  }
14466
14633
  }
14467
14634
  break;
14635
+ case "content_block_stop":
14636
+ finalizeContentBlock(
14637
+ event.index,
14638
+ contentBlocks,
14639
+ contentBlockDeltas,
14640
+ fallbackTextDeltas
14641
+ );
14642
+ break;
14468
14643
  case "message_delta":
14469
14644
  if (event.usage) {
14470
14645
  const finalMetrics = parseMetricsFromUsage2(event.usage);
@@ -14476,7 +14651,21 @@ function aggregateAnthropicStreamChunks(chunks) {
14476
14651
  break;
14477
14652
  }
14478
14653
  }
14479
- const output = deltas.join("");
14654
+ const orderedContent = Object.entries(contentBlocks).map(([index, block]) => ({
14655
+ block,
14656
+ index: Number(index)
14657
+ })).filter(({ block }) => block !== void 0).sort((left, right) => left.index - right.index).map(({ block }) => block);
14658
+ let output = fallbackTextDeltas.join("");
14659
+ if (orderedContent.length > 0) {
14660
+ if (orderedContent.every(isTextContentBlock)) {
14661
+ output = orderedContent.map((block) => block.text).join("");
14662
+ } else {
14663
+ output = {
14664
+ ...role ? { role } : {},
14665
+ content: orderedContent
14666
+ };
14667
+ }
14668
+ }
14480
14669
  const finalized = finalizeAnthropicTokens(metrics);
14481
14670
  const filteredMetrics = Object.fromEntries(
14482
14671
  Object.entries(finalized).filter(
@@ -14489,6 +14678,49 @@ function aggregateAnthropicStreamChunks(chunks) {
14489
14678
  metadata
14490
14679
  };
14491
14680
  }
14681
+ function finalizeContentBlock(index, contentBlocks, contentBlockDeltas, fallbackTextDeltas) {
14682
+ const contentBlock = contentBlocks[index];
14683
+ if (!contentBlock) {
14684
+ return;
14685
+ }
14686
+ const text = contentBlockDeltas[index]?.join("") ?? "";
14687
+ if (isToolUseContentBlock(contentBlock)) {
14688
+ if (!text) {
14689
+ return;
14690
+ }
14691
+ try {
14692
+ contentBlocks[index] = {
14693
+ ...contentBlock,
14694
+ input: JSON.parse(text)
14695
+ };
14696
+ } catch {
14697
+ fallbackTextDeltas.push(text);
14698
+ delete contentBlocks[index];
14699
+ }
14700
+ return;
14701
+ }
14702
+ if (isTextContentBlock(contentBlock)) {
14703
+ if (!text) {
14704
+ delete contentBlocks[index];
14705
+ return;
14706
+ }
14707
+ contentBlocks[index] = {
14708
+ ...contentBlock,
14709
+ text
14710
+ };
14711
+ return;
14712
+ }
14713
+ if (text) {
14714
+ fallbackTextDeltas.push(text);
14715
+ }
14716
+ delete contentBlocks[index];
14717
+ }
14718
+ function isTextContentBlock(contentBlock) {
14719
+ return contentBlock.type === "text";
14720
+ }
14721
+ function isToolUseContentBlock(contentBlock) {
14722
+ return contentBlock.type === "tool_use";
14723
+ }
14492
14724
  function isAnthropicBase64ContentBlock(input) {
14493
14725
  return (input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64";
14494
14726
  }
@@ -15611,12 +15843,15 @@ var claudeAgentSDKChannels = defineChannels(
15611
15843
  {
15612
15844
  query: channel({
15613
15845
  channelName: "query",
15614
- kind: "async"
15846
+ kind: "sync-stream"
15615
15847
  })
15616
15848
  }
15617
15849
  );
15618
15850
 
15619
15851
  // src/instrumentation/plugins/claude-agent-sdk-plugin.ts
15852
+ function isSubAgentToolName(toolName) {
15853
+ return toolName === "Agent" || toolName === "Task";
15854
+ }
15620
15855
  function filterSerializableOptions(options) {
15621
15856
  const allowedKeys = [
15622
15857
  "model",
@@ -15670,34 +15905,50 @@ function extractUsageFromMessage(message) {
15670
15905
  const cacheReadTokens = getNumberProperty(usage, "cache_read_input_tokens") || 0;
15671
15906
  const cacheCreationTokens = getNumberProperty(usage, "cache_creation_input_tokens") || 0;
15672
15907
  if (cacheReadTokens > 0 || cacheCreationTokens > 0) {
15673
- const cacheTokens = extractAnthropicCacheTokens(
15674
- cacheReadTokens,
15675
- cacheCreationTokens
15908
+ Object.assign(
15909
+ metrics,
15910
+ extractAnthropicCacheTokens(cacheReadTokens, cacheCreationTokens)
15676
15911
  );
15677
- Object.assign(metrics, cacheTokens);
15678
15912
  }
15679
15913
  if (Object.keys(metrics).length > 0) {
15680
15914
  Object.assign(metrics, finalizeAnthropicTokens(metrics));
15681
15915
  }
15682
15916
  return metrics;
15683
15917
  }
15684
- function buildLLMInput(prompt, conversationHistory) {
15685
- const promptMessage = typeof prompt === "string" ? { content: prompt, role: "user" } : void 0;
15686
- const inputParts = [
15687
- ...promptMessage ? [promptMessage] : [],
15688
- ...conversationHistory
15689
- ];
15918
+ function buildLLMInput(prompt, conversationHistory, capturedPromptMessages) {
15919
+ const promptMessages = [];
15920
+ if (typeof prompt === "string") {
15921
+ promptMessages.push({ content: prompt, role: "user" });
15922
+ } else if (capturedPromptMessages && capturedPromptMessages.length > 0) {
15923
+ for (const msg of capturedPromptMessages) {
15924
+ const role = msg.message?.role;
15925
+ const content = msg.message?.content;
15926
+ if (role && content !== void 0) {
15927
+ promptMessages.push({ content, role });
15928
+ }
15929
+ }
15930
+ }
15931
+ const inputParts = [...promptMessages, ...conversationHistory];
15690
15932
  return inputParts.length > 0 ? inputParts : void 0;
15691
15933
  }
15692
- async function createLLMSpanForMessages(messages, prompt, conversationHistory, options, startTime, parentSpan) {
15693
- if (messages.length === 0) return void 0;
15934
+ function formatCapturedMessages(messages) {
15935
+ return messages.length > 0 ? messages : [];
15936
+ }
15937
+ async function createLLMSpanForMessages(messages, prompt, conversationHistory, options, startTime, capturedPromptMessages, parentSpan) {
15938
+ if (messages.length === 0) {
15939
+ return void 0;
15940
+ }
15694
15941
  const lastMessage = messages[messages.length - 1];
15695
15942
  if (lastMessage.type !== "assistant" || !lastMessage.message?.usage) {
15696
15943
  return void 0;
15697
15944
  }
15698
15945
  const model = lastMessage.message.model || options.model;
15699
15946
  const usage = extractUsageFromMessage(lastMessage);
15700
- const input = buildLLMInput(prompt, conversationHistory);
15947
+ const input = buildLLMInput(
15948
+ prompt,
15949
+ conversationHistory,
15950
+ capturedPromptMessages
15951
+ );
15701
15952
  const outputs = messages.map(
15702
15953
  (m) => m.message?.content && m.message?.role ? { content: m.message.content, role: m.message.role } : void 0
15703
15954
  ).filter(
@@ -15705,21 +15956,359 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
15705
15956
  );
15706
15957
  const span = startSpan({
15707
15958
  name: "anthropic.messages.create",
15959
+ parent: parentSpan,
15708
15960
  spanAttributes: {
15709
15961
  type: "llm" /* LLM */
15710
15962
  },
15711
- startTime,
15712
- parent: parentSpan
15963
+ startTime
15713
15964
  });
15714
15965
  span.log({
15715
15966
  input,
15716
- output: outputs,
15717
15967
  metadata: model ? { model } : void 0,
15718
- metrics: usage
15968
+ metrics: usage,
15969
+ output: outputs
15719
15970
  });
15720
15971
  await span.end();
15721
15972
  return lastMessage.message?.content && lastMessage.message?.role ? { content: lastMessage.message.content, role: lastMessage.message.role } : void 0;
15722
15973
  }
15974
+ function getMcpServerMetadata(serverName, mcpServers) {
15975
+ if (!serverName || !mcpServers) {
15976
+ return {};
15977
+ }
15978
+ const serverConfig = mcpServers[serverName];
15979
+ if (!serverConfig) {
15980
+ return {};
15981
+ }
15982
+ const metadata = {};
15983
+ if (serverConfig.type) {
15984
+ metadata["mcp.type"] = serverConfig.type;
15985
+ } else if (typeof serverConfig === "object" && "transport" in serverConfig) {
15986
+ metadata["mcp.type"] = "sdk";
15987
+ }
15988
+ if (serverConfig.url) {
15989
+ metadata["mcp.url"] = serverConfig.url;
15990
+ }
15991
+ if (serverConfig.command) {
15992
+ metadata["mcp.command"] = serverConfig.command;
15993
+ if (serverConfig.args) {
15994
+ metadata["mcp.args"] = serverConfig.args.join(" ");
15995
+ }
15996
+ }
15997
+ return metadata;
15998
+ }
15999
+ function parseToolName(rawToolName) {
16000
+ const mcpMatch = rawToolName.match(/^mcp__([^_]+)__(.+)$/);
16001
+ if (mcpMatch) {
16002
+ const [, mcpServer, toolName] = mcpMatch;
16003
+ return {
16004
+ displayName: `tool: ${mcpServer}/${toolName}`,
16005
+ mcpServer,
16006
+ rawToolName,
16007
+ toolName
16008
+ };
16009
+ }
16010
+ return {
16011
+ displayName: `tool: ${rawToolName}`,
16012
+ rawToolName,
16013
+ toolName: rawToolName
16014
+ };
16015
+ }
16016
+ function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers, subAgentSpans, endedSubAgentSpans) {
16017
+ const preToolUse = async (input, toolUseID) => {
16018
+ if (input.hook_event_name !== "PreToolUse" || !toolUseID) {
16019
+ return {};
16020
+ }
16021
+ if (isSubAgentToolName(input.tool_name)) {
16022
+ return {};
16023
+ }
16024
+ const parsed = parseToolName(input.tool_name);
16025
+ const toolSpan = startSpan({
16026
+ event: {
16027
+ input: input.tool_input,
16028
+ metadata: {
16029
+ "claude_agent_sdk.cwd": input.cwd,
16030
+ "claude_agent_sdk.raw_tool_name": parsed.rawToolName,
16031
+ "claude_agent_sdk.session_id": input.session_id,
16032
+ "gen_ai.tool.call.id": toolUseID,
16033
+ "gen_ai.tool.name": parsed.toolName,
16034
+ ...parsed.mcpServer && { "mcp.server": parsed.mcpServer },
16035
+ ...getMcpServerMetadata(parsed.mcpServer, mcpServers)
16036
+ }
16037
+ },
16038
+ name: parsed.displayName,
16039
+ parent: await resolveParentSpan(toolUseID),
16040
+ spanAttributes: { type: "tool" /* TOOL */ }
16041
+ });
16042
+ activeToolSpans.set(toolUseID, toolSpan);
16043
+ return {};
16044
+ };
16045
+ const postToolUse = async (input, toolUseID) => {
16046
+ if (input.hook_event_name !== "PostToolUse" || !toolUseID) {
16047
+ return {};
16048
+ }
16049
+ const subAgentSpan = subAgentSpans.get(toolUseID);
16050
+ if (subAgentSpan) {
16051
+ try {
16052
+ const response = input.tool_response;
16053
+ const metadata = {};
16054
+ if (response?.status) {
16055
+ metadata["claude_agent_sdk.status"] = response.status;
16056
+ }
16057
+ if (response?.totalDurationMs) {
16058
+ metadata["claude_agent_sdk.duration_ms"] = response.totalDurationMs;
16059
+ }
16060
+ if (response?.totalToolUseCount !== void 0) {
16061
+ metadata["claude_agent_sdk.tool_use_count"] = response.totalToolUseCount;
16062
+ }
16063
+ subAgentSpan.log({
16064
+ metadata,
16065
+ output: response?.content
16066
+ });
16067
+ } finally {
16068
+ subAgentSpan.end();
16069
+ endedSubAgentSpans.add(toolUseID);
16070
+ }
16071
+ return {};
16072
+ }
16073
+ const toolSpan = activeToolSpans.get(toolUseID);
16074
+ if (!toolSpan) {
16075
+ return {};
16076
+ }
16077
+ try {
16078
+ toolSpan.log({ output: input.tool_response });
16079
+ } finally {
16080
+ toolSpan.end();
16081
+ activeToolSpans.delete(toolUseID);
16082
+ }
16083
+ return {};
16084
+ };
16085
+ const postToolUseFailure = async (input, toolUseID) => {
16086
+ if (input.hook_event_name !== "PostToolUseFailure" || !toolUseID) {
16087
+ return {};
16088
+ }
16089
+ const subAgentSpan = subAgentSpans.get(toolUseID);
16090
+ if (subAgentSpan) {
16091
+ try {
16092
+ subAgentSpan.log({ error: input.error });
16093
+ } finally {
16094
+ subAgentSpan.end();
16095
+ endedSubAgentSpans.add(toolUseID);
16096
+ }
16097
+ return {};
16098
+ }
16099
+ const toolSpan = activeToolSpans.get(toolUseID);
16100
+ if (!toolSpan) {
16101
+ return {};
16102
+ }
16103
+ const parsed = parseToolName(input.tool_name);
16104
+ try {
16105
+ toolSpan.log({
16106
+ error: input.error,
16107
+ metadata: {
16108
+ "claude_agent_sdk.is_interrupt": input.is_interrupt,
16109
+ "claude_agent_sdk.session_id": input.session_id,
16110
+ "gen_ai.tool.call.id": toolUseID,
16111
+ "gen_ai.tool.name": parsed.toolName,
16112
+ ...parsed.mcpServer && { "mcp.server": parsed.mcpServer }
16113
+ }
16114
+ });
16115
+ } finally {
16116
+ toolSpan.end();
16117
+ activeToolSpans.delete(toolUseID);
16118
+ }
16119
+ return {};
16120
+ };
16121
+ return { postToolUse, postToolUseFailure, preToolUse };
16122
+ }
16123
+ function injectTracingHooks(options, resolveParentSpan, activeToolSpans, subAgentSpans, endedSubAgentSpans) {
16124
+ const { preToolUse, postToolUse, postToolUseFailure } = createToolTracingHooks(
16125
+ resolveParentSpan,
16126
+ activeToolSpans,
16127
+ options.mcpServers,
16128
+ subAgentSpans,
16129
+ endedSubAgentSpans
16130
+ );
16131
+ const existingHooks = options.hooks ?? {};
16132
+ return {
16133
+ ...options,
16134
+ hooks: {
16135
+ ...existingHooks,
16136
+ PostToolUse: [
16137
+ ...existingHooks.PostToolUse ?? [],
16138
+ { hooks: [postToolUse] }
16139
+ ],
16140
+ PostToolUseFailure: [
16141
+ ...existingHooks.PostToolUseFailure ?? [],
16142
+ {
16143
+ hooks: [postToolUseFailure]
16144
+ }
16145
+ ],
16146
+ PreToolUse: [
16147
+ ...existingHooks.PreToolUse ?? [],
16148
+ { hooks: [preToolUse] }
16149
+ ]
16150
+ }
16151
+ };
16152
+ }
16153
+ async function finalizeCurrentMessageGroup(state) {
16154
+ if (state.currentMessages.length === 0) {
16155
+ return;
16156
+ }
16157
+ const parentToolUseId = state.currentMessages[0]?.parent_tool_use_id ?? null;
16158
+ let parentSpan = await state.span.export();
16159
+ if (parentToolUseId) {
16160
+ const subAgentSpan = state.subAgentSpans.get(parentToolUseId);
16161
+ if (subAgentSpan) {
16162
+ parentSpan = await subAgentSpan.export();
16163
+ }
16164
+ }
16165
+ const finalMessage = await createLLMSpanForMessages(
16166
+ state.currentMessages,
16167
+ state.originalPrompt,
16168
+ state.finalResults,
16169
+ state.options,
16170
+ state.currentMessageStartTime,
16171
+ state.capturedPromptMessages,
16172
+ parentSpan
16173
+ );
16174
+ if (finalMessage) {
16175
+ state.finalResults.push(finalMessage);
16176
+ }
16177
+ const lastMessage = state.currentMessages[state.currentMessages.length - 1];
16178
+ if (lastMessage?.message?.usage) {
16179
+ state.accumulatedOutputTokens += getNumberProperty(lastMessage.message.usage, "output_tokens") || 0;
16180
+ }
16181
+ state.currentMessages.length = 0;
16182
+ }
16183
+ function maybeTrackToolUseContext(state, message) {
16184
+ if (message.type !== "assistant" || !Array.isArray(message.message?.content)) {
16185
+ return;
16186
+ }
16187
+ const parentToolUseId = message.parent_tool_use_id ?? null;
16188
+ for (const block of message.message.content) {
16189
+ if (typeof block !== "object" || block === null || !("type" in block) || block.type !== "tool_use" || !("id" in block) || typeof block.id !== "string") {
16190
+ continue;
16191
+ }
16192
+ state.toolUseToParent.set(block.id, parentToolUseId);
16193
+ if (block.name === "Task" && typeof block.input === "object" && block.input !== null && "subagent_type" in block.input && typeof block.input.subagent_type === "string") {
16194
+ state.pendingSubAgentNames.set(block.id, block.input.subagent_type);
16195
+ }
16196
+ }
16197
+ }
16198
+ async function maybeStartSubAgentSpan(state, message) {
16199
+ if (!("parent_tool_use_id" in message)) {
16200
+ return;
16201
+ }
16202
+ const parentToolUseId = message.parent_tool_use_id;
16203
+ if (!parentToolUseId) {
16204
+ return;
16205
+ }
16206
+ await ensureSubAgentSpan(
16207
+ state.pendingSubAgentNames,
16208
+ state.span,
16209
+ state.subAgentSpans,
16210
+ parentToolUseId
16211
+ );
16212
+ }
16213
+ async function ensureSubAgentSpan(pendingSubAgentNames, rootSpan, subAgentSpans, parentToolUseId) {
16214
+ const existingSpan = subAgentSpans.get(parentToolUseId);
16215
+ if (existingSpan) {
16216
+ return existingSpan;
16217
+ }
16218
+ const agentName = pendingSubAgentNames.get(parentToolUseId);
16219
+ const spanName = agentName ? `Agent: ${agentName}` : "Agent: sub-agent";
16220
+ const subAgentSpan = startSpan({
16221
+ event: {
16222
+ metadata: {
16223
+ ...agentName && { "claude_agent_sdk.agent_type": agentName }
16224
+ }
16225
+ },
16226
+ name: spanName,
16227
+ parent: await rootSpan.export(),
16228
+ spanAttributes: { type: "task" /* TASK */ }
16229
+ });
16230
+ subAgentSpans.set(parentToolUseId, subAgentSpan);
16231
+ return subAgentSpan;
16232
+ }
16233
+ async function handleStreamMessage(state, message) {
16234
+ maybeTrackToolUseContext(state, message);
16235
+ await maybeStartSubAgentSpan(state, message);
16236
+ const messageId = message.message?.id;
16237
+ if (messageId && messageId !== state.currentMessageId) {
16238
+ await finalizeCurrentMessageGroup(state);
16239
+ state.currentMessageId = messageId;
16240
+ state.currentMessageStartTime = getCurrentUnixTimestamp();
16241
+ }
16242
+ if (message.type === "assistant" && message.message?.usage) {
16243
+ state.currentMessages.push(message);
16244
+ }
16245
+ if (message.type !== "result" || !message.usage) {
16246
+ return;
16247
+ }
16248
+ const finalUsageMetrics = extractUsageFromMessage(message);
16249
+ if (state.currentMessages.length > 0 && finalUsageMetrics.completion_tokens !== void 0) {
16250
+ const lastMessage = state.currentMessages[state.currentMessages.length - 1];
16251
+ if (lastMessage?.message?.usage) {
16252
+ const adjustedTokens = finalUsageMetrics.completion_tokens - state.accumulatedOutputTokens;
16253
+ if (adjustedTokens >= 0) {
16254
+ lastMessage.message.usage.output_tokens = adjustedTokens;
16255
+ }
16256
+ const resultUsage = message.usage;
16257
+ if (resultUsage && typeof resultUsage === "object") {
16258
+ const cacheReadTokens = getNumberProperty(
16259
+ resultUsage,
16260
+ "cache_read_input_tokens"
16261
+ );
16262
+ if (cacheReadTokens !== void 0) {
16263
+ lastMessage.message.usage.cache_read_input_tokens = cacheReadTokens;
16264
+ }
16265
+ const cacheCreationTokens = getNumberProperty(
16266
+ resultUsage,
16267
+ "cache_creation_input_tokens"
16268
+ );
16269
+ if (cacheCreationTokens !== void 0) {
16270
+ lastMessage.message.usage.cache_creation_input_tokens = cacheCreationTokens;
16271
+ }
16272
+ }
16273
+ }
16274
+ }
16275
+ const metadata = {};
16276
+ if (message.num_turns !== void 0) {
16277
+ metadata.num_turns = message.num_turns;
16278
+ }
16279
+ if (message.session_id !== void 0) {
16280
+ metadata.session_id = message.session_id;
16281
+ }
16282
+ if (Object.keys(metadata).length > 0) {
16283
+ state.span.log({ metadata });
16284
+ }
16285
+ }
16286
+ async function finalizeQuerySpan(state) {
16287
+ try {
16288
+ await finalizeCurrentMessageGroup(state);
16289
+ state.span.log({
16290
+ output: state.finalResults.length > 0 ? state.finalResults[state.finalResults.length - 1] : void 0
16291
+ });
16292
+ if (state.capturedPromptMessages) {
16293
+ if (state.promptStarted()) {
16294
+ await state.promptDone;
16295
+ }
16296
+ if (state.capturedPromptMessages.length > 0) {
16297
+ state.span.log({
16298
+ input: formatCapturedMessages(state.capturedPromptMessages)
16299
+ });
16300
+ }
16301
+ }
16302
+ } finally {
16303
+ for (const [id, subAgentSpan] of state.subAgentSpans) {
16304
+ if (!state.endedSubAgentSpans.has(id)) {
16305
+ subAgentSpan.end();
16306
+ }
16307
+ }
16308
+ state.subAgentSpans.clear();
16309
+ state.span.end();
16310
+ }
16311
+ }
15723
16312
  var ClaudeAgentSDKPlugin = class extends BasePlugin {
15724
16313
  onEnable() {
15725
16314
  this.subscribeToQuery();
@@ -15730,19 +16319,36 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
15730
16319
  }
15731
16320
  this.unsubscribers = [];
15732
16321
  }
15733
- /**
15734
- * Subscribe to the query channel for agent interactions.
15735
- * Handles streaming responses and traces both the top-level agent task
15736
- * and individual LLM calls.
15737
- */
15738
16322
  subscribeToQuery() {
15739
16323
  const channel2 = claudeAgentSDKChannels.query.tracingChannel();
15740
16324
  const spans = /* @__PURE__ */ new WeakMap();
15741
16325
  const handlers = {
15742
16326
  start: (event) => {
15743
- const params = event.arguments[0];
15744
- const prompt = params?.prompt;
15745
- const options = params?.options ?? {};
16327
+ const params = event.arguments[0] ?? {};
16328
+ const originalPrompt = params.prompt;
16329
+ const options = params.options ?? {};
16330
+ const promptIsAsyncIterable = isAsyncIterable3(originalPrompt);
16331
+ let promptStarted = false;
16332
+ let capturedPromptMessages;
16333
+ let resolvePromptDone;
16334
+ const promptDone = new Promise((resolve2) => {
16335
+ resolvePromptDone = resolve2;
16336
+ });
16337
+ if (promptIsAsyncIterable) {
16338
+ capturedPromptMessages = [];
16339
+ const promptStream = originalPrompt;
16340
+ params.prompt = (async function* () {
16341
+ promptStarted = true;
16342
+ try {
16343
+ for await (const message of promptStream) {
16344
+ capturedPromptMessages.push(message);
16345
+ yield message;
16346
+ }
16347
+ } finally {
16348
+ resolvePromptDone?.();
16349
+ }
16350
+ })();
16351
+ }
15746
16352
  const span = startSpan({
15747
16353
  name: "Claude Agent",
15748
16354
  spanAttributes: {
@@ -15752,163 +16358,115 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
15752
16358
  const startTime = getCurrentUnixTimestamp();
15753
16359
  try {
15754
16360
  span.log({
15755
- input: typeof prompt === "string" ? prompt : {
15756
- type: "streaming",
15757
- description: "AsyncIterable<ClaudeAgentSDKMessage>"
15758
- },
16361
+ input: typeof originalPrompt === "string" ? originalPrompt : promptIsAsyncIterable ? void 0 : originalPrompt !== void 0 ? String(originalPrompt) : void 0,
15759
16362
  metadata: filterSerializableOptions(options)
15760
16363
  });
15761
16364
  } catch (error2) {
15762
16365
  console.error("Error extracting input for Claude Agent SDK:", error2);
15763
16366
  }
16367
+ const activeToolSpans = /* @__PURE__ */ new Map();
16368
+ const subAgentSpans = /* @__PURE__ */ new Map();
16369
+ const endedSubAgentSpans = /* @__PURE__ */ new Set();
16370
+ const toolUseToParent = /* @__PURE__ */ new Map();
16371
+ const pendingSubAgentNames = /* @__PURE__ */ new Map();
16372
+ const optionsWithHooks = injectTracingHooks(
16373
+ options,
16374
+ async (toolUseID) => {
16375
+ const parentToolUseId = toolUseToParent.get(toolUseID);
16376
+ if (parentToolUseId) {
16377
+ const subAgentSpan = await ensureSubAgentSpan(
16378
+ pendingSubAgentNames,
16379
+ span,
16380
+ subAgentSpans,
16381
+ parentToolUseId
16382
+ );
16383
+ return subAgentSpan.export();
16384
+ }
16385
+ return span.export();
16386
+ },
16387
+ activeToolSpans,
16388
+ subAgentSpans,
16389
+ endedSubAgentSpans
16390
+ );
16391
+ params.options = optionsWithHooks;
16392
+ event.arguments[0] = params;
15764
16393
  spans.set(event, {
15765
- span,
15766
- startTime,
15767
- conversationHistory: [],
15768
- currentMessages: [],
16394
+ accumulatedOutputTokens: 0,
16395
+ activeToolSpans,
16396
+ capturedPromptMessages,
15769
16397
  currentMessageId: void 0,
15770
16398
  currentMessageStartTime: startTime,
15771
- accumulatedOutputTokens: 0
16399
+ currentMessages: [],
16400
+ endedSubAgentSpans,
16401
+ finalResults: [],
16402
+ options: optionsWithHooks,
16403
+ originalPrompt,
16404
+ pendingSubAgentNames,
16405
+ processing: Promise.resolve(),
16406
+ promptDone,
16407
+ promptStarted: () => promptStarted,
16408
+ span,
16409
+ subAgentSpans,
16410
+ toolUseToParent
15772
16411
  });
15773
16412
  },
15774
- asyncEnd: (event) => {
15775
- const spanData = spans.get(event);
15776
- if (!spanData) {
16413
+ end: (event) => {
16414
+ const state = spans.get(event);
16415
+ if (!state) {
15777
16416
  return;
15778
16417
  }
15779
16418
  const eventResult = event.result;
15780
16419
  if (eventResult === void 0) {
15781
- spanData.span.end();
16420
+ state.span.end();
15782
16421
  spans.delete(event);
15783
16422
  return;
15784
16423
  }
15785
16424
  if (isAsyncIterable3(eventResult)) {
15786
16425
  patchStreamIfNeeded(eventResult, {
15787
- onChunk: async (message) => {
15788
- const currentTime = getCurrentUnixTimestamp();
15789
- const params = event.arguments[0];
15790
- const prompt = params?.prompt;
15791
- const options = params?.options ?? {};
15792
- const messageId = message.message?.id;
15793
- if (messageId && messageId !== spanData.currentMessageId) {
15794
- if (spanData.currentMessages.length > 0) {
15795
- const finalMessage = await createLLMSpanForMessages(
15796
- spanData.currentMessages,
15797
- prompt,
15798
- spanData.conversationHistory,
15799
- options,
15800
- spanData.currentMessageStartTime,
15801
- await spanData.span.export()
15802
- );
15803
- if (finalMessage) {
15804
- spanData.conversationHistory.push(finalMessage);
15805
- }
15806
- const lastMessage = spanData.currentMessages[spanData.currentMessages.length - 1];
15807
- if (lastMessage?.message?.usage) {
15808
- const outputTokens = getNumberProperty(
15809
- lastMessage.message.usage,
15810
- "output_tokens"
15811
- ) || 0;
15812
- spanData.accumulatedOutputTokens += outputTokens;
15813
- }
15814
- spanData.currentMessages = [];
15815
- }
15816
- spanData.currentMessageId = messageId;
15817
- spanData.currentMessageStartTime = currentTime;
15818
- }
15819
- if (message.type === "assistant" && message.message?.usage) {
15820
- spanData.currentMessages.push(message);
15821
- }
15822
- if (message.type === "result" && message.usage) {
15823
- const finalUsageMetrics = extractUsageFromMessage(message);
15824
- if (spanData.currentMessages.length > 0 && finalUsageMetrics.completion_tokens !== void 0) {
15825
- const lastMessage = spanData.currentMessages[spanData.currentMessages.length - 1];
15826
- if (lastMessage?.message?.usage) {
15827
- const adjustedTokens = finalUsageMetrics.completion_tokens - spanData.accumulatedOutputTokens;
15828
- if (adjustedTokens >= 0) {
15829
- lastMessage.message.usage.output_tokens = adjustedTokens;
15830
- }
15831
- }
15832
- }
15833
- const result_metadata = {};
15834
- if (message.num_turns !== void 0) {
15835
- result_metadata.num_turns = message.num_turns;
15836
- }
15837
- if (message.session_id !== void 0) {
15838
- result_metadata.session_id = message.session_id;
15839
- }
15840
- if (Object.keys(result_metadata).length > 0) {
15841
- spanData.span.log({
15842
- metadata: result_metadata
15843
- });
15844
- }
15845
- }
15846
- },
15847
- onComplete: async () => {
15848
- try {
15849
- const params = event.arguments[0];
15850
- const prompt = params?.prompt;
15851
- const options = params?.options ?? {};
15852
- if (spanData.currentMessages.length > 0) {
15853
- const finalMessage = await createLLMSpanForMessages(
15854
- spanData.currentMessages,
15855
- prompt,
15856
- spanData.conversationHistory,
15857
- options,
15858
- spanData.currentMessageStartTime,
15859
- await spanData.span.export()
15860
- );
15861
- if (finalMessage) {
15862
- spanData.conversationHistory.push(finalMessage);
15863
- }
15864
- }
15865
- spanData.span.log({
15866
- output: spanData.conversationHistory.length > 0 ? spanData.conversationHistory[spanData.conversationHistory.length - 1] : void 0
15867
- });
15868
- } catch (error2) {
16426
+ onChunk: (message) => {
16427
+ maybeTrackToolUseContext(state, message);
16428
+ state.processing = state.processing.then(() => handleStreamMessage(state, message)).catch((error2) => {
15869
16429
  console.error(
15870
- "Error extracting output for Claude Agent SDK:",
16430
+ "Error processing Claude Agent SDK stream chunk:",
15871
16431
  error2
15872
16432
  );
15873
- } finally {
15874
- spanData.span.end();
15875
- spans.delete(event);
15876
- }
16433
+ });
16434
+ },
16435
+ onComplete: () => {
16436
+ void state.processing.then(() => finalizeQuerySpan(state)).finally(() => {
16437
+ spans.delete(event);
16438
+ });
15877
16439
  },
15878
16440
  onError: (error2) => {
15879
- spanData.span.log({
15880
- error: error2.message
16441
+ void state.processing.then(() => {
16442
+ state.span.log({
16443
+ error: error2.message
16444
+ });
16445
+ }).then(() => finalizeQuerySpan(state)).finally(() => {
16446
+ spans.delete(event);
15881
16447
  });
15882
- spanData.span.end();
15883
- spans.delete(event);
15884
16448
  }
15885
16449
  });
15886
- } else {
15887
- try {
15888
- spanData.span.log({
15889
- output: eventResult
15890
- });
15891
- } catch (error2) {
15892
- console.error(
15893
- "Error extracting output for Claude Agent SDK:",
15894
- error2
15895
- );
15896
- } finally {
15897
- spanData.span.end();
15898
- spans.delete(event);
15899
- }
16450
+ return;
16451
+ }
16452
+ try {
16453
+ state.span.log({ output: eventResult });
16454
+ } catch (error2) {
16455
+ console.error("Error extracting output for Claude Agent SDK:", error2);
16456
+ } finally {
16457
+ state.span.end();
16458
+ spans.delete(event);
15900
16459
  }
15901
16460
  },
15902
16461
  error: (event) => {
15903
- const spanData = spans.get(event);
15904
- if (!spanData || !event.error) {
16462
+ const state = spans.get(event);
16463
+ if (!state || !event.error) {
15905
16464
  return;
15906
16465
  }
15907
- const { span } = spanData;
15908
- span.log({
16466
+ state.span.log({
15909
16467
  error: event.error.message
15910
16468
  });
15911
- span.end();
16469
+ state.span.end();
15912
16470
  spans.delete(event);
15913
16471
  }
15914
16472
  };
@@ -15932,6 +16490,18 @@ var googleGenAIChannels = defineChannels("@google/genai", {
15932
16490
  });
15933
16491
 
15934
16492
  // src/instrumentation/plugins/google-genai-plugin.ts
16493
+ var GOOGLE_GENAI_INTERNAL_CONTEXT = {
16494
+ caller_filename: "<node-internal>",
16495
+ caller_functionname: "<node-internal>",
16496
+ caller_lineno: 0
16497
+ };
16498
+ function createWrapperParityEvent(args) {
16499
+ return {
16500
+ context: GOOGLE_GENAI_INTERNAL_CONTEXT,
16501
+ input: args.input,
16502
+ metadata: args.metadata
16503
+ };
16504
+ }
15935
16505
  var GoogleGenAIPlugin = class extends BasePlugin {
15936
16506
  onEnable() {
15937
16507
  this.subscribeToGoogleGenAIChannels();
@@ -15940,51 +16510,282 @@ var GoogleGenAIPlugin = class extends BasePlugin {
15940
16510
  this.unsubscribers = unsubscribeAll(this.unsubscribers);
15941
16511
  }
15942
16512
  subscribeToGoogleGenAIChannels() {
15943
- this.unsubscribers.push(
15944
- traceAsyncChannel(googleGenAIChannels.generateContent, {
15945
- name: "google-genai.generateContent",
15946
- type: "llm" /* LLM */,
15947
- extractInput: ([params]) => {
15948
- const input = serializeInput(params);
15949
- const metadata = extractMetadata(params);
15950
- return {
15951
- input,
15952
- metadata: { ...metadata, provider: "google-genai" }
15953
- };
15954
- },
15955
- extractOutput: (result) => {
15956
- return result;
15957
- },
15958
- extractMetrics: (result, startTime) => {
15959
- return extractGenerateContentMetrics(result, startTime);
15960
- }
15961
- })
16513
+ this.subscribeToGenerateContentChannel();
16514
+ this.subscribeToGenerateContentStreamChannel();
16515
+ }
16516
+ subscribeToGenerateContentChannel() {
16517
+ const tracingChannel2 = googleGenAIChannels.generateContent.tracingChannel();
16518
+ const states = /* @__PURE__ */ new WeakMap();
16519
+ const unbindCurrentSpanStore = bindCurrentSpanStoreToStart2(
16520
+ tracingChannel2,
16521
+ states,
16522
+ (event) => {
16523
+ const params = event.arguments[0];
16524
+ const input = serializeInput(params);
16525
+ const metadata = extractMetadata(params);
16526
+ const span = startSpan({
16527
+ name: "generate_content",
16528
+ spanAttributes: {
16529
+ type: "llm" /* LLM */
16530
+ },
16531
+ event: createWrapperParityEvent({ input, metadata })
16532
+ });
16533
+ return {
16534
+ span,
16535
+ startTime: getCurrentUnixTimestamp()
16536
+ };
16537
+ }
15962
16538
  );
15963
- this.unsubscribers.push(
15964
- traceStreamingChannel(googleGenAIChannels.generateContentStream, {
15965
- name: "google-genai.generateContentStream",
15966
- type: "llm" /* LLM */,
15967
- extractInput: ([params]) => {
16539
+ const handlers = {
16540
+ start: (event) => {
16541
+ ensureSpanState(states, event, () => {
16542
+ const params = event.arguments[0];
15968
16543
  const input = serializeInput(params);
15969
16544
  const metadata = extractMetadata(params);
16545
+ const span = startSpan({
16546
+ name: "generate_content",
16547
+ spanAttributes: {
16548
+ type: "llm" /* LLM */
16549
+ },
16550
+ event: createWrapperParityEvent({ input, metadata })
16551
+ });
15970
16552
  return {
15971
- input,
15972
- metadata: { ...metadata, provider: "google-genai" }
16553
+ span,
16554
+ startTime: getCurrentUnixTimestamp()
15973
16555
  };
15974
- },
15975
- extractOutput: (result) => {
15976
- return result;
15977
- },
15978
- extractMetrics: () => {
15979
- return {};
15980
- },
15981
- aggregateChunks: (chunks, _result, _endEvent, startTime) => {
15982
- return aggregateGenerateContentChunks(chunks, startTime);
16556
+ });
16557
+ },
16558
+ asyncEnd: (event) => {
16559
+ const spanState = states.get(event);
16560
+ if (!spanState) {
16561
+ return;
15983
16562
  }
15984
- })
15985
- );
16563
+ try {
16564
+ spanState.span.log({
16565
+ metrics: cleanMetrics(
16566
+ extractGenerateContentMetrics(
16567
+ event.result,
16568
+ spanState.startTime
16569
+ )
16570
+ ),
16571
+ output: event.result
16572
+ });
16573
+ } finally {
16574
+ spanState.span.end();
16575
+ states.delete(event);
16576
+ }
16577
+ },
16578
+ error: (event) => {
16579
+ logErrorAndEndSpan(states, event);
16580
+ }
16581
+ };
16582
+ tracingChannel2.subscribe(handlers);
16583
+ this.unsubscribers.push(() => {
16584
+ unbindCurrentSpanStore?.();
16585
+ tracingChannel2.unsubscribe(handlers);
16586
+ });
16587
+ }
16588
+ subscribeToGenerateContentStreamChannel() {
16589
+ const tracingChannel2 = googleGenAIChannels.generateContentStream.tracingChannel();
16590
+ const handlers = {
16591
+ start: (event) => {
16592
+ const streamEvent = event;
16593
+ const params = event.arguments[0];
16594
+ streamEvent.googleGenAIInput = serializeInput(params);
16595
+ streamEvent.googleGenAIMetadata = extractMetadata(params);
16596
+ },
16597
+ asyncEnd: (event) => {
16598
+ const streamEvent = event;
16599
+ patchGoogleGenAIStreamingResult({
16600
+ input: streamEvent.googleGenAIInput,
16601
+ metadata: streamEvent.googleGenAIMetadata,
16602
+ result: streamEvent.result
16603
+ });
16604
+ },
16605
+ error: () => {
16606
+ }
16607
+ };
16608
+ tracingChannel2.subscribe(handlers);
16609
+ this.unsubscribers.push(() => {
16610
+ tracingChannel2.unsubscribe(handlers);
16611
+ });
15986
16612
  }
15987
16613
  };
16614
+ function ensureSpanState(states, event, create) {
16615
+ const existing = states.get(event);
16616
+ if (existing) {
16617
+ return existing;
16618
+ }
16619
+ const created = create();
16620
+ states.set(event, created);
16621
+ return created;
16622
+ }
16623
+ function bindCurrentSpanStoreToStart2(tracingChannel2, states, create) {
16624
+ const state = _internalGetGlobalState();
16625
+ const startChannel = tracingChannel2.start;
16626
+ const currentSpanStore = state?.contextManager ? state.contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
16627
+ if (!startChannel?.bindStore || !currentSpanStore) {
16628
+ return void 0;
16629
+ }
16630
+ startChannel.bindStore(
16631
+ currentSpanStore,
16632
+ (event) => ensureSpanState(
16633
+ states,
16634
+ event,
16635
+ () => create(event)
16636
+ ).span
16637
+ );
16638
+ return () => {
16639
+ startChannel.unbindStore?.(currentSpanStore);
16640
+ };
16641
+ }
16642
+ function logErrorAndEndSpan(states, event) {
16643
+ const spanState = states.get(event);
16644
+ if (!spanState) {
16645
+ return;
16646
+ }
16647
+ spanState.span.log({
16648
+ error: event.error.message
16649
+ });
16650
+ spanState.span.end();
16651
+ states.delete(event);
16652
+ }
16653
+ function patchGoogleGenAIStreamingResult(args) {
16654
+ const { input, metadata, result } = args;
16655
+ if (!input || !metadata || !result || typeof result !== "object" || typeof result.next !== "function") {
16656
+ return false;
16657
+ }
16658
+ const chunks = [];
16659
+ let firstTokenTime = null;
16660
+ let finalized = false;
16661
+ let span = null;
16662
+ let startTime = null;
16663
+ const ensureSpan = () => {
16664
+ if (!span) {
16665
+ span = startSpan({
16666
+ name: "generate_content_stream",
16667
+ spanAttributes: {
16668
+ type: "llm" /* LLM */
16669
+ },
16670
+ event: {
16671
+ input,
16672
+ metadata
16673
+ }
16674
+ });
16675
+ startTime = getCurrentUnixTimestamp();
16676
+ }
16677
+ return span;
16678
+ };
16679
+ const finalize = (options) => {
16680
+ if (finalized || !span) {
16681
+ return;
16682
+ }
16683
+ finalized = true;
16684
+ if (options.result) {
16685
+ const { end, ...metricsWithoutEnd } = options.result.metrics;
16686
+ span.log({
16687
+ metrics: cleanMetrics(metricsWithoutEnd),
16688
+ output: options.result.aggregated
16689
+ });
16690
+ span.end(typeof end === "number" ? { endTime: end } : void 0);
16691
+ return;
16692
+ }
16693
+ if (options.error !== void 0) {
16694
+ span.log({
16695
+ error: options.error instanceof Error ? options.error.message : String(options.error)
16696
+ });
16697
+ }
16698
+ span.end();
16699
+ };
16700
+ const patchIterator = (iterator) => {
16701
+ if (typeof iterator !== "object" || iterator === null || "__braintrustGoogleGenAIPatched" in iterator) {
16702
+ return iterator;
16703
+ }
16704
+ const iteratorRecord = iterator;
16705
+ const originalNext = typeof iteratorRecord.next === "function" ? iteratorRecord.next.bind(iterator) : void 0;
16706
+ const originalReturn = typeof iteratorRecord.return === "function" ? iteratorRecord.return.bind(iterator) : void 0;
16707
+ const originalThrow = typeof iteratorRecord.throw === "function" ? iteratorRecord.throw.bind(iterator) : void 0;
16708
+ const asyncIteratorMethod = iteratorRecord[Symbol.asyncIterator];
16709
+ const originalAsyncIterator = typeof asyncIteratorMethod === "function" ? asyncIteratorMethod.bind(iterator) : void 0;
16710
+ Object.defineProperty(iteratorRecord, "__braintrustGoogleGenAIPatched", {
16711
+ configurable: true,
16712
+ enumerable: false,
16713
+ value: true,
16714
+ writable: false
16715
+ });
16716
+ if (originalNext) {
16717
+ iteratorRecord.next = async (...nextArgs) => {
16718
+ ensureSpan();
16719
+ try {
16720
+ const nextResult = await originalNext(
16721
+ ...nextArgs
16722
+ );
16723
+ if (!nextResult.done && nextResult.value) {
16724
+ if (firstTokenTime === null) {
16725
+ firstTokenTime = getCurrentUnixTimestamp();
16726
+ }
16727
+ chunks.push(nextResult.value);
16728
+ }
16729
+ if (nextResult.done && startTime !== null) {
16730
+ finalize({
16731
+ result: aggregateGenerateContentChunks(
16732
+ chunks,
16733
+ startTime,
16734
+ firstTokenTime
16735
+ )
16736
+ });
16737
+ }
16738
+ return nextResult;
16739
+ } catch (error2) {
16740
+ finalize({ error: error2 });
16741
+ throw error2;
16742
+ }
16743
+ };
16744
+ }
16745
+ if (originalReturn) {
16746
+ iteratorRecord.return = async (...returnArgs) => {
16747
+ ensureSpan();
16748
+ try {
16749
+ return await originalReturn(
16750
+ ...returnArgs
16751
+ );
16752
+ } finally {
16753
+ if (startTime !== null) {
16754
+ finalize({
16755
+ result: chunks.length > 0 ? aggregateGenerateContentChunks(
16756
+ chunks,
16757
+ startTime,
16758
+ firstTokenTime
16759
+ ) : void 0
16760
+ });
16761
+ } else {
16762
+ finalize({});
16763
+ }
16764
+ }
16765
+ };
16766
+ }
16767
+ if (originalThrow) {
16768
+ iteratorRecord.throw = async (...throwArgs) => {
16769
+ ensureSpan();
16770
+ try {
16771
+ return await originalThrow(
16772
+ ...throwArgs
16773
+ );
16774
+ } catch (error2) {
16775
+ finalize({ error: error2 });
16776
+ throw error2;
16777
+ }
16778
+ };
16779
+ }
16780
+ iteratorRecord[Symbol.asyncIterator] = () => {
16781
+ const asyncIterator = originalAsyncIterator ? originalAsyncIterator() : iterator;
16782
+ return patchIterator(asyncIterator);
16783
+ };
16784
+ return iterator;
16785
+ };
16786
+ patchIterator(result);
16787
+ return true;
16788
+ }
15988
16789
  function serializeInput(params) {
15989
16790
  const input = {
15990
16791
  model: params.model,
@@ -15993,11 +16794,13 @@ function serializeInput(params) {
15993
16794
  if (params.config) {
15994
16795
  const config3 = tryToDict(params.config);
15995
16796
  if (config3) {
15996
- const tools = serializeTools(params);
15997
- if (tools) {
15998
- config3.tools = tools;
15999
- }
16000
- input.config = config3;
16797
+ const filteredConfig = {};
16798
+ Object.keys(config3).forEach((key) => {
16799
+ if (key !== "tools") {
16800
+ filteredConfig[key] = config3[key];
16801
+ }
16802
+ });
16803
+ input.config = filteredConfig;
16001
16804
  }
16002
16805
  }
16003
16806
  return input;
@@ -16084,12 +16887,18 @@ function extractMetadata(params) {
16084
16887
  });
16085
16888
  }
16086
16889
  }
16890
+ const tools = serializeTools(params);
16891
+ if (tools) {
16892
+ metadata.tools = tools;
16893
+ }
16087
16894
  return metadata;
16088
16895
  }
16089
16896
  function extractGenerateContentMetrics(response, startTime) {
16090
16897
  const metrics = {};
16091
- if (startTime) {
16898
+ if (startTime !== void 0) {
16092
16899
  const end = getCurrentUnixTimestamp();
16900
+ metrics.start = startTime;
16901
+ metrics.end = end;
16093
16902
  metrics.duration = end - startTime;
16094
16903
  }
16095
16904
  if (response?.usageMetadata) {
@@ -16114,19 +16923,18 @@ function populateUsageMetrics(metrics, usage) {
16114
16923
  metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
16115
16924
  }
16116
16925
  }
16117
- function aggregateGenerateContentChunks(chunks, startTime) {
16118
- const metrics = {};
16119
- if (startTime !== void 0) {
16120
- const end = getCurrentUnixTimestamp();
16121
- metrics.duration = end - startTime;
16122
- }
16123
- let firstTokenTime = null;
16124
- if (chunks.length > 0 && firstTokenTime === null && startTime !== void 0) {
16125
- firstTokenTime = getCurrentUnixTimestamp();
16926
+ function aggregateGenerateContentChunks(chunks, startTime, firstTokenTime) {
16927
+ const end = getCurrentUnixTimestamp();
16928
+ const metrics = {
16929
+ start: startTime,
16930
+ end,
16931
+ duration: end - startTime
16932
+ };
16933
+ if (firstTokenTime !== null) {
16126
16934
  metrics.time_to_first_token = firstTokenTime - startTime;
16127
16935
  }
16128
16936
  if (chunks.length === 0) {
16129
- return { output: {}, metrics };
16937
+ return { aggregated: {}, metrics };
16130
16938
  }
16131
16939
  let text = "";
16132
16940
  let thoughtText = "";
@@ -16162,7 +16970,7 @@ function aggregateGenerateContentChunks(chunks, startTime) {
16162
16970
  }
16163
16971
  }
16164
16972
  }
16165
- const output = {};
16973
+ const aggregated = {};
16166
16974
  const parts = [];
16167
16975
  if (thoughtText) {
16168
16976
  parts.push({ text: thoughtText, thought: true });
@@ -16188,16 +16996,25 @@ function aggregateGenerateContentChunks(chunks, startTime) {
16188
16996
  }
16189
16997
  candidates.push(candidateDict);
16190
16998
  }
16191
- output.candidates = candidates;
16999
+ aggregated.candidates = candidates;
16192
17000
  }
16193
17001
  if (usageMetadata) {
16194
- output.usageMetadata = usageMetadata;
17002
+ aggregated.usageMetadata = usageMetadata;
16195
17003
  populateUsageMetrics(metrics, usageMetadata);
16196
17004
  }
16197
17005
  if (text) {
16198
- output.text = text;
17006
+ aggregated.text = text;
16199
17007
  }
16200
- return { output, metrics };
17008
+ return { aggregated, metrics };
17009
+ }
17010
+ function cleanMetrics(metrics) {
17011
+ const cleaned = {};
17012
+ for (const [key, value] of Object.entries(metrics)) {
17013
+ if (value !== null && value !== void 0) {
17014
+ cleaned[key] = value;
17015
+ }
17016
+ }
17017
+ return cleaned;
16201
17018
  }
16202
17019
  function tryToDict(obj) {
16203
17020
  if (obj === null || obj === void 0) {
@@ -16213,54 +17030,1076 @@ function tryToDict(obj) {
16213
17030
  return null;
16214
17031
  }
16215
17032
 
16216
- // src/instrumentation/braintrust-plugin.ts
16217
- var BraintrustPlugin = class extends BasePlugin {
16218
- config;
16219
- openaiPlugin = null;
16220
- anthropicPlugin = null;
16221
- aiSDKPlugin = null;
16222
- claudeAgentSDKPlugin = null;
16223
- googleGenAIPlugin = null;
16224
- constructor(config3 = {}) {
16225
- super();
16226
- this.config = config3;
17033
+ // src/instrumentation/plugins/openrouter-channels.ts
17034
+ var openRouterChannels = defineChannels("@openrouter/sdk", {
17035
+ chatSend: channel({
17036
+ channelName: "chat.send",
17037
+ kind: "async"
17038
+ }),
17039
+ embeddingsGenerate: channel({
17040
+ channelName: "embeddings.generate",
17041
+ kind: "async"
17042
+ }),
17043
+ betaResponsesSend: channel({
17044
+ channelName: "beta.responses.send",
17045
+ kind: "async"
17046
+ }),
17047
+ callModel: channel({
17048
+ channelName: "callModel",
17049
+ kind: "sync-stream"
17050
+ }),
17051
+ toolExecute: channel({
17052
+ channelName: "tool.execute",
17053
+ kind: "async"
17054
+ })
17055
+ });
17056
+
17057
+ // src/openrouter-utils.ts
17058
+ var TOKEN_NAME_MAP2 = {
17059
+ promptTokens: "prompt_tokens",
17060
+ inputTokens: "prompt_tokens",
17061
+ completionTokens: "completion_tokens",
17062
+ outputTokens: "completion_tokens",
17063
+ totalTokens: "tokens",
17064
+ prompt_tokens: "prompt_tokens",
17065
+ input_tokens: "prompt_tokens",
17066
+ completion_tokens: "completion_tokens",
17067
+ output_tokens: "completion_tokens",
17068
+ total_tokens: "tokens"
17069
+ };
17070
+ var TOKEN_DETAIL_PREFIX_MAP = {
17071
+ promptTokensDetails: "prompt",
17072
+ inputTokensDetails: "prompt",
17073
+ completionTokensDetails: "completion",
17074
+ outputTokensDetails: "completion",
17075
+ costDetails: "cost",
17076
+ prompt_tokens_details: "prompt",
17077
+ input_tokens_details: "prompt",
17078
+ completion_tokens_details: "completion",
17079
+ output_tokens_details: "completion",
17080
+ cost_details: "cost"
17081
+ };
17082
+ function camelToSnake(value) {
17083
+ return value.replace(/[A-Z]/g, (match) => `_${match.toLowerCase()}`);
17084
+ }
17085
+ function parseOpenRouterMetricsFromUsage(usage) {
17086
+ if (!isObject(usage)) {
17087
+ return {};
16227
17088
  }
16228
- onEnable() {
16229
- const integrations = this.config.integrations || {};
16230
- if (integrations.openai !== false) {
16231
- this.openaiPlugin = new OpenAIPlugin();
16232
- this.openaiPlugin.enable();
16233
- }
16234
- if (integrations.anthropic !== false) {
16235
- this.anthropicPlugin = new AnthropicPlugin();
16236
- this.anthropicPlugin.enable();
17089
+ const metrics = {};
17090
+ for (const [name, value] of Object.entries(usage)) {
17091
+ if (typeof value === "number") {
17092
+ metrics[TOKEN_NAME_MAP2[name] || camelToSnake(name)] = value;
17093
+ continue;
16237
17094
  }
16238
- if (integrations.aisdk !== false && integrations.vercel !== false) {
16239
- this.aiSDKPlugin = new AISDKPlugin();
16240
- this.aiSDKPlugin.enable();
17095
+ if (!isObject(value)) {
17096
+ continue;
16241
17097
  }
16242
- if (integrations.claudeAgentSDK !== false) {
16243
- this.claudeAgentSDKPlugin = new ClaudeAgentSDKPlugin();
16244
- this.claudeAgentSDKPlugin.enable();
17098
+ const prefix = TOKEN_DETAIL_PREFIX_MAP[name];
17099
+ if (!prefix) {
17100
+ continue;
16245
17101
  }
16246
- if (integrations.googleGenAI !== false && integrations.google !== false) {
16247
- this.googleGenAIPlugin = new GoogleGenAIPlugin();
16248
- this.googleGenAIPlugin.enable();
17102
+ for (const [nestedName, nestedValue] of Object.entries(value)) {
17103
+ if (typeof nestedValue !== "number") {
17104
+ continue;
17105
+ }
17106
+ metrics[`${prefix}_${camelToSnake(nestedName)}`] = nestedValue;
16249
17107
  }
16250
17108
  }
16251
- onDisable() {
16252
- if (this.openaiPlugin) {
16253
- this.openaiPlugin.disable();
16254
- this.openaiPlugin = null;
16255
- }
16256
- if (this.anthropicPlugin) {
16257
- this.anthropicPlugin.disable();
16258
- this.anthropicPlugin = null;
16259
- }
16260
- if (this.aiSDKPlugin) {
16261
- this.aiSDKPlugin.disable();
16262
- this.aiSDKPlugin = null;
16263
- }
17109
+ return metrics;
17110
+ }
17111
+ function extractOpenRouterUsageMetadata(usage) {
17112
+ if (!isObject(usage)) {
17113
+ return void 0;
17114
+ }
17115
+ const metadata = {};
17116
+ if (typeof usage.isByok === "boolean") {
17117
+ metadata.is_byok = usage.isByok;
17118
+ } else if (typeof usage.is_byok === "boolean") {
17119
+ metadata.is_byok = usage.is_byok;
17120
+ }
17121
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
17122
+ }
17123
+
17124
+ // src/openrouter-logging.ts
17125
+ var OMITTED_OPENROUTER_KEYS = /* @__PURE__ */ new Set([
17126
+ "execute",
17127
+ "render",
17128
+ "nextTurnParams",
17129
+ "requireApproval"
17130
+ ]);
17131
+ function parseOpenRouterModelString(model) {
17132
+ if (typeof model !== "string") {
17133
+ return { model };
17134
+ }
17135
+ const slashIndex = model.indexOf("/");
17136
+ if (slashIndex > 0 && slashIndex < model.length - 1) {
17137
+ return {
17138
+ provider: model.substring(0, slashIndex),
17139
+ model: model.substring(slashIndex + 1)
17140
+ };
17141
+ }
17142
+ return { model };
17143
+ }
17144
+ function isZodSchema2(value) {
17145
+ return value != null && typeof value === "object" && "_def" in value && typeof value._def === "object";
17146
+ }
17147
+ function serializeZodSchema2(schema) {
17148
+ try {
17149
+ return zodToJsonSchema(schema);
17150
+ } catch {
17151
+ return {
17152
+ type: "object",
17153
+ description: "Zod schema (conversion failed)"
17154
+ };
17155
+ }
17156
+ }
17157
+ function serializeOpenRouterTool(tool) {
17158
+ if (!isObject(tool)) {
17159
+ return tool;
17160
+ }
17161
+ const serialized = {};
17162
+ for (const [key, value] of Object.entries(tool)) {
17163
+ if (OMITTED_OPENROUTER_KEYS.has(key)) {
17164
+ continue;
17165
+ }
17166
+ if (key === "function" && isObject(value)) {
17167
+ serialized.function = sanitizeOpenRouterLoggedValue(value);
17168
+ continue;
17169
+ }
17170
+ serialized[key] = sanitizeOpenRouterLoggedValue(value);
17171
+ }
17172
+ return serialized;
17173
+ }
17174
+ function serializeOpenRouterToolsForLogging(tools) {
17175
+ if (!Array.isArray(tools)) {
17176
+ return void 0;
17177
+ }
17178
+ return tools.map((tool) => serializeOpenRouterTool(tool));
17179
+ }
17180
+ function sanitizeOpenRouterLoggedValue(value) {
17181
+ if (isZodSchema2(value)) {
17182
+ return serializeZodSchema2(value);
17183
+ }
17184
+ if (typeof value === "function") {
17185
+ return "[Function]";
17186
+ }
17187
+ if (Array.isArray(value)) {
17188
+ return value.map((entry) => sanitizeOpenRouterLoggedValue(entry));
17189
+ }
17190
+ if (!isObject(value)) {
17191
+ return value;
17192
+ }
17193
+ const sanitized = {};
17194
+ for (const [key, entry] of Object.entries(value)) {
17195
+ if (OMITTED_OPENROUTER_KEYS.has(key)) {
17196
+ continue;
17197
+ }
17198
+ if (key === "tools" && Array.isArray(entry)) {
17199
+ sanitized.tools = serializeOpenRouterToolsForLogging(entry);
17200
+ continue;
17201
+ }
17202
+ sanitized[key] = sanitizeOpenRouterLoggedValue(entry);
17203
+ }
17204
+ return sanitized;
17205
+ }
17206
+ function buildOpenRouterMetadata(metadata, httpReferer, xTitle) {
17207
+ const sanitized = sanitizeOpenRouterLoggedValue(metadata);
17208
+ const metadataRecord = isObject(sanitized) ? sanitized : {};
17209
+ const { model, provider: providerRouting, ...rest } = metadataRecord;
17210
+ const normalizedModel = parseOpenRouterModelString(model);
17211
+ return {
17212
+ ...rest,
17213
+ ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
17214
+ ...providerRouting !== void 0 ? { providerRouting } : {},
17215
+ ...httpReferer !== void 0 ? { httpReferer } : {},
17216
+ ...xTitle !== void 0 ? { xTitle } : {},
17217
+ provider: normalizedModel.provider || "openrouter"
17218
+ };
17219
+ }
17220
+ function buildOpenRouterEmbeddingMetadata(metadata, httpReferer, xTitle) {
17221
+ const normalized = buildOpenRouterMetadata(metadata, httpReferer, xTitle);
17222
+ return typeof normalized.model === "string" ? {
17223
+ ...normalized,
17224
+ embedding_model: normalized.model
17225
+ } : normalized;
17226
+ }
17227
+ function extractOpenRouterCallModelInput(request) {
17228
+ return isObject(request) && "input" in request ? sanitizeOpenRouterLoggedValue(request.input) : void 0;
17229
+ }
17230
+ function extractOpenRouterCallModelMetadata(request) {
17231
+ if (!isObject(request)) {
17232
+ return { provider: "openrouter" };
17233
+ }
17234
+ const { input: _input, ...metadata } = request;
17235
+ return buildOpenRouterMetadata(metadata, void 0, void 0);
17236
+ }
17237
+ function extractOpenRouterResponseMetadata(result) {
17238
+ if (!isObject(result)) {
17239
+ return void 0;
17240
+ }
17241
+ const { output: _output, data: _data, usage, ...metadata } = result;
17242
+ const sanitized = sanitizeOpenRouterLoggedValue(metadata);
17243
+ const metadataRecord = isObject(sanitized) ? sanitized : {};
17244
+ const { model, provider, ...rest } = metadataRecord;
17245
+ const normalizedModel = parseOpenRouterModelString(model);
17246
+ const normalizedProvider = (typeof provider === "string" ? provider : void 0) || normalizedModel.provider;
17247
+ const usageMetadata = extractOpenRouterUsageMetadata(usage);
17248
+ const combined = {
17249
+ ...rest,
17250
+ ...normalizedModel.model !== void 0 ? { model: normalizedModel.model } : {},
17251
+ ...usageMetadata || {},
17252
+ ...normalizedProvider !== void 0 ? { provider: normalizedProvider } : {}
17253
+ };
17254
+ return Object.keys(combined).length > 0 ? combined : void 0;
17255
+ }
17256
+ function extractOpenRouterResponseOutput(response, fallbackOutput) {
17257
+ if (isObject(response) && "output" in response && response.output !== void 0) {
17258
+ return sanitizeOpenRouterLoggedValue(response.output);
17259
+ }
17260
+ if (fallbackOutput !== void 0) {
17261
+ return sanitizeOpenRouterLoggedValue(fallbackOutput);
17262
+ }
17263
+ return void 0;
17264
+ }
17265
+
17266
+ // src/openrouter-tool-wrapping.ts
17267
+ var OPENROUTER_WRAPPED_TOOL = Symbol("braintrust.openrouter.wrappedTool");
17268
+ var OPENROUTER_WRAPPED_CALL_MODEL_RESULT = Symbol(
17269
+ "braintrust.openrouter.wrappedCallModelResult"
17270
+ );
17271
+ var OPENROUTER_CALL_MODEL_STREAM_METHODS = [
17272
+ "getFullResponsesStream",
17273
+ "getItemsStream",
17274
+ "getNewMessagesStream",
17275
+ "getReasoningStream",
17276
+ "getTextStream",
17277
+ "getToolCallsStream",
17278
+ "getToolStream"
17279
+ ];
17280
+ var OPENROUTER_CALL_MODEL_CONTEXT_METHODS = [
17281
+ "cancel",
17282
+ "getPendingToolCalls",
17283
+ "getState",
17284
+ "getToolCalls",
17285
+ "requiresApproval"
17286
+ ];
17287
+ function patchOpenRouterCallModelRequestTools(request) {
17288
+ if (!Array.isArray(request.tools) || request.tools.length === 0) {
17289
+ return void 0;
17290
+ }
17291
+ const originalTools = request.tools;
17292
+ const wrappedTools = originalTools.map((tool) => wrapOpenRouterTool(tool));
17293
+ const didPatch = wrappedTools.some(
17294
+ (tool, index) => tool !== originalTools[index]
17295
+ );
17296
+ if (!didPatch) {
17297
+ return void 0;
17298
+ }
17299
+ request.tools = wrappedTools;
17300
+ return () => {
17301
+ request.tools = originalTools;
17302
+ };
17303
+ }
17304
+ function patchOpenRouterCallModelResult(span, result, request) {
17305
+ if (!isObject(result) || isWrappedCallModelResult(result)) {
17306
+ return false;
17307
+ }
17308
+ const resultLike = result;
17309
+ const hasInstrumentableMethod = typeof resultLike.getResponse === "function" || typeof resultLike.getText === "function" || OPENROUTER_CALL_MODEL_STREAM_METHODS.some(
17310
+ (methodName) => typeof resultLike[methodName] === "function"
17311
+ );
17312
+ if (!hasInstrumentableMethod) {
17313
+ return false;
17314
+ }
17315
+ Object.defineProperty(resultLike, OPENROUTER_WRAPPED_CALL_MODEL_RESULT, {
17316
+ value: true,
17317
+ enumerable: false,
17318
+ configurable: false
17319
+ });
17320
+ const originalGetResponse = typeof resultLike.getResponse === "function" ? resultLike.getResponse.bind(resultLike) : void 0;
17321
+ const originalGetInitialResponse = typeof resultLike.getInitialResponse === "function" ? resultLike.getInitialResponse.bind(resultLike) : void 0;
17322
+ const originalMakeFollowupRequest = typeof resultLike.makeFollowupRequest === "function" ? resultLike.makeFollowupRequest.bind(resultLike) : void 0;
17323
+ let ended = false;
17324
+ let tracedTurnCount = 0;
17325
+ const endSpanWithResult = async (response, fallbackOutput) => {
17326
+ if (ended) {
17327
+ return;
17328
+ }
17329
+ ended = true;
17330
+ const finalResponse = getFinalOpenRouterCallModelResponse(
17331
+ resultLike,
17332
+ response
17333
+ );
17334
+ if (finalResponse) {
17335
+ const rounds = getOpenRouterCallModelRounds(resultLike);
17336
+ const metadata = extractOpenRouterCallModelResultMetadata(
17337
+ finalResponse,
17338
+ rounds.length + 1
17339
+ );
17340
+ span.log({
17341
+ output: extractOpenRouterResponseOutput(finalResponse, fallbackOutput),
17342
+ ...metadata ? { metadata } : {},
17343
+ metrics: aggregateOpenRouterCallModelMetrics(rounds, finalResponse)
17344
+ });
17345
+ span.end();
17346
+ return;
17347
+ }
17348
+ if (fallbackOutput !== void 0) {
17349
+ span.log({
17350
+ output: fallbackOutput
17351
+ });
17352
+ }
17353
+ span.end();
17354
+ };
17355
+ const endSpanWithError = (error2) => {
17356
+ if (ended) {
17357
+ return;
17358
+ }
17359
+ ended = true;
17360
+ span.log({
17361
+ error: normalizeError(error2).message
17362
+ });
17363
+ span.end();
17364
+ };
17365
+ const finalizeFromResponse = async (fallbackOutput) => {
17366
+ if (!originalGetResponse) {
17367
+ await endSpanWithResult(void 0, fallbackOutput);
17368
+ return;
17369
+ }
17370
+ try {
17371
+ await endSpanWithResult(await originalGetResponse(), fallbackOutput);
17372
+ } catch {
17373
+ await endSpanWithResult(void 0, fallbackOutput);
17374
+ }
17375
+ };
17376
+ if (originalGetResponse) {
17377
+ resultLike.getResponse = async (...args) => {
17378
+ return await withCurrent(span, async () => {
17379
+ try {
17380
+ const response = await originalGetResponse(...args);
17381
+ await endSpanWithResult(response);
17382
+ return response;
17383
+ } catch (error2) {
17384
+ endSpanWithError(error2);
17385
+ throw error2;
17386
+ }
17387
+ });
17388
+ };
17389
+ }
17390
+ if (typeof resultLike.getText === "function") {
17391
+ const originalGetText = resultLike.getText.bind(resultLike);
17392
+ resultLike.getText = async (...args) => {
17393
+ return await withCurrent(span, async () => {
17394
+ try {
17395
+ const text = await originalGetText(...args);
17396
+ await finalizeFromResponse(text);
17397
+ return text;
17398
+ } catch (error2) {
17399
+ endSpanWithError(error2);
17400
+ throw error2;
17401
+ }
17402
+ });
17403
+ };
17404
+ }
17405
+ for (const methodName of OPENROUTER_CALL_MODEL_CONTEXT_METHODS) {
17406
+ if (typeof resultLike[methodName] !== "function") {
17407
+ continue;
17408
+ }
17409
+ const originalMethod = resultLike[methodName];
17410
+ resultLike[methodName] = async (...args) => {
17411
+ return await withCurrent(span, async () => {
17412
+ return await originalMethod.apply(resultLike, args);
17413
+ });
17414
+ };
17415
+ }
17416
+ for (const methodName of OPENROUTER_CALL_MODEL_STREAM_METHODS) {
17417
+ if (typeof resultLike[methodName] !== "function") {
17418
+ continue;
17419
+ }
17420
+ const originalMethod = resultLike[methodName];
17421
+ resultLike[methodName] = (...args) => {
17422
+ const stream = withCurrent(
17423
+ span,
17424
+ () => originalMethod.apply(resultLike, args)
17425
+ );
17426
+ if (!isAsyncIterable4(stream)) {
17427
+ return stream;
17428
+ }
17429
+ return wrapAsyncIterableWithSpan({
17430
+ finalize: finalizeFromResponse,
17431
+ iteratorFactory: () => stream[Symbol.asyncIterator](),
17432
+ onError: endSpanWithError,
17433
+ span
17434
+ });
17435
+ };
17436
+ }
17437
+ if (originalGetInitialResponse) {
17438
+ let initialTurnTraced = false;
17439
+ resultLike.getInitialResponse = async (...args) => {
17440
+ if (initialTurnTraced) {
17441
+ return await withCurrent(span, async () => {
17442
+ return await originalGetInitialResponse(...args);
17443
+ });
17444
+ }
17445
+ initialTurnTraced = true;
17446
+ const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
17447
+ const childSpan = startOpenRouterCallModelTurnSpan({
17448
+ request: resolvedRequest,
17449
+ step: tracedTurnCount + 1,
17450
+ stepType: tracedTurnCount === 0 ? "initial" : "continue"
17451
+ });
17452
+ return await withCurrent(childSpan, async () => {
17453
+ try {
17454
+ const response = await originalGetInitialResponse(...args);
17455
+ tracedTurnCount++;
17456
+ finishOpenRouterCallModelTurnSpan({
17457
+ response,
17458
+ step: tracedTurnCount,
17459
+ stepType: tracedTurnCount === 1 ? "initial" : "continue",
17460
+ span: childSpan
17461
+ });
17462
+ return response;
17463
+ } catch (error2) {
17464
+ childSpan.log({
17465
+ error: normalizeError(error2).message
17466
+ });
17467
+ childSpan.end();
17468
+ throw error2;
17469
+ }
17470
+ });
17471
+ };
17472
+ }
17473
+ if (originalMakeFollowupRequest) {
17474
+ resultLike.makeFollowupRequest = async (...args) => {
17475
+ const currentResponse = args[0];
17476
+ const toolResults = Array.isArray(args[1]) ? args[1] : [];
17477
+ const resolvedRequest = getOpenRouterResolvedRequest(resultLike, request);
17478
+ const followupRequest = buildOpenRouterFollowupRequest(
17479
+ resolvedRequest,
17480
+ currentResponse,
17481
+ toolResults
17482
+ );
17483
+ const childSpan = startOpenRouterCallModelTurnSpan({
17484
+ request: followupRequest,
17485
+ step: tracedTurnCount + 1,
17486
+ stepType: "continue"
17487
+ });
17488
+ return await withCurrent(childSpan, async () => {
17489
+ try {
17490
+ const response = await originalMakeFollowupRequest(...args);
17491
+ tracedTurnCount++;
17492
+ finishOpenRouterCallModelTurnSpan({
17493
+ response,
17494
+ step: tracedTurnCount,
17495
+ stepType: "continue",
17496
+ span: childSpan
17497
+ });
17498
+ return response;
17499
+ } catch (error2) {
17500
+ childSpan.log({
17501
+ error: normalizeError(error2).message
17502
+ });
17503
+ childSpan.end();
17504
+ throw error2;
17505
+ }
17506
+ });
17507
+ };
17508
+ }
17509
+ return true;
17510
+ }
17511
+ function wrapOpenRouterTool(tool) {
17512
+ if (isWrappedTool(tool) || !tool.function || typeof tool.function !== "object" || typeof tool.function.execute !== "function") {
17513
+ return tool;
17514
+ }
17515
+ const toolName = tool.function.name || "tool";
17516
+ const originalExecute = tool.function.execute;
17517
+ const wrappedTool = {
17518
+ ...tool,
17519
+ function: {
17520
+ ...tool.function,
17521
+ execute(...args) {
17522
+ return traceToolExecution({
17523
+ args,
17524
+ execute: () => Reflect.apply(originalExecute, this, args),
17525
+ toolCallId: getToolCallId(args[1]),
17526
+ toolName
17527
+ });
17528
+ }
17529
+ }
17530
+ };
17531
+ Object.defineProperty(wrappedTool, OPENROUTER_WRAPPED_TOOL, {
17532
+ value: true,
17533
+ enumerable: false,
17534
+ configurable: false
17535
+ });
17536
+ return wrappedTool;
17537
+ }
17538
+ function isWrappedTool(tool) {
17539
+ return Boolean(tool[OPENROUTER_WRAPPED_TOOL]);
17540
+ }
17541
+ function isWrappedCallModelResult(value) {
17542
+ return Boolean(
17543
+ isObject(value) && value[OPENROUTER_WRAPPED_CALL_MODEL_RESULT]
17544
+ );
17545
+ }
17546
+ function traceToolExecution(args) {
17547
+ const tracingChannel2 = openRouterChannels.toolExecute.tracingChannel();
17548
+ const input = args.args.length > 0 ? args.args[0] : void 0;
17549
+ const event = {
17550
+ arguments: [input],
17551
+ span_info: {
17552
+ name: args.toolName
17553
+ },
17554
+ toolCallId: args.toolCallId,
17555
+ toolName: args.toolName
17556
+ };
17557
+ tracingChannel2.start.publish(event);
17558
+ try {
17559
+ const result = args.execute();
17560
+ return publishToolResult(tracingChannel2, event, result);
17561
+ } catch (error2) {
17562
+ event.error = normalizeError(error2);
17563
+ tracingChannel2.error.publish(event);
17564
+ throw error2;
17565
+ }
17566
+ }
17567
+ function publishToolResult(tracingChannel2, event, result) {
17568
+ if (isPromiseLike(result)) {
17569
+ return result.then(
17570
+ (resolved) => {
17571
+ event.result = resolved;
17572
+ tracingChannel2.asyncEnd.publish(event);
17573
+ return resolved;
17574
+ },
17575
+ (error2) => {
17576
+ event.error = normalizeError(error2);
17577
+ tracingChannel2.error.publish(event);
17578
+ throw error2;
17579
+ }
17580
+ );
17581
+ }
17582
+ event.result = result;
17583
+ tracingChannel2.asyncEnd.publish(event);
17584
+ return result;
17585
+ }
17586
+ function getToolCallId(context2) {
17587
+ const toolContext = context2;
17588
+ return typeof toolContext?.toolCall?.id === "string" ? toolContext.toolCall.id : void 0;
17589
+ }
17590
+ function extractOpenRouterCallModelResultMetadata(response, turnCount) {
17591
+ const combined = {
17592
+ ...extractOpenRouterResponseMetadata(response) || {},
17593
+ ...turnCount !== void 0 ? { turn_count: turnCount } : {}
17594
+ };
17595
+ return Object.keys(combined).length > 0 ? combined : void 0;
17596
+ }
17597
+ function getFinalOpenRouterCallModelResponse(result, response) {
17598
+ if (isObject(response)) {
17599
+ return response;
17600
+ }
17601
+ return isObject(result.finalResponse) ? result.finalResponse : void 0;
17602
+ }
17603
+ function getOpenRouterCallModelRounds(result) {
17604
+ if (!Array.isArray(result.allToolExecutionRounds)) {
17605
+ return [];
17606
+ }
17607
+ return result.allToolExecutionRounds.filter((round) => isObject(round)).map((round) => ({
17608
+ response: isObject(round.response) ? round.response : void 0,
17609
+ round: typeof round.round === "number" ? round.round : void 0,
17610
+ toolResults: Array.isArray(round.toolResults) ? round.toolResults : []
17611
+ })).filter((round) => round.response !== void 0);
17612
+ }
17613
+ function aggregateOpenRouterCallModelMetrics(rounds, finalResponse) {
17614
+ const metrics = {};
17615
+ const responses = [
17616
+ ...rounds.map((round) => round.response).filter(isObject),
17617
+ finalResponse
17618
+ ];
17619
+ for (const response of responses) {
17620
+ const responseMetrics = parseOpenRouterMetricsFromUsage(response.usage);
17621
+ for (const [name, value] of Object.entries(responseMetrics)) {
17622
+ metrics[name] = (metrics[name] || 0) + value;
17623
+ }
17624
+ }
17625
+ return metrics;
17626
+ }
17627
+ function buildNextOpenRouterCallModelInput(currentInput, response, toolResults) {
17628
+ const normalizedInput = Array.isArray(currentInput) ? [...currentInput] : currentInput === void 0 ? [] : [currentInput];
17629
+ const responseOutput = Array.isArray(response.output) ? response.output : response.output === void 0 ? [] : [response.output];
17630
+ return [...normalizedInput, ...responseOutput, ...toolResults].map(
17631
+ (entry) => sanitizeOpenRouterLoggedValue(entry)
17632
+ );
17633
+ }
17634
+ function startOpenRouterCallModelTurnSpan(args) {
17635
+ const requestRecord = isObject(args.request) ? args.request : void 0;
17636
+ const metadata = requestRecord ? extractOpenRouterCallModelMetadata(requestRecord) : { provider: "openrouter" };
17637
+ if (isObject(metadata) && "tools" in metadata) {
17638
+ delete metadata.tools;
17639
+ }
17640
+ return startSpan({
17641
+ name: "openrouter.beta.responses.send",
17642
+ spanAttributes: {
17643
+ type: "llm" /* LLM */
17644
+ },
17645
+ event: {
17646
+ input: requestRecord ? extractOpenRouterCallModelInput(requestRecord) : void 0,
17647
+ metadata: {
17648
+ ...metadata,
17649
+ step: args.step,
17650
+ step_type: args.stepType
17651
+ }
17652
+ }
17653
+ });
17654
+ }
17655
+ function finishOpenRouterCallModelTurnSpan(args) {
17656
+ if (!isObject(args.response)) {
17657
+ args.span.end();
17658
+ return;
17659
+ }
17660
+ args.span.log({
17661
+ output: extractOpenRouterResponseOutput(args.response),
17662
+ ...extractOpenRouterResponseMetadata(args.response) ? {
17663
+ metadata: {
17664
+ ...extractOpenRouterResponseMetadata(args.response),
17665
+ ...args.step !== void 0 ? { step: args.step } : {},
17666
+ ...args.stepType ? { step_type: args.stepType } : {}
17667
+ }
17668
+ } : {},
17669
+ metrics: parseOpenRouterMetricsFromUsage(args.response.usage)
17670
+ });
17671
+ args.span.end();
17672
+ }
17673
+ function getOpenRouterResolvedRequest(result, request) {
17674
+ if (isObject(result.resolvedRequest)) {
17675
+ return result.resolvedRequest;
17676
+ }
17677
+ return request;
17678
+ }
17679
+ function buildOpenRouterFollowupRequest(request, currentResponse, toolResults) {
17680
+ if (!request) {
17681
+ return void 0;
17682
+ }
17683
+ return {
17684
+ ...request,
17685
+ input: buildNextOpenRouterCallModelInput(
17686
+ extractOpenRouterCallModelInput(request),
17687
+ isObject(currentResponse) ? currentResponse : {},
17688
+ toolResults
17689
+ ),
17690
+ stream: false
17691
+ };
17692
+ }
17693
+ function wrapAsyncIterableWithSpan(args) {
17694
+ return {
17695
+ [Symbol.asyncIterator]() {
17696
+ const iterator = args.iteratorFactory();
17697
+ return {
17698
+ next(value) {
17699
+ return withCurrent(
17700
+ args.span,
17701
+ () => value === void 0 ? iterator.next() : iterator.next(value)
17702
+ ).then(
17703
+ async (result) => {
17704
+ if (result.done) {
17705
+ await args.finalize();
17706
+ }
17707
+ return result;
17708
+ },
17709
+ (error2) => {
17710
+ args.onError(error2);
17711
+ throw error2;
17712
+ }
17713
+ );
17714
+ },
17715
+ return(value) {
17716
+ if (typeof iterator.return !== "function") {
17717
+ return args.finalize().then(() => ({
17718
+ done: true,
17719
+ value
17720
+ }));
17721
+ }
17722
+ return withCurrent(args.span, () => iterator.return(value)).then(
17723
+ async (result) => {
17724
+ await args.finalize();
17725
+ return result;
17726
+ },
17727
+ (error2) => {
17728
+ args.onError(error2);
17729
+ throw error2;
17730
+ }
17731
+ );
17732
+ },
17733
+ throw(error2) {
17734
+ args.onError(error2);
17735
+ if (typeof iterator.throw !== "function") {
17736
+ return Promise.reject(error2);
17737
+ }
17738
+ return withCurrent(args.span, () => iterator.throw(error2));
17739
+ },
17740
+ [Symbol.asyncIterator]() {
17741
+ return this;
17742
+ }
17743
+ };
17744
+ }
17745
+ };
17746
+ }
17747
+ function isAsyncIterable4(value) {
17748
+ return !!value && (typeof value === "object" || typeof value === "function") && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
17749
+ }
17750
+ function isPromiseLike(value) {
17751
+ return !!value && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
17752
+ }
17753
+ function normalizeError(error2) {
17754
+ return error2 instanceof Error ? error2 : new Error(String(error2));
17755
+ }
17756
+
17757
+ // src/instrumentation/plugins/openrouter-plugin.ts
17758
+ var OpenRouterPlugin = class extends BasePlugin {
17759
+ onEnable() {
17760
+ this.subscribeToOpenRouterChannels();
17761
+ }
17762
+ onDisable() {
17763
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
17764
+ }
17765
+ subscribeToOpenRouterChannels() {
17766
+ this.unsubscribers.push(
17767
+ traceStreamingChannel(openRouterChannels.chatSend, {
17768
+ name: "openrouter.chat.send",
17769
+ type: "llm" /* LLM */,
17770
+ extractInput: (args) => {
17771
+ const request = getOpenRouterRequestArg(args);
17772
+ const chatGenerationParams = isObject(request?.chatGenerationParams) ? request.chatGenerationParams : {};
17773
+ const httpReferer = request?.httpReferer;
17774
+ const xTitle = request?.xTitle;
17775
+ const { messages, ...metadata } = chatGenerationParams;
17776
+ return {
17777
+ input: messages,
17778
+ metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
17779
+ };
17780
+ },
17781
+ extractOutput: (result) => {
17782
+ return isObject(result) ? result.choices : void 0;
17783
+ },
17784
+ extractMetrics: (result, startTime) => {
17785
+ const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
17786
+ if (startTime) {
17787
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
17788
+ }
17789
+ return metrics;
17790
+ },
17791
+ aggregateChunks: aggregateOpenRouterChatChunks
17792
+ })
17793
+ );
17794
+ this.unsubscribers.push(
17795
+ traceAsyncChannel(openRouterChannels.embeddingsGenerate, {
17796
+ name: "openrouter.embeddings.generate",
17797
+ type: "llm" /* LLM */,
17798
+ extractInput: (args) => {
17799
+ const request = getOpenRouterRequestArg(args);
17800
+ const requestBody = isObject(request?.requestBody) ? request.requestBody : {};
17801
+ const httpReferer = request?.httpReferer;
17802
+ const xTitle = request?.xTitle;
17803
+ const { input, ...metadata } = requestBody;
17804
+ return {
17805
+ input,
17806
+ metadata: buildOpenRouterEmbeddingMetadata(
17807
+ metadata,
17808
+ httpReferer,
17809
+ xTitle
17810
+ )
17811
+ };
17812
+ },
17813
+ extractOutput: (result) => {
17814
+ if (!isObject(result)) {
17815
+ return void 0;
17816
+ }
17817
+ const embedding = result.data?.[0]?.embedding;
17818
+ return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
17819
+ },
17820
+ extractMetadata: (result) => {
17821
+ if (!isObject(result)) {
17822
+ return void 0;
17823
+ }
17824
+ return extractOpenRouterResponseMetadata(result);
17825
+ },
17826
+ extractMetrics: (result) => {
17827
+ return isObject(result) ? parseOpenRouterMetricsFromUsage(result.usage) : {};
17828
+ }
17829
+ })
17830
+ );
17831
+ this.unsubscribers.push(
17832
+ traceStreamingChannel(openRouterChannels.betaResponsesSend, {
17833
+ name: "openrouter.beta.responses.send",
17834
+ type: "llm" /* LLM */,
17835
+ extractInput: (args) => {
17836
+ const request = getOpenRouterRequestArg(args);
17837
+ const openResponsesRequest = isObject(request?.openResponsesRequest) ? request.openResponsesRequest : {};
17838
+ const httpReferer = request?.httpReferer;
17839
+ const xTitle = request?.xTitle;
17840
+ const { input, ...metadata } = openResponsesRequest;
17841
+ return {
17842
+ input,
17843
+ metadata: buildOpenRouterMetadata(metadata, httpReferer, xTitle)
17844
+ };
17845
+ },
17846
+ extractOutput: (result) => extractOpenRouterResponseOutput(result),
17847
+ extractMetadata: (result) => extractOpenRouterResponseMetadata(result),
17848
+ extractMetrics: (result, startTime) => {
17849
+ const metrics = parseOpenRouterMetricsFromUsage(result?.usage);
17850
+ if (startTime) {
17851
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
17852
+ }
17853
+ return metrics;
17854
+ },
17855
+ aggregateChunks: aggregateOpenRouterResponseStreamEvents
17856
+ })
17857
+ );
17858
+ this.unsubscribers.push(
17859
+ traceSyncStreamChannel(openRouterChannels.callModel, {
17860
+ name: "openrouter.callModel",
17861
+ type: "llm" /* LLM */,
17862
+ extractInput: (args) => {
17863
+ const request = getOpenRouterCallModelRequestArg(args);
17864
+ return {
17865
+ input: request ? extractOpenRouterCallModelInput(request) : void 0,
17866
+ metadata: request ? extractOpenRouterCallModelMetadata(request) : { provider: "openrouter" }
17867
+ };
17868
+ },
17869
+ patchResult: ({ endEvent, result, span }) => {
17870
+ return patchOpenRouterCallModelResult(
17871
+ span,
17872
+ result,
17873
+ getOpenRouterCallModelRequestArg(endEvent.arguments)
17874
+ );
17875
+ }
17876
+ })
17877
+ );
17878
+ this.unsubscribers.push(
17879
+ traceStreamingChannel(openRouterChannels.toolExecute, {
17880
+ name: "openrouter.tool",
17881
+ type: "tool" /* TOOL */,
17882
+ extractInput: (args, event) => ({
17883
+ input: args[0],
17884
+ metadata: {
17885
+ provider: "openrouter",
17886
+ tool_name: event.toolName,
17887
+ ...event.toolCallId ? { tool_call_id: event.toolCallId } : {}
17888
+ }
17889
+ }),
17890
+ extractOutput: (result) => result,
17891
+ extractMetrics: () => ({}),
17892
+ aggregateChunks: (chunks) => ({
17893
+ output: chunks.length > 0 ? chunks[chunks.length - 1] : void 0,
17894
+ metrics: {}
17895
+ })
17896
+ })
17897
+ );
17898
+ const callModelChannel = openRouterChannels.callModel.tracingChannel();
17899
+ const callModelHandlers = {
17900
+ start: (event) => {
17901
+ const request = getOpenRouterCallModelRequestArg(event.arguments);
17902
+ if (!request) {
17903
+ return;
17904
+ }
17905
+ patchOpenRouterCallModelRequestTools(request);
17906
+ }
17907
+ };
17908
+ callModelChannel.subscribe(callModelHandlers);
17909
+ this.unsubscribers.push(() => {
17910
+ callModelChannel.unsubscribe(callModelHandlers);
17911
+ });
17912
+ }
17913
+ };
17914
+ function normalizeArgs(args) {
17915
+ if (Array.isArray(args)) {
17916
+ return args;
17917
+ }
17918
+ if (isArrayLike2(args)) {
17919
+ return Array.from(args);
17920
+ }
17921
+ return [args];
17922
+ }
17923
+ function isArrayLike2(value) {
17924
+ return isObject(value) && "length" in value && typeof value.length === "number" && Number.isInteger(value.length) && value.length >= 0;
17925
+ }
17926
+ function getOpenRouterRequestArg(args) {
17927
+ const normalizedArgs = normalizeArgs(args);
17928
+ const keyedCandidate = normalizedArgs.find(
17929
+ (arg) => isObject(arg) && ("chatGenerationParams" in arg || "requestBody" in arg || "openResponsesRequest" in arg)
17930
+ );
17931
+ if (isObject(keyedCandidate)) {
17932
+ return keyedCandidate;
17933
+ }
17934
+ const firstObjectArg = normalizedArgs.find((arg) => isObject(arg));
17935
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
17936
+ }
17937
+ function getOpenRouterCallModelRequestArg(args) {
17938
+ const firstObjectArg = normalizeArgs(args).find((arg) => isObject(arg));
17939
+ return isObject(firstObjectArg) ? firstObjectArg : void 0;
17940
+ }
17941
+ function aggregateOpenRouterChatChunks(chunks) {
17942
+ let role;
17943
+ let content = "";
17944
+ let toolCalls;
17945
+ let finishReason;
17946
+ let metrics = {};
17947
+ for (const chunk of chunks) {
17948
+ metrics = {
17949
+ ...metrics,
17950
+ ...parseOpenRouterMetricsFromUsage(chunk?.usage)
17951
+ };
17952
+ const choice = chunk?.choices?.[0];
17953
+ const delta = choice?.delta;
17954
+ if (!delta) {
17955
+ if (choice?.finish_reason !== void 0) {
17956
+ finishReason = choice.finish_reason;
17957
+ }
17958
+ continue;
17959
+ }
17960
+ if (!role && delta.role) {
17961
+ role = delta.role;
17962
+ }
17963
+ if (typeof delta.content === "string") {
17964
+ content += delta.content;
17965
+ }
17966
+ const choiceFinishReason = choice?.finishReason ?? choice?.finish_reason ?? void 0;
17967
+ const deltaFinishReason = delta.finishReason ?? delta.finish_reason ?? void 0;
17968
+ if (choiceFinishReason !== void 0) {
17969
+ finishReason = choiceFinishReason;
17970
+ } else if (deltaFinishReason !== void 0) {
17971
+ finishReason = deltaFinishReason;
17972
+ }
17973
+ const toolCallDeltas = Array.isArray(delta.toolCalls) ? delta.toolCalls : Array.isArray(delta.tool_calls) ? delta.tool_calls : void 0;
17974
+ if (!toolCallDeltas) {
17975
+ continue;
17976
+ }
17977
+ for (const toolDelta of toolCallDeltas) {
17978
+ if (!toolDelta?.function) {
17979
+ continue;
17980
+ }
17981
+ const toolIndex = toolDelta.index ?? 0;
17982
+ const existingToolCall = toolCalls?.[toolIndex];
17983
+ if (!existingToolCall || toolDelta.id && existingToolCall.id !== void 0 && existingToolCall.id !== toolDelta.id) {
17984
+ const nextToolCalls = [...toolCalls || []];
17985
+ nextToolCalls[toolIndex] = {
17986
+ index: toolIndex,
17987
+ id: toolDelta.id,
17988
+ type: toolDelta.type,
17989
+ function: {
17990
+ name: toolDelta.function.name,
17991
+ arguments: toolDelta.function.arguments || ""
17992
+ }
17993
+ };
17994
+ toolCalls = nextToolCalls;
17995
+ continue;
17996
+ }
17997
+ const current = existingToolCall;
17998
+ if (toolDelta.id && !current.id) {
17999
+ current.id = toolDelta.id;
18000
+ }
18001
+ if (toolDelta.type && !current.type) {
18002
+ current.type = toolDelta.type;
18003
+ }
18004
+ if (toolDelta.function.name && !current.function.name) {
18005
+ current.function.name = toolDelta.function.name;
18006
+ }
18007
+ current.function.arguments += toolDelta.function.arguments || "";
18008
+ }
18009
+ }
18010
+ return {
18011
+ output: [
18012
+ {
18013
+ index: 0,
18014
+ message: {
18015
+ role,
18016
+ content: content || void 0,
18017
+ ...toolCalls ? { tool_calls: toolCalls } : {}
18018
+ },
18019
+ logprobs: null,
18020
+ finish_reason: finishReason
18021
+ }
18022
+ ],
18023
+ metrics
18024
+ };
18025
+ }
18026
+ function aggregateOpenRouterResponseStreamEvents(chunks) {
18027
+ let finalResponse;
18028
+ for (const chunk of chunks) {
18029
+ const response = chunk?.response;
18030
+ if (!response) {
18031
+ continue;
18032
+ }
18033
+ if (chunk.type === "response.completed" || chunk.type === "response.incomplete" || chunk.type === "response.failed") {
18034
+ finalResponse = response;
18035
+ }
18036
+ }
18037
+ if (!finalResponse) {
18038
+ return {
18039
+ output: void 0,
18040
+ metrics: {}
18041
+ };
18042
+ }
18043
+ return {
18044
+ output: extractOpenRouterResponseOutput(finalResponse),
18045
+ metrics: parseOpenRouterMetricsFromUsage(finalResponse.usage),
18046
+ ...extractOpenRouterResponseMetadata(finalResponse) ? { metadata: extractOpenRouterResponseMetadata(finalResponse) } : {}
18047
+ };
18048
+ }
18049
+
18050
+ // src/instrumentation/braintrust-plugin.ts
18051
+ var BraintrustPlugin = class extends BasePlugin {
18052
+ config;
18053
+ openaiPlugin = null;
18054
+ anthropicPlugin = null;
18055
+ aiSDKPlugin = null;
18056
+ claudeAgentSDKPlugin = null;
18057
+ googleGenAIPlugin = null;
18058
+ openRouterPlugin = null;
18059
+ constructor(config3 = {}) {
18060
+ super();
18061
+ this.config = config3;
18062
+ }
18063
+ onEnable() {
18064
+ const integrations = this.config.integrations || {};
18065
+ if (integrations.openai !== false) {
18066
+ this.openaiPlugin = new OpenAIPlugin();
18067
+ this.openaiPlugin.enable();
18068
+ }
18069
+ if (integrations.anthropic !== false) {
18070
+ this.anthropicPlugin = new AnthropicPlugin();
18071
+ this.anthropicPlugin.enable();
18072
+ }
18073
+ if (integrations.aisdk !== false && integrations.vercel !== false) {
18074
+ this.aiSDKPlugin = new AISDKPlugin();
18075
+ this.aiSDKPlugin.enable();
18076
+ }
18077
+ if (integrations.claudeAgentSDK !== false) {
18078
+ this.claudeAgentSDKPlugin = new ClaudeAgentSDKPlugin();
18079
+ this.claudeAgentSDKPlugin.enable();
18080
+ }
18081
+ if (integrations.googleGenAI !== false && integrations.google !== false) {
18082
+ this.googleGenAIPlugin = new GoogleGenAIPlugin();
18083
+ this.googleGenAIPlugin.enable();
18084
+ }
18085
+ if (integrations.openrouter !== false) {
18086
+ this.openRouterPlugin = new OpenRouterPlugin();
18087
+ this.openRouterPlugin.enable();
18088
+ }
18089
+ }
18090
+ onDisable() {
18091
+ if (this.openaiPlugin) {
18092
+ this.openaiPlugin.disable();
18093
+ this.openaiPlugin = null;
18094
+ }
18095
+ if (this.anthropicPlugin) {
18096
+ this.anthropicPlugin.disable();
18097
+ this.anthropicPlugin = null;
18098
+ }
18099
+ if (this.aiSDKPlugin) {
18100
+ this.aiSDKPlugin.disable();
18101
+ this.aiSDKPlugin = null;
18102
+ }
16264
18103
  if (this.claudeAgentSDKPlugin) {
16265
18104
  this.claudeAgentSDKPlugin.disable();
16266
18105
  this.claudeAgentSDKPlugin = null;
@@ -16269,6 +18108,10 @@ var BraintrustPlugin = class extends BasePlugin {
16269
18108
  this.googleGenAIPlugin.disable();
16270
18109
  this.googleGenAIPlugin = null;
16271
18110
  }
18111
+ if (this.openRouterPlugin) {
18112
+ this.openRouterPlugin.disable();
18113
+ this.openRouterPlugin = null;
18114
+ }
16272
18115
  }
16273
18116
  };
16274
18117
 
@@ -16340,7 +18183,8 @@ var PluginRegistry = class {
16340
18183
  vercel: true,
16341
18184
  aisdk: true,
16342
18185
  google: true,
16343
- claudeAgentSDK: true
18186
+ claudeAgentSDK: true,
18187
+ openrouter: true
16344
18188
  };
16345
18189
  }
16346
18190
  /**
@@ -16370,6 +18214,7 @@ function configureNode() {
16370
18214
  isomorph_default.getCallerLocation = getCallerLocation;
16371
18215
  isomorph_default.newAsyncLocalStorage = () => new import_node_async_hooks.AsyncLocalStorage();
16372
18216
  isomorph_default.newTracingChannel = (nameOrChannels) => diagnostics_channel.tracingChannel(nameOrChannels);
18217
+ patchTracingChannel(diagnostics_channel.tracingChannel);
16373
18218
  isomorph_default.processOn = (event, handler) => {
16374
18219
  process.on(event, handler);
16375
18220
  };