braintrust 3.17.0 → 3.18.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 (52) hide show
  1. package/dev/dist/index.d.mts +13 -0
  2. package/dev/dist/index.d.ts +13 -0
  3. package/dev/dist/index.js +4300 -2199
  4. package/dev/dist/index.mjs +2211 -110
  5. package/dist/apply-auto-instrumentation.js +238 -186
  6. package/dist/apply-auto-instrumentation.mjs +68 -16
  7. package/dist/auto-instrumentations/bundler/esbuild.cjs +90 -15
  8. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  9. package/dist/auto-instrumentations/bundler/next.cjs +90 -15
  10. package/dist/auto-instrumentations/bundler/next.mjs +3 -3
  11. package/dist/auto-instrumentations/bundler/rollup.cjs +90 -15
  12. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  13. package/dist/auto-instrumentations/bundler/vite.cjs +90 -15
  14. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  15. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +89 -14
  16. package/dist/auto-instrumentations/bundler/webpack.cjs +90 -15
  17. package/dist/auto-instrumentations/bundler/webpack.mjs +3 -3
  18. package/dist/auto-instrumentations/{chunk-VXJONZVX.mjs → chunk-GNUEZ2PE.mjs} +13 -3
  19. package/dist/auto-instrumentations/{chunk-CNQ7BUKN.mjs → chunk-MYCHHXOE.mjs} +1 -1
  20. package/dist/auto-instrumentations/{chunk-E5DUYJWK.mjs → chunk-ZYKZEMRT.mjs} +82 -15
  21. package/dist/auto-instrumentations/hook.mjs +102 -15
  22. package/dist/auto-instrumentations/index.cjs +84 -16
  23. package/dist/auto-instrumentations/index.d.mts +3 -1
  24. package/dist/auto-instrumentations/index.d.ts +3 -1
  25. package/dist/auto-instrumentations/index.mjs +5 -3
  26. package/dist/auto-instrumentations/loader/cjs-patch.cjs +1 -1
  27. package/dist/auto-instrumentations/loader/cjs-patch.mjs +1 -1
  28. package/dist/auto-instrumentations/loader/esm-hook.mjs +1 -1
  29. package/dist/browser.d.mts +177 -11
  30. package/dist/browser.d.ts +177 -11
  31. package/dist/browser.js +2296 -104
  32. package/dist/browser.mjs +2455 -263
  33. package/dist/{chunk-VMBQETG3.js → chunk-73IYIIOL.js} +28 -2
  34. package/dist/{chunk-O4ZIWXO3.mjs → chunk-BYFADLEZ.mjs} +27 -1
  35. package/dist/cli.js +2219 -118
  36. package/dist/edge-light.d.mts +1 -1
  37. package/dist/edge-light.d.ts +1 -1
  38. package/dist/edge-light.js +2296 -104
  39. package/dist/edge-light.mjs +2455 -263
  40. package/dist/index.d.mts +177 -11
  41. package/dist/index.d.ts +177 -11
  42. package/dist/index.js +3296 -1128
  43. package/dist/index.mjs +2272 -104
  44. package/dist/instrumentation/index.d.mts +3 -0
  45. package/dist/instrumentation/index.d.ts +3 -0
  46. package/dist/instrumentation/index.js +2171 -105
  47. package/dist/instrumentation/index.mjs +2171 -105
  48. package/dist/workerd.d.mts +1 -1
  49. package/dist/workerd.d.ts +1 -1
  50. package/dist/workerd.js +2296 -104
  51. package/dist/workerd.mjs +2455 -263
  52. package/package.json +10 -10
@@ -7174,6 +7174,10 @@ var Experiment2 = class extends ObjectFetcher {
7174
7174
  return (await this.lazyMetadata.get()).project;
7175
7175
  })();
7176
7176
  }
7177
+ async _getBaseExperimentId() {
7178
+ const baseExperimentId = (await this.lazyMetadata.get()).experiment.fullInfo["base_exp_id"];
7179
+ return typeof baseExperimentId === "string" && baseExperimentId ? baseExperimentId : void 0;
7180
+ }
7177
7181
  parentObjectType() {
7178
7182
  return 1 /* EXPERIMENT */;
7179
7183
  }
@@ -7314,6 +7318,14 @@ var Experiment2 = class extends ObjectFetcher {
7314
7318
  comparisonExperimentId = baseExperiment.id;
7315
7319
  comparisonExperimentName = baseExperiment.name;
7316
7320
  }
7321
+ } else {
7322
+ try {
7323
+ const comparisonExperiment = await state.apiConn().get_json(`v1/experiment/${comparisonExperimentId}`);
7324
+ if (typeof comparisonExperiment["name"] === "string") {
7325
+ comparisonExperimentName = comparisonExperiment["name"];
7326
+ }
7327
+ } catch {
7328
+ }
7317
7329
  }
7318
7330
  try {
7319
7331
  const results = await state.apiConn().get_json(
@@ -8573,6 +8585,64 @@ function isValidChannelName(channelName) {
8573
8585
  return /^braintrust:[^:]+:.+$/.test(channelName);
8574
8586
  }
8575
8587
 
8588
+ // src/instrumentation/auto-instrumentation-suppression.ts
8589
+ var autoInstrumentationSuppressionStore;
8590
+ function suppressionStore() {
8591
+ autoInstrumentationSuppressionStore ??= isomorph_default.newAsyncLocalStorage();
8592
+ return autoInstrumentationSuppressionStore;
8593
+ }
8594
+ function currentFrames() {
8595
+ return suppressionStore().getStore()?.frames ?? [];
8596
+ }
8597
+ function isAutoInstrumentationSuppressed() {
8598
+ const frames = currentFrames();
8599
+ return frames[frames.length - 1]?.mode === "suppress";
8600
+ }
8601
+ function runWithAutoInstrumentationSuppressed(callback) {
8602
+ const frame = {
8603
+ id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-suppress"),
8604
+ mode: "suppress"
8605
+ };
8606
+ return suppressionStore().run(
8607
+ { frames: [...currentFrames(), frame] },
8608
+ callback
8609
+ );
8610
+ }
8611
+ function bindAutoInstrumentationSuppressionToStart(tracingChannel) {
8612
+ const startChannel = tracingChannel.start;
8613
+ if (!startChannel) {
8614
+ return void 0;
8615
+ }
8616
+ const store = suppressionStore();
8617
+ startChannel.bindStore(store, () => ({
8618
+ frames: [
8619
+ ...currentFrames(),
8620
+ {
8621
+ id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-suppress"),
8622
+ mode: "suppress"
8623
+ }
8624
+ ]
8625
+ }));
8626
+ return () => {
8627
+ startChannel.unbindStore(store);
8628
+ };
8629
+ }
8630
+ function enterAutoInstrumentationAllowed() {
8631
+ const frame = {
8632
+ id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-allow"),
8633
+ mode: "allow"
8634
+ };
8635
+ suppressionStore().enterWith({
8636
+ frames: [...currentFrames(), frame]
8637
+ });
8638
+ return () => {
8639
+ const frames = currentFrames().filter(
8640
+ (candidate) => candidate.id !== frame.id
8641
+ );
8642
+ suppressionStore().enterWith(frames.length > 0 ? { frames } : void 0);
8643
+ };
8644
+ }
8645
+
8576
8646
  // src/instrumentation/core/channel-tracing.ts
8577
8647
  function isSyncStreamLike(value) {
8578
8648
  return !!value && typeof value === "object" && typeof value.on === "function";
@@ -8604,16 +8674,33 @@ function startSpanForEvent(config, event, channelName) {
8604
8674
  metadata: mergeInputMetadata(metadata, spanInfoMetadata)
8605
8675
  });
8606
8676
  } catch (error) {
8607
- console.error(`Error extracting input for ${channelName}:`, error);
8677
+ debugLogger.error(`Error extracting input for ${channelName}:`, error);
8608
8678
  }
8609
8679
  return { span, startTime };
8610
8680
  }
8681
+ function shouldTraceEvent(config, event, channelName) {
8682
+ if (!config.shouldTrace) {
8683
+ return true;
8684
+ }
8685
+ try {
8686
+ return config.shouldTrace(event.arguments, event);
8687
+ } catch (error) {
8688
+ debugLogger.error(
8689
+ `Error checking trace predicate for ${channelName}:`,
8690
+ error
8691
+ );
8692
+ return true;
8693
+ }
8694
+ }
8611
8695
  function ensureSpanStateForEvent(states, config, event, channelName) {
8612
8696
  const key = event;
8613
8697
  const existing = states.get(key);
8614
8698
  if (existing) {
8615
8699
  return existing;
8616
8700
  }
8701
+ if (!shouldTraceEvent(config, event, channelName)) {
8702
+ return void 0;
8703
+ }
8617
8704
  const created = startSpanForEvent(config, event, channelName);
8618
8705
  states.set(key, created);
8619
8706
  return created;
@@ -8629,13 +8716,16 @@ function bindCurrentSpanStoreToStart(tracingChannel, states, config, channelName
8629
8716
  startChannel.bindStore(
8630
8717
  currentSpanStore,
8631
8718
  (event) => {
8632
- const span = ensureSpanStateForEvent(
8719
+ if (isAutoInstrumentationSuppressed()) {
8720
+ return currentSpanStore.getStore();
8721
+ }
8722
+ const spanState = ensureSpanStateForEvent(
8633
8723
  states,
8634
8724
  config,
8635
8725
  event,
8636
8726
  channelName
8637
- ).span;
8638
- return contextManager.wrapSpanForStore(span);
8727
+ );
8728
+ return spanState ? contextManager.wrapSpanForStore(spanState.span) : currentSpanStore.getStore();
8639
8729
  }
8640
8730
  );
8641
8731
  return () => {
@@ -8670,7 +8760,10 @@ function runStreamingCompletionHook(args) {
8670
8760
  startTime: args.startTime
8671
8761
  });
8672
8762
  } catch (error) {
8673
- console.error(`Error in onComplete hook for ${args.channelName}:`, error);
8763
+ debugLogger.error(
8764
+ `Error in onComplete hook for ${args.channelName}:`,
8765
+ error
8766
+ );
8674
8767
  }
8675
8768
  }
8676
8769
  function traceAsyncChannel(channel2, config) {
@@ -8685,6 +8778,9 @@ function traceAsyncChannel(channel2, config) {
8685
8778
  );
8686
8779
  const handlers = {
8687
8780
  start: (event) => {
8781
+ if (isAutoInstrumentationSuppressed()) {
8782
+ return;
8783
+ }
8688
8784
  ensureSpanStateForEvent(
8689
8785
  states,
8690
8786
  config,
@@ -8719,7 +8815,7 @@ function traceAsyncChannel(channel2, config) {
8719
8815
  metrics
8720
8816
  });
8721
8817
  } catch (error) {
8722
- console.error(`Error extracting output for ${channelName}:`, error);
8818
+ debugLogger.error(`Error extracting output for ${channelName}:`, error);
8723
8819
  } finally {
8724
8820
  span.end();
8725
8821
  states.delete(event);
@@ -8747,6 +8843,9 @@ function traceStreamingChannel(channel2, config) {
8747
8843
  );
8748
8844
  const handlers = {
8749
8845
  start: (event) => {
8846
+ if (isAutoInstrumentationSuppressed()) {
8847
+ return;
8848
+ }
8750
8849
  ensureSpanStateForEvent(
8751
8850
  states,
8752
8851
  config,
@@ -8818,7 +8917,7 @@ function traceStreamingChannel(channel2, config) {
8818
8917
  metrics
8819
8918
  });
8820
8919
  } catch (error) {
8821
- console.error(
8920
+ debugLogger.error(
8822
8921
  `Error extracting output for ${channelName}:`,
8823
8922
  error
8824
8923
  );
@@ -8878,7 +8977,7 @@ function traceStreamingChannel(channel2, config) {
8878
8977
  metrics
8879
8978
  });
8880
8979
  } catch (error) {
8881
- console.error(`Error extracting output for ${channelName}:`, error);
8980
+ debugLogger.error(`Error extracting output for ${channelName}:`, error);
8882
8981
  } finally {
8883
8982
  span.end();
8884
8983
  states.delete(event);
@@ -8906,6 +9005,9 @@ function traceSyncStreamChannel(channel2, config) {
8906
9005
  );
8907
9006
  const handlers = {
8908
9007
  start: (event) => {
9008
+ if (isAutoInstrumentationSuppressed()) {
9009
+ return;
9010
+ }
8909
9011
  ensureSpanStateForEvent(
8910
9012
  states,
8911
9013
  config,
@@ -8959,7 +9061,7 @@ function traceSyncStreamChannel(channel2, config) {
8959
9061
  });
8960
9062
  }
8961
9063
  } catch (error) {
8962
- console.error(
9064
+ debugLogger.error(
8963
9065
  `Error extracting chatCompletion for ${channelName}:`,
8964
9066
  error
8965
9067
  );
@@ -8983,7 +9085,10 @@ function traceSyncStreamChannel(channel2, config) {
8983
9085
  span.log(extracted);
8984
9086
  }
8985
9087
  } catch (error) {
8986
- console.error(`Error extracting event for ${channelName}:`, error);
9088
+ debugLogger.error(
9089
+ `Error extracting event for ${channelName}:`,
9090
+ error
9091
+ );
8987
9092
  }
8988
9093
  });
8989
9094
  stream.on("end", () => {
@@ -10512,6 +10617,9 @@ var AnthropicPlugin = class extends BasePlugin {
10512
10617
  const states = /* @__PURE__ */ new WeakMap();
10513
10618
  const handlers = {
10514
10619
  start: (event) => {
10620
+ if (isAutoInstrumentationSuppressed()) {
10621
+ return;
10622
+ }
10515
10623
  const params = event.arguments[0] ?? {};
10516
10624
  const span = startSpan({
10517
10625
  name: "anthropic.beta.messages.toolRunner",
@@ -11241,6 +11349,520 @@ function serializeAISDKToolsForLogging(tools) {
11241
11349
  return serialized;
11242
11350
  }
11243
11351
 
11352
+ // src/wrappers/ai-sdk/telemetry.ts
11353
+ function braintrustAISDKTelemetry() {
11354
+ const operations = /* @__PURE__ */ new Map();
11355
+ const modelSpans = /* @__PURE__ */ new Map();
11356
+ const objectSpans = /* @__PURE__ */ new Map();
11357
+ const embedSpans = /* @__PURE__ */ new Map();
11358
+ const rerankSpans = /* @__PURE__ */ new Map();
11359
+ const toolSpans = /* @__PURE__ */ new Map();
11360
+ const runSafely = (name, callback) => {
11361
+ try {
11362
+ callback();
11363
+ } catch (error) {
11364
+ console.error(`Error in Braintrust AI SDK telemetry ${name}:`, error);
11365
+ }
11366
+ };
11367
+ const startChildSpan = (callId, name, type, event) => {
11368
+ const parent = operations.get(callId)?.span;
11369
+ const spanArgs = {
11370
+ name,
11371
+ spanAttributes: { type },
11372
+ ...event ? { event } : {}
11373
+ };
11374
+ const span = parent ? parent.startSpan(spanArgs) : startSpan(spanArgs);
11375
+ const state = operations.get(callId);
11376
+ if (state && type === "llm" /* LLM */) {
11377
+ state.hadModelChild = true;
11378
+ }
11379
+ return span;
11380
+ };
11381
+ return {
11382
+ onStart(event) {
11383
+ runSafely("onStart", () => {
11384
+ const operationName = operationNameFromId(event.operationId);
11385
+ const span = startSpan({
11386
+ name: operationName,
11387
+ spanAttributes: { type: "function" /* FUNCTION */ }
11388
+ });
11389
+ operations.set(event.callId, {
11390
+ hadModelChild: false,
11391
+ operationName,
11392
+ span,
11393
+ startTime: getCurrentUnixTimestamp()
11394
+ });
11395
+ const metadata = metadataFromEvent(event);
11396
+ const logPayload = { metadata };
11397
+ if (shouldRecordInputs(event)) {
11398
+ const { input, outputPromise } = processAISDKCallInput(
11399
+ operationInput(event, operationName)
11400
+ );
11401
+ logPayload.input = input;
11402
+ if (outputPromise && input && typeof input === "object") {
11403
+ outputPromise.then((resolvedData) => {
11404
+ span.log({
11405
+ input: {
11406
+ ...input,
11407
+ ...resolvedData
11408
+ }
11409
+ });
11410
+ }).catch(() => {
11411
+ });
11412
+ }
11413
+ }
11414
+ span.log(logPayload);
11415
+ });
11416
+ },
11417
+ onLanguageModelCallStart(event) {
11418
+ runSafely("onLanguageModelCallStart", () => {
11419
+ const state = operations.get(event.callId);
11420
+ const openSpans = modelSpans.get(event.callId);
11421
+ if (openSpans) {
11422
+ for (const span2 of openSpans) {
11423
+ span2.end();
11424
+ }
11425
+ modelSpans.delete(event.callId);
11426
+ }
11427
+ const span = startChildSpan(
11428
+ event.callId,
11429
+ state?.operationName === "streamText" ? "doStream" : "doGenerate",
11430
+ "llm" /* LLM */,
11431
+ {
11432
+ ...shouldRecordInputs(event) ? {
11433
+ input: processAISDKCallInput(
11434
+ operationInput(
11435
+ event,
11436
+ state?.operationName ?? "generateText"
11437
+ )
11438
+ ).input
11439
+ } : {},
11440
+ metadata: metadataFromEvent(event)
11441
+ }
11442
+ );
11443
+ const spans = modelSpans.get(event.callId) ?? [];
11444
+ spans.push(span);
11445
+ modelSpans.set(event.callId, spans);
11446
+ });
11447
+ },
11448
+ onLanguageModelCallEnd(event) {
11449
+ runSafely("onLanguageModelCallEnd", () => {
11450
+ const span = shiftModelSpan(modelSpans, event.callId);
11451
+ if (!span) {
11452
+ return;
11453
+ }
11454
+ const result = {
11455
+ ...event,
11456
+ response: event.responseId ? { id: event.responseId } : void 0
11457
+ };
11458
+ span.log({
11459
+ ...shouldRecordOutputs(event) ? {
11460
+ output: processAISDKOutput(result, DEFAULT_DENY_OUTPUT_PATHS)
11461
+ } : {},
11462
+ metrics: extractTokenMetrics(result)
11463
+ });
11464
+ span.end();
11465
+ });
11466
+ },
11467
+ onObjectStepStart(event) {
11468
+ runSafely("onObjectStepStart", () => {
11469
+ const state = operations.get(event.callId);
11470
+ const openSpan = objectSpans.get(event.callId);
11471
+ if (openSpan) {
11472
+ openSpan.end();
11473
+ objectSpans.delete(event.callId);
11474
+ }
11475
+ const span = startChildSpan(
11476
+ event.callId,
11477
+ state?.operationName === "streamObject" ? "doStream" : "doGenerate",
11478
+ "llm" /* LLM */,
11479
+ {
11480
+ ...shouldRecordInputs(event) ? {
11481
+ input: {
11482
+ prompt: event.promptMessages
11483
+ }
11484
+ } : {},
11485
+ metadata: metadataFromEvent(event)
11486
+ }
11487
+ );
11488
+ objectSpans.set(event.callId, span);
11489
+ });
11490
+ },
11491
+ onObjectStepFinish(event) {
11492
+ runSafely("onObjectStepFinish", () => {
11493
+ const span = objectSpans.get(event.callId);
11494
+ if (!span) {
11495
+ return;
11496
+ }
11497
+ const result = {
11498
+ ...event,
11499
+ text: event.objectText
11500
+ };
11501
+ span.log({
11502
+ ...shouldRecordOutputs(event) ? {
11503
+ output: processAISDKOutput(result, DEFAULT_DENY_OUTPUT_PATHS)
11504
+ } : {},
11505
+ metrics: extractTokenMetrics(result)
11506
+ });
11507
+ span.end();
11508
+ objectSpans.delete(event.callId);
11509
+ });
11510
+ },
11511
+ onEmbedStart(event) {
11512
+ runSafely("onEmbedStart", () => {
11513
+ const state = operations.get(event.callId);
11514
+ for (const [embedCallId, embedState] of embedSpans) {
11515
+ if (embedState.callId === event.callId && (state?.operationName === "embed" || embedState.values === event.values)) {
11516
+ embedState.span.end();
11517
+ embedSpans.delete(embedCallId);
11518
+ }
11519
+ }
11520
+ const span = startChildSpan(
11521
+ event.callId,
11522
+ "doEmbed",
11523
+ "llm" /* LLM */,
11524
+ {
11525
+ ...shouldRecordInputs(event) ? {
11526
+ input: {
11527
+ values: event.values
11528
+ }
11529
+ } : {},
11530
+ metadata: metadataFromEvent(event)
11531
+ }
11532
+ );
11533
+ embedSpans.set(event.embedCallId, {
11534
+ callId: event.callId,
11535
+ span,
11536
+ values: event.values
11537
+ });
11538
+ });
11539
+ },
11540
+ onEmbedFinish(event) {
11541
+ runSafely("onEmbedFinish", () => {
11542
+ const state = embedSpans.get(event.embedCallId);
11543
+ if (!state) {
11544
+ return;
11545
+ }
11546
+ const result = {
11547
+ ...event,
11548
+ embeddings: event.embeddings
11549
+ };
11550
+ state.span.log({
11551
+ ...shouldRecordOutputs(event) ? {
11552
+ output: processAISDKEmbeddingOutput(
11553
+ result,
11554
+ DEFAULT_DENY_OUTPUT_PATHS
11555
+ )
11556
+ } : {},
11557
+ metrics: extractTokenMetrics(result)
11558
+ });
11559
+ state.span.end();
11560
+ embedSpans.delete(event.embedCallId);
11561
+ });
11562
+ },
11563
+ onRerankStart(event) {
11564
+ runSafely("onRerankStart", () => {
11565
+ const openSpan = rerankSpans.get(event.callId);
11566
+ if (openSpan) {
11567
+ openSpan.end();
11568
+ rerankSpans.delete(event.callId);
11569
+ }
11570
+ const span = startChildSpan(
11571
+ event.callId,
11572
+ "doRerank",
11573
+ "llm" /* LLM */,
11574
+ {
11575
+ ...shouldRecordInputs(event) ? {
11576
+ input: {
11577
+ documents: event.documents,
11578
+ query: event.query,
11579
+ topN: event.topN
11580
+ }
11581
+ } : {},
11582
+ metadata: metadataFromEvent(event)
11583
+ }
11584
+ );
11585
+ rerankSpans.set(event.callId, span);
11586
+ });
11587
+ },
11588
+ onRerankFinish(event) {
11589
+ runSafely("onRerankFinish", () => {
11590
+ const span = rerankSpans.get(event.callId);
11591
+ if (!span) {
11592
+ return;
11593
+ }
11594
+ const result = {
11595
+ ranking: event.ranking?.map((entry) => ({
11596
+ originalIndex: entry.index,
11597
+ score: entry.relevanceScore
11598
+ }))
11599
+ };
11600
+ span.log({
11601
+ ...shouldRecordOutputs(event) ? {
11602
+ output: processAISDKRerankOutput(
11603
+ result,
11604
+ DEFAULT_DENY_OUTPUT_PATHS
11605
+ )
11606
+ } : {}
11607
+ });
11608
+ span.end();
11609
+ rerankSpans.delete(event.callId);
11610
+ });
11611
+ },
11612
+ onToolExecutionStart(event) {
11613
+ runSafely("onToolExecutionStart", () => {
11614
+ const toolCallId = event.toolCall.toolCallId;
11615
+ if (!toolCallId) {
11616
+ return;
11617
+ }
11618
+ const span = startChildSpan(
11619
+ event.callId,
11620
+ event.toolCall.toolName || "tool",
11621
+ "tool" /* TOOL */,
11622
+ {
11623
+ ...shouldRecordInputs(event) ? {
11624
+ input: event.toolCall.input
11625
+ } : {},
11626
+ metadata: {
11627
+ ...createAISDKIntegrationMetadata(),
11628
+ toolCallId,
11629
+ ...typeof event.toolCall.toolName === "string" ? { toolName: event.toolCall.toolName } : {}
11630
+ }
11631
+ }
11632
+ );
11633
+ toolSpans.set(toolCallId, { callId: event.callId, span });
11634
+ });
11635
+ },
11636
+ onToolExecutionEnd(event) {
11637
+ runSafely("onToolExecutionEnd", () => {
11638
+ const toolCallId = event.toolCall.toolCallId;
11639
+ const state = toolCallId ? toolSpans.get(toolCallId) : void 0;
11640
+ if (!toolCallId || !state) {
11641
+ return;
11642
+ }
11643
+ const toolOutput = event.toolOutput;
11644
+ if (toolOutput?.type === "tool-error") {
11645
+ state.span.log({
11646
+ error: toolOutput.error instanceof Error ? toolOutput.error.message : String(toolOutput?.error),
11647
+ metrics: typeof event.durationMs === "number" ? { duration_ms: event.durationMs } : {}
11648
+ });
11649
+ } else {
11650
+ state.span.log({
11651
+ ...shouldRecordOutputs(event) ? { output: toolOutput?.output } : {},
11652
+ metrics: typeof event.durationMs === "number" ? { duration_ms: event.durationMs } : {}
11653
+ });
11654
+ }
11655
+ state.span.end();
11656
+ toolSpans.delete(toolCallId);
11657
+ });
11658
+ },
11659
+ onChunk(event) {
11660
+ runSafely("onChunk", () => {
11661
+ const callId = event.chunk?.callId;
11662
+ if (!callId) {
11663
+ return;
11664
+ }
11665
+ const state = operations.get(callId);
11666
+ if (!state || state.firstChunkTime !== void 0) {
11667
+ return;
11668
+ }
11669
+ state.firstChunkTime = getCurrentUnixTimestamp();
11670
+ });
11671
+ },
11672
+ onFinish(event) {
11673
+ runSafely("onFinish", () => {
11674
+ const state = operations.get(event.callId);
11675
+ if (!state) {
11676
+ return;
11677
+ }
11678
+ const result = finishResult(event, state.operationName);
11679
+ const metrics = state.hadModelChild ? {} : extractTokenMetrics(result);
11680
+ if (state.firstChunkTime !== void 0) {
11681
+ metrics.time_to_first_token = state.firstChunkTime - state.startTime;
11682
+ }
11683
+ state.span.log({
11684
+ ...shouldRecordOutputs(event) ? {
11685
+ output: finishOutput(result, state.operationName)
11686
+ } : {},
11687
+ metrics
11688
+ });
11689
+ state.span.end();
11690
+ operations.delete(event.callId);
11691
+ });
11692
+ },
11693
+ onError(event) {
11694
+ runSafely("onError", () => {
11695
+ const errorEvent = isObject(event) ? event : {};
11696
+ const callId = typeof errorEvent.callId === "string" ? errorEvent.callId : void 0;
11697
+ if (!callId) {
11698
+ return;
11699
+ }
11700
+ const state = operations.get(callId);
11701
+ if (!state) {
11702
+ return;
11703
+ }
11704
+ const error = errorEvent.error ?? event;
11705
+ const openModelSpans = modelSpans.get(callId);
11706
+ if (openModelSpans) {
11707
+ for (const span of openModelSpans) {
11708
+ logError(span, error);
11709
+ span.end();
11710
+ }
11711
+ modelSpans.delete(callId);
11712
+ }
11713
+ const openObjectSpan = objectSpans.get(callId);
11714
+ if (openObjectSpan) {
11715
+ logError(openObjectSpan, error);
11716
+ openObjectSpan.end();
11717
+ objectSpans.delete(callId);
11718
+ }
11719
+ for (const [embedCallId, embedState] of embedSpans) {
11720
+ if (embedState.callId === callId) {
11721
+ logError(embedState.span, error);
11722
+ embedState.span.end();
11723
+ embedSpans.delete(embedCallId);
11724
+ }
11725
+ }
11726
+ const openRerankSpan = rerankSpans.get(callId);
11727
+ if (openRerankSpan) {
11728
+ logError(openRerankSpan, error);
11729
+ openRerankSpan.end();
11730
+ rerankSpans.delete(callId);
11731
+ }
11732
+ for (const [toolCallId, toolState] of toolSpans) {
11733
+ if (toolState.callId === callId) {
11734
+ logError(toolState.span, error);
11735
+ toolState.span.end();
11736
+ toolSpans.delete(toolCallId);
11737
+ }
11738
+ }
11739
+ logError(state.span, error);
11740
+ state.span.end();
11741
+ operations.delete(callId);
11742
+ });
11743
+ },
11744
+ executeTool({ toolCallId, execute }) {
11745
+ const state = toolSpans.get(toolCallId);
11746
+ return state ? withCurrent(state.span, () => execute()) : execute();
11747
+ }
11748
+ };
11749
+ }
11750
+ function shouldRecordInputs(event) {
11751
+ return event.recordInputs !== false;
11752
+ }
11753
+ function shouldRecordOutputs(event) {
11754
+ return event.recordOutputs !== false;
11755
+ }
11756
+ function operationNameFromId(operationId) {
11757
+ return operationId?.startsWith("ai.") ? operationId.slice("ai.".length) : operationId || "ai-sdk";
11758
+ }
11759
+ function modelFromEvent(event) {
11760
+ return event.modelId ? {
11761
+ modelId: event.modelId,
11762
+ ...event.provider ? { provider: event.provider } : {}
11763
+ } : void 0;
11764
+ }
11765
+ function metadataFromEvent(event) {
11766
+ const metadata = createAISDKIntegrationMetadata();
11767
+ const { model, provider } = serializeModelWithProvider(modelFromEvent(event));
11768
+ if (model) {
11769
+ metadata.model = model;
11770
+ }
11771
+ if (provider) {
11772
+ metadata.provider = provider;
11773
+ }
11774
+ if (typeof event.functionId === "string") {
11775
+ metadata.functionId = event.functionId;
11776
+ }
11777
+ return metadata;
11778
+ }
11779
+ function operationInput(event, operationName) {
11780
+ if (operationName === "embed") {
11781
+ return {
11782
+ model: modelFromEvent(event),
11783
+ value: event.value
11784
+ };
11785
+ }
11786
+ if (operationName === "embedMany") {
11787
+ return {
11788
+ model: modelFromEvent(event),
11789
+ values: Array.isArray(event.value) ? event.value ?? [] : void 0
11790
+ };
11791
+ }
11792
+ if (operationName === "rerank") {
11793
+ return {
11794
+ model: modelFromEvent(event),
11795
+ documents: event.documents,
11796
+ query: event.query,
11797
+ topN: event.topN
11798
+ };
11799
+ }
11800
+ return {
11801
+ model: modelFromEvent(event),
11802
+ system: event.system,
11803
+ prompt: event.prompt,
11804
+ messages: event.messages,
11805
+ tools: event.tools,
11806
+ toolChoice: event.toolChoice,
11807
+ activeTools: event.activeTools,
11808
+ output: event.output,
11809
+ schema: event.schema,
11810
+ schemaName: event.schemaName,
11811
+ schemaDescription: event.schemaDescription,
11812
+ maxOutputTokens: event.maxOutputTokens,
11813
+ temperature: event.temperature,
11814
+ topP: event.topP,
11815
+ topK: event.topK,
11816
+ presencePenalty: event.presencePenalty,
11817
+ frequencyPenalty: event.frequencyPenalty,
11818
+ seed: event.seed,
11819
+ maxRetries: event.maxRetries,
11820
+ headers: event.headers,
11821
+ providerOptions: event.providerOptions
11822
+ };
11823
+ }
11824
+ function shiftModelSpan(modelSpans, callId) {
11825
+ const spans = modelSpans.get(callId);
11826
+ const span = spans?.shift();
11827
+ if (spans && spans.length === 0) {
11828
+ modelSpans.delete(callId);
11829
+ }
11830
+ return span;
11831
+ }
11832
+ function finishResult(event, operationName) {
11833
+ if (operationName === "embed") {
11834
+ return {
11835
+ ...event,
11836
+ embedding: event.embedding
11837
+ };
11838
+ }
11839
+ if (operationName === "embedMany") {
11840
+ return {
11841
+ ...event,
11842
+ embeddings: event.embedding
11843
+ };
11844
+ }
11845
+ if (operationName === "rerank") {
11846
+ return event;
11847
+ }
11848
+ return event;
11849
+ }
11850
+ function finishOutput(result, operationName) {
11851
+ if (operationName === "embed" || operationName === "embedMany") {
11852
+ return processAISDKEmbeddingOutput(
11853
+ result,
11854
+ DEFAULT_DENY_OUTPUT_PATHS
11855
+ );
11856
+ }
11857
+ if (operationName === "rerank") {
11858
+ return processAISDKRerankOutput(
11859
+ result,
11860
+ DEFAULT_DENY_OUTPUT_PATHS
11861
+ );
11862
+ }
11863
+ return processAISDKOutput(result, DEFAULT_DENY_OUTPUT_PATHS);
11864
+ }
11865
+
11244
11866
  // src/instrumentation/plugins/ai-sdk-channels.ts
11245
11867
  var aiSDKChannels = defineChannels("ai", {
11246
11868
  generateText: channel({
@@ -11300,6 +11922,10 @@ var aiSDKChannels = defineChannels("ai", {
11300
11922
  toolLoopAgentStream: channel({
11301
11923
  channelName: "ToolLoopAgent.stream",
11302
11924
  kind: "async"
11925
+ }),
11926
+ v7CreateTelemetryDispatcher: channel({
11927
+ channelName: "createTelemetryDispatcher",
11928
+ kind: "sync-stream"
11303
11929
  })
11304
11930
  });
11305
11931
 
@@ -11320,9 +11946,30 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
11320
11946
  ];
11321
11947
  var AUTO_PATCHED_MODEL = /* @__PURE__ */ Symbol.for("braintrust.ai-sdk.auto-patched-model");
11322
11948
  var AUTO_PATCHED_TOOL = /* @__PURE__ */ Symbol.for("braintrust.ai-sdk.auto-patched-tool");
11949
+ var AUTO_PATCHED_V7_TELEMETRY_DISPATCHER = /* @__PURE__ */ Symbol.for(
11950
+ "braintrust.ai-sdk.v7.auto-patched-telemetry-dispatcher"
11951
+ );
11323
11952
  var RUNTIME_DENY_OUTPUT_PATHS = /* @__PURE__ */ Symbol.for(
11324
11953
  "braintrust.ai-sdk.deny-output-paths"
11325
11954
  );
11955
+ var AI_SDK_V7_TELEMETRY_CALLBACKS = [
11956
+ "onStart",
11957
+ "onStepStart",
11958
+ "onLanguageModelCallStart",
11959
+ "onLanguageModelCallEnd",
11960
+ "onToolExecutionStart",
11961
+ "onToolExecutionEnd",
11962
+ "onChunk",
11963
+ "onStepFinish",
11964
+ "onObjectStepStart",
11965
+ "onObjectStepFinish",
11966
+ "onEmbedStart",
11967
+ "onEmbedFinish",
11968
+ "onRerankStart",
11969
+ "onRerankFinish",
11970
+ "onFinish",
11971
+ "onError"
11972
+ ];
11326
11973
  var AISDKPlugin = class extends BasePlugin {
11327
11974
  config;
11328
11975
  constructor(config = {}) {
@@ -11337,6 +11984,7 @@ var AISDKPlugin = class extends BasePlugin {
11337
11984
  }
11338
11985
  subscribeToAISDK() {
11339
11986
  const denyOutputPaths = this.config.denyOutputPaths || DEFAULT_DENY_OUTPUT_PATHS;
11987
+ this.unsubscribers.push(subscribeToAISDKV7TelemetryDispatcher());
11340
11988
  this.unsubscribers.push(
11341
11989
  traceStreamingChannel(aiSDKChannels.generateText, {
11342
11990
  name: "generateText",
@@ -11561,17 +12209,68 @@ var AISDKPlugin = class extends BasePlugin {
11561
12209
  );
11562
12210
  }
11563
12211
  };
11564
- function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
11565
- if (Array.isArray(event?.denyOutputPaths)) {
11566
- return event.denyOutputPaths;
11567
- }
11568
- const firstArgument2 = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
11569
- if (!firstArgument2 || typeof firstArgument2 !== "object") {
11570
- return defaultDenyOutputPaths;
11571
- }
11572
- const runtimeDenyOutputPaths = firstArgument2[RUNTIME_DENY_OUTPUT_PATHS];
11573
- if (Array.isArray(runtimeDenyOutputPaths) && runtimeDenyOutputPaths.every((path) => typeof path === "string")) {
11574
- return runtimeDenyOutputPaths;
12212
+ function subscribeToAISDKV7TelemetryDispatcher() {
12213
+ const channel2 = aiSDKChannels.v7CreateTelemetryDispatcher.tracingChannel();
12214
+ const telemetry = braintrustAISDKTelemetry();
12215
+ const handlers = {
12216
+ end: (event) => {
12217
+ const telemetryOptions = event.arguments?.[0]?.telemetry;
12218
+ if (telemetryOptions?.isEnabled === false) {
12219
+ return;
12220
+ }
12221
+ patchAISDKV7TelemetryDispatcher(event.result, telemetry);
12222
+ }
12223
+ };
12224
+ channel2.subscribe(handlers);
12225
+ return () => {
12226
+ channel2.unsubscribe(handlers);
12227
+ };
12228
+ }
12229
+ function patchAISDKV7TelemetryDispatcher(dispatcher, telemetry) {
12230
+ if (!isObject(dispatcher)) {
12231
+ return;
12232
+ }
12233
+ const dispatcherRecord = dispatcher;
12234
+ if (dispatcherRecord[AUTO_PATCHED_V7_TELEMETRY_DISPATCHER]) {
12235
+ return;
12236
+ }
12237
+ dispatcherRecord[AUTO_PATCHED_V7_TELEMETRY_DISPATCHER] = true;
12238
+ for (const key of AI_SDK_V7_TELEMETRY_CALLBACKS) {
12239
+ const braintrustCallback = telemetry[key];
12240
+ if (typeof braintrustCallback !== "function") {
12241
+ continue;
12242
+ }
12243
+ const existingCallback = dispatcherRecord[key];
12244
+ dispatcherRecord[key] = (event) => {
12245
+ const existingResult = typeof existingCallback === "function" ? existingCallback.call(dispatcher, event) : void 0;
12246
+ const braintrustResult = braintrustCallback.call(telemetry, event);
12247
+ const pending = [existingResult, braintrustResult].filter(isPromiseLike);
12248
+ if (pending.length > 0) {
12249
+ return Promise.allSettled(pending).then(() => void 0);
12250
+ }
12251
+ };
12252
+ }
12253
+ const braintrustExecuteTool = telemetry.executeTool;
12254
+ if (typeof braintrustExecuteTool !== "function") {
12255
+ return;
12256
+ }
12257
+ const existingExecuteTool = dispatcherRecord.executeTool;
12258
+ dispatcherRecord.executeTool = (args) => braintrustExecuteTool.call(telemetry, {
12259
+ ...args,
12260
+ execute: () => typeof existingExecuteTool === "function" ? existingExecuteTool.call(dispatcher, args) : args.execute()
12261
+ });
12262
+ }
12263
+ function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
12264
+ if (Array.isArray(event?.denyOutputPaths)) {
12265
+ return event.denyOutputPaths;
12266
+ }
12267
+ const firstArgument2 = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
12268
+ if (!firstArgument2 || typeof firstArgument2 !== "object") {
12269
+ return defaultDenyOutputPaths;
12270
+ }
12271
+ const runtimeDenyOutputPaths = firstArgument2[RUNTIME_DENY_OUTPUT_PATHS];
12272
+ if (Array.isArray(runtimeDenyOutputPaths) && runtimeDenyOutputPaths.every((path) => typeof path === "string")) {
12273
+ return runtimeDenyOutputPaths;
11575
12274
  }
11576
12275
  return defaultDenyOutputPaths;
11577
12276
  }
@@ -12012,11 +12711,11 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
12012
12711
  metadata: baseMetadata
12013
12712
  }
12014
12713
  });
12714
+ const streamStartTime = getCurrentUnixTimestamp();
12015
12715
  const result = await withCurrent(
12016
12716
  span,
12017
12717
  () => Reflect.apply(originalDoStream, resolvedModel, [options])
12018
12718
  );
12019
- const streamStartTime = getCurrentUnixTimestamp();
12020
12719
  let firstChunkTime;
12021
12720
  const output = {};
12022
12721
  let text = "";
@@ -12025,7 +12724,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
12025
12724
  let object = void 0;
12026
12725
  const transformStream = new TransformStream({
12027
12726
  transform(chunk, controller) {
12028
- if (firstChunkTime === void 0) {
12727
+ if (firstChunkTime === void 0 && isAISDKContentStreamChunk(chunk)) {
12029
12728
  firstChunkTime = getCurrentUnixTimestamp();
12030
12729
  }
12031
12730
  switch (chunk.type) {
@@ -12217,6 +12916,78 @@ function finalizeAISDKChildTracing(event) {
12217
12916
  delete event.__braintrust_ai_sdk_cleanup;
12218
12917
  }
12219
12918
  }
12919
+ function extractAISDKStreamPart(chunk) {
12920
+ if (!isObject(chunk) || !isObject(chunk.part)) {
12921
+ return chunk;
12922
+ }
12923
+ return chunk.part;
12924
+ }
12925
+ function stringContent(value) {
12926
+ return typeof value === "string" && value.length > 0 ? value : void 0;
12927
+ }
12928
+ function rawValueHasAISDKContent(value) {
12929
+ if (value === void 0 || value === null) {
12930
+ return false;
12931
+ }
12932
+ if (typeof value === "string") {
12933
+ return value.length > 0;
12934
+ }
12935
+ if (!isObject(value)) {
12936
+ return true;
12937
+ }
12938
+ const delta = value.delta;
12939
+ if (stringContent(value.content) || stringContent(value.text) || stringContent(value.delta) || stringContent(value.arguments) || stringContent(value.partial_json) || isObject(delta) && (stringContent(delta.content) || stringContent(delta.text) || stringContent(delta.arguments) || stringContent(delta.partial_json) || stringContent(delta.thinking)) || Array.isArray(value.choices) && value.choices.some((choice) => {
12940
+ if (!isObject(choice) || !isObject(choice.delta)) {
12941
+ return false;
12942
+ }
12943
+ const choiceDelta = choice.delta;
12944
+ if (stringContent(choiceDelta.content) || stringContent(choiceDelta.text)) {
12945
+ return true;
12946
+ }
12947
+ if (isObject(choiceDelta.function_call) && stringContent(choiceDelta.function_call.arguments)) {
12948
+ return true;
12949
+ }
12950
+ return Array.isArray(choiceDelta.tool_calls) && choiceDelta.tool_calls.some(
12951
+ (toolCall) => isObject(toolCall) && isObject(toolCall.function) && stringContent(toolCall.function.arguments)
12952
+ );
12953
+ })) {
12954
+ return true;
12955
+ }
12956
+ return false;
12957
+ }
12958
+ function isAISDKContentStreamChunk(chunk) {
12959
+ const part = extractAISDKStreamPart(chunk);
12960
+ if (typeof part === "string") {
12961
+ return part.length > 0;
12962
+ }
12963
+ if (!isObject(part) || typeof part.type !== "string") {
12964
+ return false;
12965
+ }
12966
+ switch (part.type) {
12967
+ case "text-delta":
12968
+ return stringContent(part.textDelta) !== void 0 || stringContent(part.delta) !== void 0 || stringContent(part.text) !== void 0 || stringContent(part.content) !== void 0;
12969
+ case "reasoning-delta":
12970
+ return stringContent(part.delta) !== void 0 || stringContent(part.text) !== void 0 || stringContent(part.content) !== void 0;
12971
+ case "tool-call":
12972
+ case "object":
12973
+ case "file":
12974
+ return true;
12975
+ case "tool-input-delta":
12976
+ case "tool-call-delta":
12977
+ return stringContent(part.argsTextDelta) !== void 0 || stringContent(part.inputTextDelta) !== void 0 || stringContent(part.delta) !== void 0 || stringContent(part.text) !== void 0 || stringContent(part.content) !== void 0;
12978
+ case "raw":
12979
+ return rawValueHasAISDKContent(part.rawValue);
12980
+ default:
12981
+ return false;
12982
+ }
12983
+ }
12984
+ function isAISDKContentAsyncIterableChunk(chunk) {
12985
+ if (isAISDKContentStreamChunk(chunk)) {
12986
+ return true;
12987
+ }
12988
+ const part = extractAISDKStreamPart(chunk);
12989
+ return isObject(part) && typeof part.type !== "string";
12990
+ }
12220
12991
  function patchAISDKStreamingResult(args) {
12221
12992
  const { defaultDenyOutputPaths, endEvent, result, span, startTime } = args;
12222
12993
  if (!result || typeof result !== "object") {
@@ -12229,7 +13000,7 @@ function patchAISDKStreamingResult(args) {
12229
13000
  const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
12230
13001
  new TransformStream({
12231
13002
  transform(chunk, controller) {
12232
- if (firstChunkTime2 === void 0) {
13003
+ if (firstChunkTime2 === void 0 && isAISDKContentStreamChunk(chunk)) {
12233
13004
  firstChunkTime2 = getCurrentUnixTimestamp();
12234
13005
  }
12235
13006
  controller.enqueue(chunk);
@@ -12273,8 +13044,8 @@ function patchAISDKStreamingResult(args) {
12273
13044
  }
12274
13045
  let firstChunkTime;
12275
13046
  const wrappedStream = createPatchedAsyncIterable(streamField.stream, {
12276
- onChunk: () => {
12277
- if (firstChunkTime === void 0) {
13047
+ onChunk: (chunk) => {
13048
+ if (firstChunkTime === void 0 && isAISDKContentAsyncIterableChunk(chunk)) {
12278
13049
  firstChunkTime = getCurrentUnixTimestamp();
12279
13050
  }
12280
13051
  },
@@ -13246,15 +14017,17 @@ function collectLocalMcpServerToolHookNames(serverName, serverConfig) {
13246
14017
 
13247
14018
  // src/instrumentation/plugins/claude-agent-sdk-plugin.ts
13248
14019
  var ROOT_LLM_PARENT_KEY = "__root__";
14020
+ var SUB_AGENT_PROMPT_SOURCE_PRIORITY = {
14021
+ delegation: 0,
14022
+ lifecycle: 1,
14023
+ sidechain: 2
14024
+ };
13249
14025
  function llmParentKey(parentToolUseId) {
13250
14026
  return parentToolUseId ?? ROOT_LLM_PARENT_KEY;
13251
14027
  }
13252
14028
  function isSubAgentDelegationToolName(toolName) {
13253
14029
  return toolName === "Agent" || toolName === "Task";
13254
14030
  }
13255
- function shouldParentToolAsTaskSibling(toolName) {
13256
- return toolName === "Agent" || toolName === "Task" || toolName === "Bash";
13257
- }
13258
14031
  function filterSerializableOptions(options) {
13259
14032
  const allowedKeys = [
13260
14033
  "model",
@@ -13391,26 +14164,39 @@ function extractUsageFromMessage(message) {
13391
14164
  }
13392
14165
  return metrics;
13393
14166
  }
13394
- function buildLLMInput(prompt, conversationHistory, capturedPromptMessages) {
13395
- const promptMessages = [];
13396
- if (typeof prompt === "string") {
13397
- promptMessages.push({ content: prompt, role: "user" });
13398
- } else if (capturedPromptMessages && capturedPromptMessages.length > 0) {
13399
- for (const msg of capturedPromptMessages) {
13400
- const role = msg.message?.role;
13401
- const content = msg.message?.content;
13402
- if (role && content !== void 0) {
13403
- promptMessages.push({ content, role });
13404
- }
13405
- }
13406
- }
14167
+ function buildLLMInput(promptMessages, conversationHistory) {
13407
14168
  const inputParts = [...promptMessages, ...conversationHistory];
13408
14169
  return inputParts.length > 0 ? inputParts : void 0;
13409
14170
  }
14171
+ function conversationMessageFromSDKMessage(message) {
14172
+ const role = message.message?.role;
14173
+ const content = message.message?.content;
14174
+ if (role && content !== void 0) {
14175
+ return { content, role };
14176
+ }
14177
+ return void 0;
14178
+ }
14179
+ function messageContentHasBlockType(message, blockType) {
14180
+ const content = message.message?.content;
14181
+ return Array.isArray(content) && content.some(
14182
+ (block) => typeof block === "object" && block !== null && "type" in block && block.type === blockType
14183
+ );
14184
+ }
14185
+ function buildRootPromptMessages(prompt, capturedPromptMessages) {
14186
+ if (typeof prompt === "string") {
14187
+ return [{ content: prompt, role: "user" }];
14188
+ }
14189
+ if (!capturedPromptMessages || capturedPromptMessages.length === 0) {
14190
+ return [];
14191
+ }
14192
+ return capturedPromptMessages.map(conversationMessageFromSDKMessage).filter(
14193
+ (message) => message !== void 0
14194
+ );
14195
+ }
13410
14196
  function formatCapturedMessages(messages) {
13411
14197
  return messages.length > 0 ? messages : [];
13412
14198
  }
13413
- async function createLLMSpanForMessages(messages, prompt, conversationHistory, options, startTime, capturedPromptMessages, parentSpan, existingSpan) {
14199
+ async function createLLMSpanForMessages(messages, promptMessages, conversationHistory, options, startTime, parentSpan, existingSpan) {
13414
14200
  if (messages.length === 0) {
13415
14201
  return void 0;
13416
14202
  }
@@ -13420,11 +14206,7 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
13420
14206
  }
13421
14207
  const model = lastMessage.message.model || options.model;
13422
14208
  const usage = extractUsageFromMessage(lastMessage);
13423
- const input = buildLLMInput(
13424
- prompt,
13425
- conversationHistory,
13426
- capturedPromptMessages
13427
- );
14209
+ const input = buildLLMInput(promptMessages, conversationHistory);
13428
14210
  const outputs = messages.map(
13429
14211
  (m) => m.message?.content && m.message?.role ? { content: m.message.content, role: m.message.role } : void 0
13430
14212
  ).filter(
@@ -13555,8 +14337,7 @@ function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers,
13555
14337
  },
13556
14338
  name: parsed.displayName,
13557
14339
  parent: await resolveParentSpan(toolUseID, {
13558
- agentId: input.agent_id,
13559
- preferTaskSiblingParent: shouldParentToolAsTaskSibling(parsed.toolName)
14340
+ agentId: input.agent_id
13560
14341
  }),
13561
14342
  spanAttributes: { type: "tool" /* TOOL */ }
13562
14343
  });
@@ -13802,12 +14583,37 @@ function injectTracingHooks(options, resolveParentSpan, activeToolSpans, localTo
13802
14583
  }
13803
14584
  };
13804
14585
  }
14586
+ function setSubAgentPromptMessages(state, parentToolUseId, promptMessages, source) {
14587
+ if (promptMessages.length === 0) {
14588
+ return;
14589
+ }
14590
+ const parentKey = llmParentKey(parentToolUseId);
14591
+ const sourcePriority = SUB_AGENT_PROMPT_SOURCE_PRIORITY[source];
14592
+ const currentPriority = state.promptSourcePriorityByParentKey.get(parentKey) ?? -1;
14593
+ if (sourcePriority > currentPriority) {
14594
+ state.promptMessagesByParentKey.set(parentKey, promptMessages);
14595
+ state.promptSourcePriorityByParentKey.set(parentKey, sourcePriority);
14596
+ }
14597
+ }
14598
+ function getConversationHistory(state, parentKey) {
14599
+ let conversationHistory = state.conversationHistoryByParentKey.get(parentKey);
14600
+ if (!conversationHistory) {
14601
+ conversationHistory = [];
14602
+ state.conversationHistoryByParentKey.set(parentKey, conversationHistory);
14603
+ }
14604
+ return conversationHistory;
14605
+ }
13805
14606
  async function finalizeCurrentMessageGroup(state) {
13806
14607
  if (state.currentMessages.length === 0) {
13807
14608
  return;
13808
14609
  }
13809
14610
  const parentToolUseId = state.currentMessages[0]?.parent_tool_use_id ?? null;
13810
14611
  const parentKey = llmParentKey(parentToolUseId);
14612
+ const conversationHistory = getConversationHistory(state, parentKey);
14613
+ const promptMessages = parentToolUseId ? state.promptMessagesByParentKey.get(parentKey) ?? [] : buildRootPromptMessages(
14614
+ state.originalPrompt,
14615
+ state.capturedPromptMessages
14616
+ );
13811
14617
  let parentSpan = await state.span.export();
13812
14618
  if (parentToolUseId) {
13813
14619
  const subAgentSpan = state.subAgentSpans.get(parentToolUseId);
@@ -13818,11 +14624,10 @@ async function finalizeCurrentMessageGroup(state) {
13818
14624
  const existingLlmSpan = state.activeLlmSpansByParentToolUse.get(parentKey);
13819
14625
  const llmSpanResult = await createLLMSpanForMessages(
13820
14626
  state.currentMessages,
13821
- state.originalPrompt,
13822
- state.finalResults,
14627
+ promptMessages,
14628
+ conversationHistory,
13823
14629
  state.options,
13824
14630
  state.currentMessageStartTime,
13825
- state.capturedPromptMessages,
13826
14631
  parentSpan,
13827
14632
  existingLlmSpan
13828
14633
  );
@@ -13836,6 +14641,7 @@ async function finalizeCurrentMessageGroup(state) {
13836
14641
  state.latestRootLlmParentRef.value = llmSpanResult.spanExport;
13837
14642
  }
13838
14643
  if (llmSpanResult.finalMessage) {
14644
+ conversationHistory.push(llmSpanResult.finalMessage);
13839
14645
  state.finalResults.push(llmSpanResult.finalMessage);
13840
14646
  }
13841
14647
  }
@@ -13863,6 +14669,15 @@ function maybeTrackToolUseContext(state, message) {
13863
14669
  description: getStringProperty(block.input, "description"),
13864
14670
  toolUseId: block.id
13865
14671
  });
14672
+ const prompt = getStringProperty(block.input, "prompt");
14673
+ if (prompt) {
14674
+ setSubAgentPromptMessages(
14675
+ state,
14676
+ block.id,
14677
+ [{ content: prompt, role: "user" }],
14678
+ "delegation"
14679
+ );
14680
+ }
13866
14681
  }
13867
14682
  }
13868
14683
  }
@@ -13981,6 +14796,14 @@ async function maybeHandleTaskLifecycleMessage(state, message) {
13981
14796
  };
13982
14797
  if (message.subtype === "task_started") {
13983
14798
  const prompt = getStringProperty(message, "prompt");
14799
+ if (prompt) {
14800
+ setSubAgentPromptMessages(
14801
+ state,
14802
+ toolUseId,
14803
+ [{ content: prompt, role: "user" }],
14804
+ "lifecycle"
14805
+ );
14806
+ }
13984
14807
  subAgentSpan.log({
13985
14808
  input: prompt,
13986
14809
  metadata
@@ -14031,6 +14854,28 @@ async function handleStreamMessage(state, message) {
14031
14854
  return;
14032
14855
  }
14033
14856
  await maybeStartSubAgentSpan(state, message);
14857
+ const messageParentToolUseId = message.parent_tool_use_id;
14858
+ if (messageParentToolUseId && message.type === "user") {
14859
+ const conversationMessage = conversationMessageFromSDKMessage(message);
14860
+ if (conversationMessage?.role === "user") {
14861
+ await finalizeCurrentMessageGroup(state);
14862
+ const parentKey = llmParentKey(messageParentToolUseId);
14863
+ const conversationHistory = getConversationHistory(state, parentKey);
14864
+ const currentPromptSourcePriority = state.promptSourcePriorityByParentKey.get(parentKey) ?? -1;
14865
+ const canUseAsInitialSidechainPrompt = conversationHistory.length === 0 && currentPromptSourcePriority < SUB_AGENT_PROMPT_SOURCE_PRIORITY.sidechain && !messageContentHasBlockType(message, "tool_result");
14866
+ if (!canUseAsInitialSidechainPrompt) {
14867
+ conversationHistory.push(conversationMessage);
14868
+ return;
14869
+ }
14870
+ setSubAgentPromptMessages(
14871
+ state,
14872
+ messageParentToolUseId,
14873
+ [conversationMessage],
14874
+ "sidechain"
14875
+ );
14876
+ return;
14877
+ }
14878
+ }
14034
14879
  const messageId = message.message?.id;
14035
14880
  if (messageId && messageId !== state.currentMessageId) {
14036
14881
  await finalizeCurrentMessageGroup(state);
@@ -14182,6 +15027,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
14182
15027
  }
14183
15028
  const activeToolSpans = /* @__PURE__ */ new Map();
14184
15029
  const activeLlmSpansByParentToolUse = /* @__PURE__ */ new Map();
15030
+ const conversationHistoryByParentKey = /* @__PURE__ */ new Map();
14185
15031
  const subAgentSpans = /* @__PURE__ */ new Map();
14186
15032
  const endedSubAgentSpans = /* @__PURE__ */ new Set();
14187
15033
  const toolUseToParent = /* @__PURE__ */ new Map();
@@ -14191,6 +15037,8 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
14191
15037
  };
14192
15038
  const subAgentDetailsByToolUseId = /* @__PURE__ */ new Map();
14193
15039
  const taskIdToToolUseId = /* @__PURE__ */ new Map();
15040
+ const promptMessagesByParentKey = /* @__PURE__ */ new Map();
15041
+ const promptSourcePriorityByParentKey = /* @__PURE__ */ new Map();
14194
15042
  const localToolContext = createClaudeLocalToolContext();
14195
15043
  const { hasLocalToolHandlers, localToolHookNames } = prepareLocalToolHandlersInMcpServers(options.mcpServers);
14196
15044
  const skipLocalToolHooks = options[CLAUDE_AGENT_SDK_SKIP_LOCAL_TOOL_HOOKS_OPTION] === true || hasLocalToolHandlers;
@@ -14199,38 +15047,19 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
14199
15047
  const parentToolUseId = trackedParentToolUseId ?? (context?.agentId ? taskIdToToolUseId.get(context.agentId) ?? null : null);
14200
15048
  const parentKey = llmParentKey(parentToolUseId);
14201
15049
  const activeLlmSpan = activeLlmSpansByParentToolUse.get(parentKey);
14202
- if (context?.preferTaskSiblingParent) {
14203
- if (!activeLlmSpan) {
14204
- await ensureActiveLlmSpanForParentToolUse(
14205
- span,
14206
- activeLlmSpansByParentToolUse,
14207
- subAgentDetailsByToolUseId,
14208
- activeToolSpans,
14209
- subAgentSpans,
14210
- parentToolUseId,
14211
- getCurrentUnixTimestamp()
14212
- );
14213
- }
14214
- if (parentToolUseId) {
14215
- const subAgentSpan = await ensureSubAgentSpan(
14216
- subAgentDetailsByToolUseId,
14217
- span,
14218
- activeToolSpans,
14219
- subAgentSpans,
14220
- parentToolUseId
14221
- );
14222
- return subAgentSpan.export();
14223
- }
14224
- return span.export();
14225
- }
14226
- if (activeLlmSpan) {
14227
- return activeLlmSpan.export();
15050
+ const latestLlmParent = parentToolUseId ? latestLlmParentBySubAgentToolUse.get(parentToolUseId) : latestRootLlmParentRef.value;
15051
+ if (!activeLlmSpan && !latestLlmParent) {
15052
+ await ensureActiveLlmSpanForParentToolUse(
15053
+ span,
15054
+ activeLlmSpansByParentToolUse,
15055
+ subAgentDetailsByToolUseId,
15056
+ activeToolSpans,
15057
+ subAgentSpans,
15058
+ parentToolUseId,
15059
+ getCurrentUnixTimestamp()
15060
+ );
14228
15061
  }
14229
15062
  if (parentToolUseId) {
14230
- const parentLlm = latestLlmParentBySubAgentToolUse.get(parentToolUseId);
14231
- if (parentLlm) {
14232
- return parentLlm;
14233
- }
14234
15063
  const subAgentSpan = await ensureSubAgentSpan(
14235
15064
  subAgentDetailsByToolUseId,
14236
15065
  span,
@@ -14240,9 +15069,6 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
14240
15069
  );
14241
15070
  return subAgentSpan.export();
14242
15071
  }
14243
- if (latestRootLlmParentRef.value) {
14244
- return latestRootLlmParentRef.value;
14245
- }
14246
15072
  return span.export();
14247
15073
  };
14248
15074
  localToolContext.resolveLocalToolParent = resolveToolUseParentSpan;
@@ -14263,6 +15089,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
14263
15089
  accumulatedOutputTokens: 0,
14264
15090
  activeLlmSpansByParentToolUse,
14265
15091
  activeToolSpans,
15092
+ conversationHistoryByParentKey,
14266
15093
  capturedPromptMessages,
14267
15094
  currentMessageId: void 0,
14268
15095
  currentMessageStartTime: startTime,
@@ -14273,7 +15100,9 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
14273
15100
  originalPrompt,
14274
15101
  processing: Promise.resolve(),
14275
15102
  promptDone,
15103
+ promptMessagesByParentKey,
14276
15104
  promptStarted: () => promptStarted,
15105
+ promptSourcePriorityByParentKey,
14277
15106
  span,
14278
15107
  subAgentDetailsByToolUseId,
14279
15108
  subAgentSpans,
@@ -15702,6 +16531,10 @@ var googleGenAIChannels = defineChannels("@google/genai", {
15702
16531
  embedContent: channel({
15703
16532
  channelName: "models.embedContent",
15704
16533
  kind: "async"
16534
+ }),
16535
+ interactionsCreate: channel({
16536
+ channelName: "interactions.create",
16537
+ kind: "async"
15705
16538
  })
15706
16539
  });
15707
16540
 
@@ -15729,6 +16562,7 @@ var GoogleGenAIPlugin = class extends BasePlugin {
15729
16562
  this.subscribeToGenerateContentChannel();
15730
16563
  this.subscribeToGenerateContentStreamChannel();
15731
16564
  this.subscribeToEmbedContentChannel();
16565
+ this.subscribeToInteractionsCreateChannel();
15732
16566
  }
15733
16567
  subscribeToGenerateContentChannel() {
15734
16568
  const tracingChannel = googleGenAIChannels.generateContent.tracingChannel();
@@ -15901,7 +16735,30 @@ var GoogleGenAIPlugin = class extends BasePlugin {
15901
16735
  tracingChannel.unsubscribe(handlers);
15902
16736
  });
15903
16737
  }
16738
+ subscribeToInteractionsCreateChannel() {
16739
+ this.unsubscribers.push(
16740
+ traceStreamingChannel(
16741
+ googleGenAIChannels.interactionsCreate,
16742
+ {
16743
+ name: "create_interaction",
16744
+ shouldTrace: ([params]) => !isBackgroundInteractionCreate(params),
16745
+ type: "llm" /* LLM */,
16746
+ extractInput: ([params]) => ({
16747
+ input: serializeInteractionInput(params),
16748
+ metadata: extractInteractionMetadata(params)
16749
+ }),
16750
+ extractOutput: (result) => serializeInteractionValue(result),
16751
+ extractMetadata: (result) => extractInteractionResponseMetadata(result),
16752
+ extractMetrics: (result, startTime) => cleanMetrics3(extractInteractionMetrics(result, startTime)),
16753
+ aggregateChunks: (chunks, _result, _event, startTime) => aggregateInteractionEvents(chunks, startTime)
16754
+ }
16755
+ )
16756
+ );
16757
+ }
15904
16758
  };
16759
+ function isBackgroundInteractionCreate(params) {
16760
+ return tryToDict(params)?.background === true;
16761
+ }
15905
16762
  function ensureSpanState(states, event, create) {
15906
16763
  const existing = states.get(event);
15907
16764
  if (existing) {
@@ -16109,6 +16966,60 @@ function serializeEmbedContentInput(params) {
16109
16966
  }
16110
16967
  return input;
16111
16968
  }
16969
+ function serializeInteractionInput(params) {
16970
+ const input = {
16971
+ input: serializeInteractionValue(params.input)
16972
+ };
16973
+ for (const key of [
16974
+ "model",
16975
+ "agent",
16976
+ "agent_config",
16977
+ "api_version",
16978
+ "background",
16979
+ "environment",
16980
+ "generation_config",
16981
+ "previous_interaction_id",
16982
+ "response_format",
16983
+ "response_mime_type",
16984
+ "response_modalities",
16985
+ "service_tier",
16986
+ "store",
16987
+ "stream",
16988
+ "system_instruction",
16989
+ "webhook_config"
16990
+ ]) {
16991
+ const value = params[key];
16992
+ if (value !== void 0) {
16993
+ input[key] = serializeInteractionValue(value);
16994
+ }
16995
+ }
16996
+ return input;
16997
+ }
16998
+ function extractInteractionMetadata(params) {
16999
+ const metadata = {};
17000
+ for (const key of [
17001
+ "model",
17002
+ "agent",
17003
+ "agent_config",
17004
+ "generation_config",
17005
+ "system_instruction",
17006
+ "response_format",
17007
+ "response_mime_type",
17008
+ "response_modalities",
17009
+ "service_tier"
17010
+ ]) {
17011
+ const value = params[key];
17012
+ if (value !== void 0) {
17013
+ metadata[key] = serializeInteractionValue(value);
17014
+ }
17015
+ }
17016
+ if (Array.isArray(params.tools)) {
17017
+ metadata.tools = params.tools.map(
17018
+ (tool) => serializeInteractionValue(tool)
17019
+ );
17020
+ }
17021
+ return metadata;
17022
+ }
16112
17023
  function serializeContentCollection(contents) {
16113
17024
  if (contents === null || contents === void 0) {
16114
17025
  return null;
@@ -16139,21 +17050,8 @@ function serializePart(part) {
16139
17050
  }
16140
17051
  if (part.inlineData && part.inlineData.data) {
16141
17052
  const { data, mimeType } = part.inlineData;
16142
- if (data instanceof Uint8Array || typeof Buffer !== "undefined" && Buffer.isBuffer(data) || typeof data === "string") {
16143
- const extension = mimeType ? mimeType.split("/")[1] : "bin";
16144
- const filename = `file.${extension}`;
16145
- const buffer = typeof data === "string" ? typeof Buffer !== "undefined" ? Buffer.from(data, "base64") : new Uint8Array(
16146
- atob(data).split("").map((c) => c.charCodeAt(0))
16147
- ) : typeof Buffer !== "undefined" ? Buffer.from(data) : new Uint8Array(data);
16148
- const arrayBuffer = buffer instanceof Uint8Array ? buffer.buffer.slice(
16149
- buffer.byteOffset,
16150
- buffer.byteOffset + buffer.byteLength
16151
- ) : buffer;
16152
- const attachment = new Attachment({
16153
- data: arrayBuffer,
16154
- filename,
16155
- contentType: mimeType || "application/octet-stream"
16156
- });
17053
+ const attachment = createAttachmentFromInlineData(data, mimeType);
17054
+ if (attachment) {
16157
17055
  return {
16158
17056
  image_url: { url: attachment }
16159
17057
  };
@@ -16161,6 +17059,59 @@ function serializePart(part) {
16161
17059
  }
16162
17060
  return part;
16163
17061
  }
17062
+ function serializeInteractionValue(value, seen = /* @__PURE__ */ new WeakSet()) {
17063
+ if (value === null || value === void 0 || typeof value !== "object") {
17064
+ return value;
17065
+ }
17066
+ if (Array.isArray(value)) {
17067
+ return value.map((item) => serializeInteractionValue(item, seen));
17068
+ }
17069
+ const dict = tryToDict(value);
17070
+ if (dict === null || dict === void 0 || typeof dict !== "object") {
17071
+ return dict;
17072
+ }
17073
+ if (Array.isArray(dict)) {
17074
+ return dict.map((item) => serializeInteractionValue(item, seen));
17075
+ }
17076
+ if (seen.has(dict)) {
17077
+ return "[Circular]";
17078
+ }
17079
+ seen.add(dict);
17080
+ try {
17081
+ const serialized = {};
17082
+ const mimeType = "mime_type" in dict && typeof dict.mime_type === "string" ? dict.mime_type : "mimeType" in dict && typeof dict.mimeType === "string" ? dict.mimeType : void 0;
17083
+ const attachment = mimeType && "data" in dict && dict.data !== void 0 ? createAttachmentFromInlineData(dict.data, mimeType) : null;
17084
+ for (const [key, entry] of Object.entries(dict)) {
17085
+ if (key === "data" && attachment) {
17086
+ serialized[key] = attachment;
17087
+ } else {
17088
+ serialized[key] = serializeInteractionValue(entry, seen);
17089
+ }
17090
+ }
17091
+ return serialized;
17092
+ } finally {
17093
+ seen.delete(dict);
17094
+ }
17095
+ }
17096
+ function createAttachmentFromInlineData(data, mimeType) {
17097
+ if (!(data instanceof Uint8Array || typeof Buffer !== "undefined" && Buffer.isBuffer(data) || typeof data === "string")) {
17098
+ return null;
17099
+ }
17100
+ const extension = mimeType ? mimeType.split("/")[1] : "bin";
17101
+ const filename = `file.${extension}`;
17102
+ const buffer = typeof data === "string" ? typeof Buffer !== "undefined" ? Buffer.from(data, "base64") : new Uint8Array(
17103
+ atob(data).split("").map((c) => c.charCodeAt(0))
17104
+ ) : typeof Buffer !== "undefined" ? Buffer.from(data) : new Uint8Array(data);
17105
+ const arrayBuffer = buffer instanceof Uint8Array ? buffer.buffer.slice(
17106
+ buffer.byteOffset,
17107
+ buffer.byteOffset + buffer.byteLength
17108
+ ) : buffer;
17109
+ return new Attachment({
17110
+ data: arrayBuffer,
17111
+ filename,
17112
+ contentType: mimeType || "application/octet-stream"
17113
+ });
17114
+ }
16164
17115
  function serializeGenerateContentTools(params) {
16165
17116
  const config = params.config ? tryToDict(params.config) : null;
16166
17117
  const tools = config?.tools;
@@ -16245,6 +17196,33 @@ function extractEmbedContentMetrics(response, startTime) {
16245
17196
  }
16246
17197
  return metrics;
16247
17198
  }
17199
+ function extractInteractionMetrics(response, startTime) {
17200
+ const metrics = {};
17201
+ if (startTime !== void 0) {
17202
+ const end = getCurrentUnixTimestamp();
17203
+ metrics.start = startTime;
17204
+ metrics.end = end;
17205
+ metrics.duration = end - startTime;
17206
+ }
17207
+ if (response?.usage) {
17208
+ populateInteractionUsageMetrics(metrics, response.usage);
17209
+ }
17210
+ return metrics;
17211
+ }
17212
+ function extractInteractionResponseMetadata(response) {
17213
+ const responseDict = tryToDict(response);
17214
+ if (!responseDict) {
17215
+ return void 0;
17216
+ }
17217
+ const metadata = {};
17218
+ if (typeof responseDict.id === "string") {
17219
+ metadata.interaction_id = responseDict.id;
17220
+ }
17221
+ if (typeof responseDict.status === "string") {
17222
+ metadata.status = responseDict.status;
17223
+ }
17224
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
17225
+ }
16248
17226
  function extractEmbedPromptTokenCount(response) {
16249
17227
  if (!response) {
16250
17228
  return void 0;
@@ -16307,6 +17285,23 @@ function populateUsageMetrics(metrics, usage) {
16307
17285
  metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
16308
17286
  }
16309
17287
  }
17288
+ function populateInteractionUsageMetrics(metrics, usage) {
17289
+ if (typeof usage.total_input_tokens === "number") {
17290
+ metrics.prompt_tokens = usage.total_input_tokens;
17291
+ }
17292
+ if (typeof usage.total_output_tokens === "number") {
17293
+ metrics.completion_tokens = usage.total_output_tokens;
17294
+ }
17295
+ if (typeof usage.total_tokens === "number") {
17296
+ metrics.tokens = usage.total_tokens;
17297
+ }
17298
+ if (typeof usage.total_cached_tokens === "number") {
17299
+ metrics.prompt_cached_tokens = usage.total_cached_tokens;
17300
+ }
17301
+ if (typeof usage.total_thought_tokens === "number") {
17302
+ metrics.completion_reasoning_tokens = usage.total_thought_tokens;
17303
+ }
17304
+ }
16310
17305
  function aggregateGenerateContentChunks(chunks, startTime, firstTokenTime) {
16311
17306
  const end = getCurrentUnixTimestamp();
16312
17307
  const metrics = {
@@ -16404,6 +17399,146 @@ function aggregateGenerateContentChunks(chunks, startTime, firstTokenTime) {
16404
17399
  }
16405
17400
  return { aggregated, metrics };
16406
17401
  }
17402
+ function aggregateInteractionEvents(chunks, startTime) {
17403
+ const end = getCurrentUnixTimestamp();
17404
+ const metrics = {};
17405
+ if (startTime !== void 0) {
17406
+ metrics.start = startTime;
17407
+ metrics.end = end;
17408
+ metrics.duration = end - startTime;
17409
+ }
17410
+ let latestInteraction;
17411
+ let latestUsage;
17412
+ let status;
17413
+ let outputText = "";
17414
+ const steps = /* @__PURE__ */ new Map();
17415
+ for (const chunk of chunks) {
17416
+ const event = tryToDict(chunk);
17417
+ if (!event) {
17418
+ continue;
17419
+ }
17420
+ const usage = extractInteractionUsageFromEvent(event);
17421
+ if (usage) {
17422
+ latestUsage = usage;
17423
+ }
17424
+ const interaction = tryToDict(event.interaction);
17425
+ if (interaction) {
17426
+ latestInteraction = serializeInteractionValue(interaction);
17427
+ if (typeof interaction.status === "string") {
17428
+ status = interaction.status;
17429
+ }
17430
+ }
17431
+ if (typeof event.status === "string") {
17432
+ status = event.status;
17433
+ }
17434
+ const index = typeof event.index === "number" ? event.index : void 0;
17435
+ if (index === void 0) {
17436
+ continue;
17437
+ }
17438
+ if (event.event_type === "step.start") {
17439
+ const compact = compactInteractionStep(event.step);
17440
+ compact.index = index;
17441
+ steps.set(index, compact);
17442
+ continue;
17443
+ }
17444
+ if (event.event_type === "step.delta") {
17445
+ const step = steps.get(index) ?? { index };
17446
+ const textDelta = applyInteractionDelta(step, event.delta);
17447
+ if (textDelta) {
17448
+ outputText += textDelta;
17449
+ }
17450
+ steps.set(index, step);
17451
+ }
17452
+ }
17453
+ if (latestUsage) {
17454
+ populateInteractionUsageMetrics(metrics, latestUsage);
17455
+ }
17456
+ const output = latestInteraction ? { ...latestInteraction } : {};
17457
+ if (status) {
17458
+ output.status = status;
17459
+ }
17460
+ if (outputText) {
17461
+ output.output_text = outputText;
17462
+ }
17463
+ if (latestUsage) {
17464
+ output.usage = serializeInteractionValue(latestUsage);
17465
+ }
17466
+ const compactSteps = Array.from(steps.values()).sort(
17467
+ (left, right) => Number(left.index ?? 0) - Number(right.index ?? 0)
17468
+ );
17469
+ if (compactSteps.length > 0) {
17470
+ output.steps = compactSteps;
17471
+ }
17472
+ const metadata = {};
17473
+ if (typeof output.id === "string") {
17474
+ metadata.interaction_id = output.id;
17475
+ }
17476
+ if (typeof output.status === "string") {
17477
+ metadata.status = output.status;
17478
+ }
17479
+ return {
17480
+ output,
17481
+ metrics: cleanMetrics3(metrics),
17482
+ ...Object.keys(metadata).length > 0 ? { metadata } : {}
17483
+ };
17484
+ }
17485
+ function extractInteractionUsageFromEvent(event) {
17486
+ const metadata = tryToDict(event.metadata);
17487
+ const metadataUsage = tryToDict(metadata?.usage);
17488
+ if (metadataUsage) {
17489
+ return metadataUsage;
17490
+ }
17491
+ const metadataTotalUsage = tryToDict(metadata?.total_usage);
17492
+ if (metadataTotalUsage) {
17493
+ return metadataTotalUsage;
17494
+ }
17495
+ const interaction = tryToDict(event.interaction);
17496
+ const interactionUsage = tryToDict(interaction?.usage);
17497
+ return interactionUsage ? interactionUsage : void 0;
17498
+ }
17499
+ function compactInteractionStep(step) {
17500
+ const stepDict = tryToDict(step);
17501
+ if (!stepDict) {
17502
+ return {};
17503
+ }
17504
+ const compact = {};
17505
+ for (const key of [
17506
+ "type",
17507
+ "content",
17508
+ "name",
17509
+ "server_name",
17510
+ "arguments",
17511
+ "result",
17512
+ "is_error"
17513
+ ]) {
17514
+ if (stepDict[key] !== void 0) {
17515
+ compact[key] = serializeInteractionValue(stepDict[key]);
17516
+ }
17517
+ }
17518
+ return Object.keys(compact).length > 0 ? compact : serializeInteractionValue(stepDict);
17519
+ }
17520
+ function applyInteractionDelta(step, delta) {
17521
+ const deltaDict = tryToDict(delta);
17522
+ if (!deltaDict) {
17523
+ return void 0;
17524
+ }
17525
+ const deltaType = deltaDict.type;
17526
+ if (typeof deltaType === "string" && typeof step.type !== "string") {
17527
+ step.type = deltaType === "text" ? "model_output" : deltaType;
17528
+ }
17529
+ if (deltaType === "text" && typeof deltaDict.text === "string") {
17530
+ step.text = `${typeof step.text === "string" ? step.text : ""}${deltaDict.text}`;
17531
+ return deltaDict.text;
17532
+ }
17533
+ if (deltaType === "arguments_delta" && typeof deltaDict.arguments === "string") {
17534
+ step.arguments = `${typeof step.arguments === "string" ? step.arguments : ""}${deltaDict.arguments}`;
17535
+ return void 0;
17536
+ }
17537
+ const deltas = Array.isArray(step.deltas) ? step.deltas : [];
17538
+ deltas.push(serializeInteractionValue(deltaDict));
17539
+ step.deltas = deltas;
17540
+ return void 0;
17541
+ }
16407
17542
  function cleanMetrics3(metrics) {
16408
17543
  const cleaned = {};
16409
17544
  for (const [key, value] of Object.entries(metrics)) {
@@ -22230,7 +23365,8 @@ var FlueObserveBridge = class {
22230
23365
  metadata
22231
23366
  }
22232
23367
  });
22233
- this.runsById.set(event.runId, { metadata, span });
23368
+ const activeContext = enterCurrentFlueSpan(span);
23369
+ this.runsById.set(event.runId, { activeContext, metadata, span });
22234
23370
  }
22235
23371
  handleRunEnd(event) {
22236
23372
  const state = this.runsById.get(event.runId);
@@ -22248,6 +23384,7 @@ var FlueObserveBridge = class {
22248
23384
  });
22249
23385
  safeEnd(state.span, eventTime(event.timestamp));
22250
23386
  this.runsById.delete(event.runId);
23387
+ restoreCurrentFlueSpan(state.activeContext);
22251
23388
  }
22252
23389
  void flush().catch((error) => {
22253
23390
  logInstrumentationError3("Flue flush", error);
@@ -22374,7 +23511,8 @@ var FlueObserveBridge = class {
22374
23511
  metadata
22375
23512
  }
22376
23513
  });
22377
- this.toolsByKey.set(toolKey(event), { metadata, span });
23514
+ const activeContext = enterCurrentFlueSpan(span);
23515
+ this.toolsByKey.set(toolKey(event), { activeContext, metadata, span });
22378
23516
  }
22379
23517
  handleToolCall(event) {
22380
23518
  if (!event.toolCallId) {
@@ -22397,6 +23535,7 @@ var FlueObserveBridge = class {
22397
23535
  });
22398
23536
  safeEnd(state.span, eventTime(event.timestamp));
22399
23537
  this.toolsByKey.delete(key);
23538
+ restoreCurrentFlueSpan(state.activeContext);
22400
23539
  }
22401
23540
  handleTaskStart(event) {
22402
23541
  if (!event.taskId) {
@@ -22801,7 +23940,35 @@ function stateMatchesRun(state, runId) {
22801
23940
  function startFlueSpan(parent, args) {
22802
23941
  return parent ? withCurrent(parent, () => startSpan(args)) : startSpan(args);
22803
23942
  }
22804
- function safeLog3(span, event) {
23943
+ function enterCurrentFlueSpan(span) {
23944
+ const contextManager = _internalGetGlobalState()?.contextManager;
23945
+ const store = contextManager ? Reflect.get(contextManager, BRAINTRUST_CURRENT_SPAN_STORE) : void 0;
23946
+ if (!contextManager || !isCurrentSpanStore(store)) {
23947
+ return void 0;
23948
+ }
23949
+ const previous = store.getStore();
23950
+ try {
23951
+ store.enterWith(contextManager.wrapSpanForStore(span));
23952
+ return { previous, store };
23953
+ } catch (error) {
23954
+ logInstrumentationError3("Flue context propagation", error);
23955
+ return void 0;
23956
+ }
23957
+ }
23958
+ function isCurrentSpanStore(value) {
23959
+ return isObjectLike(value) && typeof Reflect.get(value, "enterWith") === "function" && typeof Reflect.get(value, "getStore") === "function";
23960
+ }
23961
+ function restoreCurrentFlueSpan(activeContext) {
23962
+ if (!activeContext) {
23963
+ return;
23964
+ }
23965
+ try {
23966
+ activeContext.store.enterWith(activeContext.previous);
23967
+ } catch (error) {
23968
+ logInstrumentationError3("Flue context restoration", error);
23969
+ }
23970
+ }
23971
+ function safeLog3(span, event) {
22805
23972
  try {
22806
23973
  span.log(event);
22807
23974
  } catch (error) {
@@ -23278,6 +24445,890 @@ function isBraintrustHandler(handler) {
23278
24445
  return Reflect.get(handler, "name") === BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME;
23279
24446
  }
23280
24447
 
24448
+ // src/instrumentation/plugins/pi-coding-agent-channels.ts
24449
+ var piCodingAgentChannels = defineChannels(
24450
+ "@earendil-works/pi-coding-agent",
24451
+ {
24452
+ prompt: channel({
24453
+ channelName: "AgentSession.prompt",
24454
+ kind: "async"
24455
+ })
24456
+ }
24457
+ );
24458
+
24459
+ // src/instrumentation/plugins/pi-coding-agent-plugin.ts
24460
+ var piStreamPatchStates = /* @__PURE__ */ new WeakMap();
24461
+ var piPromptContextStore;
24462
+ var PiCodingAgentPlugin = class extends BasePlugin {
24463
+ activePromptStates = /* @__PURE__ */ new Set();
24464
+ onEnable() {
24465
+ this.subscribeToPrompt();
24466
+ }
24467
+ onDisable() {
24468
+ for (const unsubscribe of this.unsubscribers) {
24469
+ unsubscribe();
24470
+ }
24471
+ this.unsubscribers = [];
24472
+ for (const state of [...this.activePromptStates]) {
24473
+ void finalizePiPromptRun(state).catch((error) => {
24474
+ logInstrumentationError4("Pi Coding Agent disable cleanup", error);
24475
+ });
24476
+ }
24477
+ }
24478
+ subscribeToPrompt() {
24479
+ const channel2 = piCodingAgentChannels.prompt.tracingChannel();
24480
+ const states = /* @__PURE__ */ new WeakMap();
24481
+ const unbindAutoInstrumentationSuppression = bindAutoInstrumentationSuppressionToStart(channel2);
24482
+ const handlers = {
24483
+ start: (event) => {
24484
+ const state = startPiPromptRun(event, (state2) => {
24485
+ this.activePromptStates.delete(state2);
24486
+ });
24487
+ if (state) {
24488
+ this.activePromptStates.add(state);
24489
+ states.set(event, state);
24490
+ }
24491
+ },
24492
+ asyncEnd: async (event) => {
24493
+ const state = states.get(event);
24494
+ if (!state) {
24495
+ return;
24496
+ }
24497
+ states.delete(event);
24498
+ state.promptCallEnded = true;
24499
+ if (!state.finalized && state.deferCompletionUntilTurnEnd && !state.turnEnded) {
24500
+ if (!state.sawStreamFn) {
24501
+ state.queued = true;
24502
+ if (!state.streamPatchState.queuedPromptStates.includes(state)) {
24503
+ state.streamPatchState.queuedPromptStates.push(state);
24504
+ }
24505
+ }
24506
+ return;
24507
+ }
24508
+ await finalizePiPromptRun(state);
24509
+ },
24510
+ error: async (event) => {
24511
+ const state = states.get(event);
24512
+ if (!state) {
24513
+ return;
24514
+ }
24515
+ states.delete(event);
24516
+ await finalizePiPromptRun(state, event.error);
24517
+ }
24518
+ };
24519
+ channel2.subscribe(handlers);
24520
+ this.unsubscribers.push(() => {
24521
+ unbindAutoInstrumentationSuppression?.();
24522
+ channel2.unsubscribe(handlers);
24523
+ });
24524
+ }
24525
+ };
24526
+ function startPiPromptRun(event, onFinalize) {
24527
+ const session = extractSession(event);
24528
+ const agent = session?.agent;
24529
+ if (!session || !isPiAgent(agent)) {
24530
+ return void 0;
24531
+ }
24532
+ const metadata = {
24533
+ ...extractSessionMetadata(session),
24534
+ ...extractPromptOptionsMetadata(event.arguments[1]),
24535
+ "pi_coding_agent.operation": "AgentSession.prompt",
24536
+ provider: session.model?.provider ?? agent.state?.model?.provider ?? "pi",
24537
+ ...session.model?.id || agent.state?.model?.id ? { model: session.model?.id ?? agent.state?.model?.id } : {},
24538
+ ...event.moduleVersion ? { "pi_coding_agent.version": event.moduleVersion } : {}
24539
+ };
24540
+ const span = startSpan({
24541
+ event: {
24542
+ input: extractPromptInput(event.arguments[0], event.arguments[1]),
24543
+ metadata
24544
+ },
24545
+ name: "AgentSession.prompt",
24546
+ spanAttributes: { type: "task" /* TASK */ }
24547
+ });
24548
+ const streamPatchState = installPiStreamPatch(agent);
24549
+ const options = event.arguments[1];
24550
+ const promptText = event.arguments[0];
24551
+ const state = {
24552
+ activeLlmSpans: /* @__PURE__ */ new Set(),
24553
+ activeToolSpans: /* @__PURE__ */ new Map(),
24554
+ agent,
24555
+ collectedLlmUsageMetrics: false,
24556
+ deferCompletionUntilTurnEnd: options?.streamingBehavior === "followUp" || options?.streamingBehavior === "steer",
24557
+ finalized: false,
24558
+ metadata,
24559
+ metrics: {},
24560
+ onFinalize,
24561
+ promptCallEnded: false,
24562
+ ...typeof promptText === "string" ? { promptText } : {},
24563
+ queued: false,
24564
+ span,
24565
+ sawStreamFn: false,
24566
+ startTime: getCurrentUnixTimestamp(),
24567
+ streamPatchState,
24568
+ turnEnded: false
24569
+ };
24570
+ state.restorePromptContext = enterPiPromptContext(state);
24571
+ streamPatchState.activePromptStates.add(state);
24572
+ try {
24573
+ state.unsubscribeAgent = agent.subscribe(async (agentEvent) => {
24574
+ try {
24575
+ await runWithAutoInstrumentationSuppressed(
24576
+ () => handlePiAgentEvent(state, agentEvent)
24577
+ );
24578
+ } catch (error) {
24579
+ logInstrumentationError4("Pi Coding Agent event", error);
24580
+ }
24581
+ });
24582
+ } catch (error) {
24583
+ logInstrumentationError4("Pi Coding Agent event subscription", error);
24584
+ }
24585
+ return state;
24586
+ }
24587
+ function extractSession(event) {
24588
+ const candidate = event.session ?? event.self;
24589
+ return isObject(candidate) && typeof candidate.prompt === "function" ? candidate : void 0;
24590
+ }
24591
+ function isPiAgent(value) {
24592
+ return isObject(value) && typeof value.streamFn === "function" && typeof value.subscribe === "function";
24593
+ }
24594
+ function promptContextStore() {
24595
+ piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
24596
+ return piPromptContextStore;
24597
+ }
24598
+ function currentPromptContextFrames() {
24599
+ return promptContextStore().getStore()?.frames ?? [];
24600
+ }
24601
+ function currentPiPromptState() {
24602
+ const frames = currentPromptContextFrames();
24603
+ return frames[frames.length - 1]?.state;
24604
+ }
24605
+ function enterPiPromptContext(state) {
24606
+ const frame = {
24607
+ id: /* @__PURE__ */ Symbol("braintrust.pi-coding-agent.prompt"),
24608
+ state
24609
+ };
24610
+ promptContextStore().enterWith({
24611
+ frames: [...currentPromptContextFrames(), frame]
24612
+ });
24613
+ return () => {
24614
+ const frames = currentPromptContextFrames().filter(
24615
+ (candidate) => candidate.id !== frame.id
24616
+ );
24617
+ promptContextStore().enterWith(frames.length > 0 ? { frames } : void 0);
24618
+ };
24619
+ }
24620
+ function installPiStreamPatch(agent) {
24621
+ const existing = piStreamPatchStates.get(agent);
24622
+ if (existing) {
24623
+ if (agent.streamFn !== existing.wrappedStreamFn) {
24624
+ debugLogger.debug(
24625
+ "Pi Coding Agent streamFn changed while Braintrust instrumentation was active; preserving existing patch state."
24626
+ );
24627
+ }
24628
+ return existing;
24629
+ }
24630
+ const patchState = {
24631
+ activePromptStates: /* @__PURE__ */ new Set(),
24632
+ agent,
24633
+ originalStreamFn: agent.streamFn,
24634
+ queuedPromptStates: [],
24635
+ wrappedStreamFn: agent.streamFn
24636
+ };
24637
+ patchState.wrappedStreamFn = makeSharedInstrumentedStreamFn(patchState);
24638
+ agent.streamFn = patchState.wrappedStreamFn;
24639
+ piStreamPatchStates.set(agent, patchState);
24640
+ return patchState;
24641
+ }
24642
+ function resolveStreamPromptState(patchState, context) {
24643
+ let lastUserText;
24644
+ if (Array.isArray(context.messages)) {
24645
+ for (let i = context.messages.length - 1; i >= 0; i--) {
24646
+ const message = context.messages[i];
24647
+ if (isPiUserMessage(message)) {
24648
+ if (typeof message.content === "string") {
24649
+ lastUserText = message.content;
24650
+ } else {
24651
+ lastUserText = message.content.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
24652
+ }
24653
+ break;
24654
+ }
24655
+ }
24656
+ }
24657
+ if (lastUserText !== void 0) {
24658
+ const queuedMatch = patchState.queuedPromptStates.find(
24659
+ (state) => state.promptText === lastUserText
24660
+ );
24661
+ if (queuedMatch) {
24662
+ return queuedMatch;
24663
+ }
24664
+ const matches = [...patchState.activePromptStates].filter(
24665
+ (state) => state.promptText === lastUserText
24666
+ );
24667
+ if (matches.length === 1) {
24668
+ return matches[0];
24669
+ }
24670
+ }
24671
+ const contextState = currentPiPromptState();
24672
+ if (contextState && patchState.activePromptStates.has(contextState) && (!contextState.queued || lastUserText !== void 0 && contextState.promptText === lastUserText)) {
24673
+ return contextState;
24674
+ }
24675
+ if (patchState.activePromptStates.size === 1) {
24676
+ return [...patchState.activePromptStates][0];
24677
+ }
24678
+ return void 0;
24679
+ }
24680
+ function makeSharedInstrumentedStreamFn(patchState) {
24681
+ return async function instrumentedPiStreamFn(model, context, options) {
24682
+ const state = resolveStreamPromptState(patchState, context);
24683
+ if (!state) {
24684
+ const invokeOriginal = () => Reflect.apply(patchState.originalStreamFn, this, [
24685
+ model,
24686
+ context,
24687
+ options
24688
+ ]);
24689
+ return patchState.activePromptStates.size > 0 ? runWithAutoInstrumentationSuppressed(invokeOriginal) : invokeOriginal();
24690
+ }
24691
+ state.sawStreamFn = true;
24692
+ removeQueuedPromptState(state);
24693
+ state.streamPatchState.eventPromptState = state;
24694
+ const llmState = await startPiLlmSpan(state, model, context, options);
24695
+ try {
24696
+ const stream = await runWithAutoInstrumentationSuppressed(
24697
+ () => Reflect.apply(patchState.originalStreamFn, this, [
24698
+ model,
24699
+ context,
24700
+ options
24701
+ ])
24702
+ );
24703
+ return patchAssistantMessageStream(stream, state, llmState);
24704
+ } catch (error) {
24705
+ finishPiLlmSpan(state, llmState, void 0, error);
24706
+ throw error;
24707
+ }
24708
+ };
24709
+ }
24710
+ async function startPiLlmSpan(state, model, context, options) {
24711
+ const metadata = {
24712
+ ...extractModelMetadata2(model),
24713
+ ...extractStreamOptionsMetadata(options),
24714
+ ...extractToolMetadata(context.tools),
24715
+ "pi_coding_agent.operation": "agent.streamFn"
24716
+ };
24717
+ const span = startSpan({
24718
+ event: {
24719
+ input: processInputAttachments(normalizePiContextInput(context)),
24720
+ metadata
24721
+ },
24722
+ name: getLlmSpanName(model),
24723
+ parent: await state.span.export(),
24724
+ spanAttributes: { type: "llm" /* LLM */ }
24725
+ });
24726
+ const llmState = {
24727
+ finalized: false,
24728
+ metadata,
24729
+ metrics: {},
24730
+ span,
24731
+ startTime: getCurrentUnixTimestamp()
24732
+ };
24733
+ state.activeLlmSpans.add(llmState);
24734
+ return llmState;
24735
+ }
24736
+ function patchAssistantMessageStream(stream, promptState, llmState) {
24737
+ if (!isObject(stream)) {
24738
+ return stream;
24739
+ }
24740
+ const streamRecord = stream;
24741
+ const originalResult = stream.result;
24742
+ if (typeof originalResult === "function") {
24743
+ streamRecord.result = function patchedPiResult() {
24744
+ return Promise.resolve(Reflect.apply(originalResult, this, [])).then(
24745
+ (message) => {
24746
+ finishPiLlmSpan(promptState, llmState, message);
24747
+ return message;
24748
+ },
24749
+ (error) => {
24750
+ finishPiLlmSpan(promptState, llmState, void 0, error);
24751
+ throw error;
24752
+ }
24753
+ );
24754
+ };
24755
+ }
24756
+ const originalIterator = stream[Symbol.asyncIterator];
24757
+ if (typeof originalIterator === "function") {
24758
+ streamRecord[Symbol.asyncIterator] = function patchedPiIterator() {
24759
+ const iterator = Reflect.apply(
24760
+ originalIterator,
24761
+ this,
24762
+ []
24763
+ );
24764
+ return {
24765
+ async next() {
24766
+ try {
24767
+ const result = await iterator.next();
24768
+ if (result.done) {
24769
+ finishPiLlmSpan(promptState, llmState);
24770
+ return result;
24771
+ }
24772
+ recordPiAssistantMessageEvent(promptState, llmState, result.value);
24773
+ return result;
24774
+ } catch (error) {
24775
+ finishPiLlmSpan(promptState, llmState, void 0, error);
24776
+ if (typeof iterator.return === "function") {
24777
+ try {
24778
+ await iterator.return();
24779
+ } catch (cleanupError) {
24780
+ logInstrumentationError4(
24781
+ "Pi Coding Agent stream cleanup",
24782
+ cleanupError
24783
+ );
24784
+ }
24785
+ }
24786
+ throw error;
24787
+ }
24788
+ },
24789
+ async return(value) {
24790
+ try {
24791
+ if (typeof iterator.return === "function") {
24792
+ return await iterator.return(value);
24793
+ }
24794
+ return {
24795
+ done: true,
24796
+ value
24797
+ };
24798
+ } catch (error) {
24799
+ finishPiLlmSpan(promptState, llmState, void 0, error);
24800
+ throw error;
24801
+ } finally {
24802
+ finishPiLlmSpan(promptState, llmState);
24803
+ }
24804
+ },
24805
+ async throw(error) {
24806
+ try {
24807
+ if (typeof iterator.throw === "function") {
24808
+ return await iterator.throw(error);
24809
+ }
24810
+ throw error;
24811
+ } catch (thrownError) {
24812
+ finishPiLlmSpan(promptState, llmState, void 0, thrownError);
24813
+ throw thrownError;
24814
+ }
24815
+ },
24816
+ [Symbol.asyncIterator]() {
24817
+ return this;
24818
+ }
24819
+ };
24820
+ };
24821
+ }
24822
+ return stream;
24823
+ }
24824
+ function recordPiAssistantMessageEvent(promptState, llmState, event) {
24825
+ recordFirstTokenMetric(llmState, event);
24826
+ const message = "message" in event ? event.message : void 0;
24827
+ const errorMessage2 = "error" in event ? event.error : void 0;
24828
+ if (event.type === "done" && isPiAssistantMessage(message)) {
24829
+ finishPiLlmSpan(promptState, llmState, message);
24830
+ } else if (event.type === "error" && isPiAssistantMessage(errorMessage2)) {
24831
+ finishPiLlmSpan(promptState, llmState, errorMessage2);
24832
+ }
24833
+ }
24834
+ function recordFirstTokenMetric(state, event) {
24835
+ if (state.metrics.time_to_first_token !== void 0 || event.type === "start") {
24836
+ return;
24837
+ }
24838
+ state.metrics.time_to_first_token = getCurrentUnixTimestamp() - state.startTime;
24839
+ }
24840
+ async function handlePiAgentEvent(state, event) {
24841
+ if (state.finalized) {
24842
+ return;
24843
+ }
24844
+ const eventPromptState = state.streamPatchState.eventPromptState;
24845
+ if (eventPromptState && eventPromptState !== state) {
24846
+ return;
24847
+ }
24848
+ if (!eventPromptState && (state.queued || state.streamPatchState.activePromptStates.size > 1 && currentPiPromptState() !== state)) {
24849
+ return;
24850
+ }
24851
+ switch (event.type) {
24852
+ case "message_end":
24853
+ if (isPiAssistantMessage(event.message)) {
24854
+ state.output = extractAssistantOutput(event.message);
24855
+ }
24856
+ return;
24857
+ case "turn_end":
24858
+ state.turnEnded = true;
24859
+ if (isPiAssistantMessage(event.message)) {
24860
+ state.output = extractAssistantOutput(event.message);
24861
+ if (!state.collectedLlmUsageMetrics) {
24862
+ addMetrics(state.metrics, extractUsageMetrics2(event.message.usage));
24863
+ }
24864
+ }
24865
+ if (state.streamPatchState.eventPromptState === state) {
24866
+ state.streamPatchState.eventPromptState = void 0;
24867
+ }
24868
+ if (state.promptCallEnded && state.deferCompletionUntilTurnEnd) {
24869
+ await finalizePiPromptRun(state);
24870
+ }
24871
+ return;
24872
+ case "tool_execution_start":
24873
+ await startPiToolSpan(state, event);
24874
+ return;
24875
+ case "tool_execution_end":
24876
+ finishPiToolSpan(state, event);
24877
+ return;
24878
+ default:
24879
+ return;
24880
+ }
24881
+ }
24882
+ async function startPiToolSpan(state, event) {
24883
+ if (!event.toolCallId || state.activeToolSpans.has(event.toolCallId)) {
24884
+ return;
24885
+ }
24886
+ const restoreAutoInstrumentation = enterAutoInstrumentationAllowed();
24887
+ const metadata = {
24888
+ "gen_ai.tool.call.id": event.toolCallId,
24889
+ "gen_ai.tool.name": event.toolName,
24890
+ "pi_coding_agent.tool.name": event.toolName
24891
+ };
24892
+ try {
24893
+ const span = startSpan({
24894
+ event: {
24895
+ input: event.args,
24896
+ metadata
24897
+ },
24898
+ name: event.toolName || "tool",
24899
+ parent: await state.span.export(),
24900
+ spanAttributes: { type: "tool" /* TOOL */ }
24901
+ });
24902
+ state.activeToolSpans.set(event.toolCallId, {
24903
+ restoreAutoInstrumentation,
24904
+ span
24905
+ });
24906
+ } catch (error) {
24907
+ restoreAutoInstrumentation();
24908
+ throw error;
24909
+ }
24910
+ }
24911
+ function finishPiToolSpan(state, event) {
24912
+ const toolState = state.activeToolSpans.get(event.toolCallId);
24913
+ if (!toolState) {
24914
+ return;
24915
+ }
24916
+ state.activeToolSpans.delete(event.toolCallId);
24917
+ const metadata = {
24918
+ "gen_ai.tool.call.id": event.toolCallId,
24919
+ "gen_ai.tool.name": event.toolName,
24920
+ "pi_coding_agent.tool.name": event.toolName,
24921
+ "pi_coding_agent.tool.is_error": event.isError
24922
+ };
24923
+ try {
24924
+ safeLog4(toolState.span, {
24925
+ ...event.isError ? { error: stringifyUnknown2(event.result) } : {},
24926
+ metadata,
24927
+ output: event.result
24928
+ });
24929
+ } finally {
24930
+ try {
24931
+ toolState.span.end();
24932
+ } finally {
24933
+ toolState.restoreAutoInstrumentation?.();
24934
+ }
24935
+ }
24936
+ }
24937
+ async function finalizePiPromptRun(state, error) {
24938
+ if (state.finalized) {
24939
+ return;
24940
+ }
24941
+ state.finalized = true;
24942
+ state.onFinalize?.(state);
24943
+ restorePiStreamFn(state);
24944
+ try {
24945
+ state.unsubscribeAgent?.();
24946
+ } catch (unsubscribeError) {
24947
+ logInstrumentationError4("Pi Coding Agent unsubscribe", unsubscribeError);
24948
+ }
24949
+ await finishOpenLlmSpans(state, error);
24950
+ finishOpenToolSpans(state, error);
24951
+ const metadata = {
24952
+ ...state.metadata,
24953
+ ...extractModelMetadata2(state.agent.state?.model)
24954
+ };
24955
+ try {
24956
+ safeLog4(state.span, {
24957
+ ...error ? { error: stringifyUnknown2(error) } : {},
24958
+ metadata,
24959
+ metrics: {
24960
+ ...cleanMetrics5(state.metrics),
24961
+ ...buildDurationMetrics3(state.startTime)
24962
+ },
24963
+ output: state.output
24964
+ });
24965
+ } finally {
24966
+ state.span.end();
24967
+ }
24968
+ }
24969
+ function restorePiStreamFn(state) {
24970
+ const patchState = state.streamPatchState;
24971
+ patchState.activePromptStates.delete(state);
24972
+ removeQueuedPromptState(state);
24973
+ if (patchState.eventPromptState === state) {
24974
+ patchState.eventPromptState = void 0;
24975
+ }
24976
+ state.restorePromptContext?.();
24977
+ if (patchState.activePromptStates.size > 0) {
24978
+ return;
24979
+ }
24980
+ if (patchState.agent.streamFn === patchState.wrappedStreamFn) {
24981
+ patchState.agent.streamFn = patchState.originalStreamFn;
24982
+ }
24983
+ piStreamPatchStates.delete(patchState.agent);
24984
+ }
24985
+ function removeQueuedPromptState(state) {
24986
+ state.queued = false;
24987
+ const queuedPromptStates = state.streamPatchState.queuedPromptStates;
24988
+ const index = queuedPromptStates.indexOf(state);
24989
+ if (index >= 0) {
24990
+ queuedPromptStates.splice(index, 1);
24991
+ }
24992
+ }
24993
+ async function finishOpenLlmSpans(state, error) {
24994
+ for (const llmState of [...state.activeLlmSpans]) {
24995
+ finishPiLlmSpan(state, llmState, void 0, error);
24996
+ }
24997
+ }
24998
+ function finishPiLlmSpan(promptState, llmState, message, error) {
24999
+ if (llmState.finalized) {
25000
+ return;
25001
+ }
25002
+ llmState.finalized = true;
25003
+ promptState.activeLlmSpans.delete(llmState);
25004
+ const messageError = message?.stopReason === "error" && message.errorMessage;
25005
+ const metrics = {
25006
+ ...extractUsageMetrics2(message?.usage),
25007
+ ...cleanMetrics5(llmState.metrics),
25008
+ ...buildDurationMetrics3(llmState.startTime)
25009
+ };
25010
+ const usageMetrics = extractUsageMetrics2(message?.usage);
25011
+ if (Object.keys(usageMetrics).length > 0) {
25012
+ promptState.collectedLlmUsageMetrics = true;
25013
+ addMetrics(promptState.metrics, usageMetrics);
25014
+ }
25015
+ try {
25016
+ safeLog4(llmState.span, {
25017
+ ...error || messageError ? { error: stringifyUnknown2(error ?? messageError) } : {},
25018
+ metadata: {
25019
+ ...llmState.metadata,
25020
+ ...message ? extractAssistantMetadata(message) : {}
25021
+ },
25022
+ metrics,
25023
+ ...message ? { output: extractAssistantOutput(message) } : {}
25024
+ });
25025
+ } finally {
25026
+ llmState.span.end();
25027
+ }
25028
+ }
25029
+ function finishOpenToolSpans(state, error) {
25030
+ for (const [, toolState] of state.activeToolSpans) {
25031
+ try {
25032
+ safeLog4(toolState.span, {
25033
+ error: error ? stringifyUnknown2(error) : "Pi tool did not complete"
25034
+ });
25035
+ toolState.span.end();
25036
+ } finally {
25037
+ toolState.restoreAutoInstrumentation?.();
25038
+ }
25039
+ }
25040
+ state.activeToolSpans.clear();
25041
+ }
25042
+ function normalizePiContextInput(context) {
25043
+ const messages = context.messages.flatMap(
25044
+ (message) => normalizePiMessage(message)
25045
+ );
25046
+ if (context.systemPrompt) {
25047
+ return [{ role: "system", content: context.systemPrompt }, ...messages];
25048
+ }
25049
+ return messages;
25050
+ }
25051
+ function normalizePiMessage(message) {
25052
+ if (isPiUserMessage(message)) {
25053
+ return [
25054
+ {
25055
+ role: "user",
25056
+ content: normalizeUserContent(message.content)
25057
+ }
25058
+ ];
25059
+ }
25060
+ if (isPiAssistantMessage(message)) {
25061
+ return [normalizeAssistantMessage(message)];
25062
+ }
25063
+ if (isPiToolResultMessage(message)) {
25064
+ return [normalizeToolResultMessage(message)];
25065
+ }
25066
+ return [];
25067
+ }
25068
+ function normalizeAssistantMessage(message) {
25069
+ const text = message.content.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
25070
+ const thinking = message.content.flatMap(
25071
+ (part) => part.type === "thinking" && !part.redacted ? [part.thinking] : []
25072
+ ).join("");
25073
+ const toolCalls = message.content.flatMap(
25074
+ (part) => part.type === "toolCall" ? [normalizeToolCall(part)] : []
25075
+ );
25076
+ return {
25077
+ role: "assistant",
25078
+ content: text || (toolCalls.length > 0 ? null : ""),
25079
+ ...thinking ? { reasoning: thinking } : {},
25080
+ ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
25081
+ };
25082
+ }
25083
+ function normalizeToolResultMessage(message) {
25084
+ return {
25085
+ role: "tool",
25086
+ tool_call_id: message.toolCallId,
25087
+ content: normalizeUserContent(message.content)
25088
+ };
25089
+ }
25090
+ function normalizeUserContent(content) {
25091
+ if (typeof content === "string") {
25092
+ return content;
25093
+ }
25094
+ return content.map((part) => {
25095
+ if (part.type === "text") {
25096
+ return { type: "text", text: part.text };
25097
+ }
25098
+ if (part.type === "image") {
25099
+ return {
25100
+ type: "image_url",
25101
+ image_url: {
25102
+ url: `data:${part.mimeType};base64,${part.data}`
25103
+ }
25104
+ };
25105
+ }
25106
+ return part;
25107
+ });
25108
+ }
25109
+ function normalizeToolCall(toolCall) {
25110
+ return {
25111
+ id: toolCall.id,
25112
+ type: "function",
25113
+ function: {
25114
+ name: toolCall.name,
25115
+ arguments: stringifyArguments(toolCall.arguments)
25116
+ }
25117
+ };
25118
+ }
25119
+ function extractAssistantOutput(message) {
25120
+ return processInputAttachments([
25121
+ {
25122
+ finish_reason: normalizeStopReason(message.stopReason),
25123
+ index: 0,
25124
+ message: normalizeAssistantMessage(message)
25125
+ }
25126
+ ]);
25127
+ }
25128
+ function isPiUserMessage(message) {
25129
+ return message.role === "user" && "content" in message;
25130
+ }
25131
+ function isPiAssistantMessage(message) {
25132
+ return isObject(message) && message.role === "assistant" && Array.isArray(message.content);
25133
+ }
25134
+ function isPiToolResultMessage(message) {
25135
+ return message.role === "toolResult" && Array.isArray(message.content);
25136
+ }
25137
+ function normalizeStopReason(reason) {
25138
+ switch (reason) {
25139
+ case "toolUse":
25140
+ return "tool_calls";
25141
+ case "length":
25142
+ case "stop":
25143
+ return reason;
25144
+ default:
25145
+ return reason ?? "stop";
25146
+ }
25147
+ }
25148
+ function extractPromptInput(text, options) {
25149
+ const images = options?.images;
25150
+ if (!images || images.length === 0) {
25151
+ return text;
25152
+ }
25153
+ return processInputAttachments([
25154
+ {
25155
+ role: "user",
25156
+ content: [
25157
+ { type: "text", text: text ?? "" },
25158
+ ...images.map((image) => ({
25159
+ type: "image_url",
25160
+ image_url: {
25161
+ url: `data:${image.mimeType};base64,${image.data}`
25162
+ }
25163
+ }))
25164
+ ]
25165
+ }
25166
+ ]);
25167
+ }
25168
+ function extractToolMetadata(tools) {
25169
+ if (!tools || tools.length === 0) {
25170
+ return {};
25171
+ }
25172
+ return {
25173
+ tools: tools.map((tool) => ({
25174
+ type: "function",
25175
+ function: {
25176
+ name: tool.name,
25177
+ ...tool.description ? { description: tool.description } : {},
25178
+ ...tool.parameters ? { parameters: tool.parameters } : {}
25179
+ }
25180
+ }))
25181
+ };
25182
+ }
25183
+ function extractModelMetadata2(model) {
25184
+ if (!model) {
25185
+ return {};
25186
+ }
25187
+ return {
25188
+ ...model.provider ? { provider: model.provider } : {},
25189
+ ...model.id ? { model: model.id, "pi_coding_agent.model": model.id } : {},
25190
+ ...model.api ? { "pi_coding_agent.api": model.api } : {},
25191
+ ...model.name ? { "pi_coding_agent.model_name": model.name } : {}
25192
+ };
25193
+ }
25194
+ function extractAssistantMetadata(message) {
25195
+ return {
25196
+ ...message.provider ? { provider: message.provider } : {},
25197
+ ...message.responseModel || message.model ? { model: message.responseModel ?? message.model } : {},
25198
+ ...message.api ? { "pi_coding_agent.api": message.api } : {},
25199
+ ...message.model ? { "pi_coding_agent.model": message.model } : {},
25200
+ ...message.responseModel ? { "pi_coding_agent.response_model": message.responseModel } : {},
25201
+ ...message.responseId ? { "pi_coding_agent.response_id": message.responseId } : {},
25202
+ ...message.stopReason ? { "pi_coding_agent.stop_reason": message.stopReason } : {}
25203
+ };
25204
+ }
25205
+ function extractSessionMetadata(session) {
25206
+ return {
25207
+ ...extractModelMetadata2(session.model),
25208
+ ...session.sessionId ? { "pi_coding_agent.session_id": session.sessionId } : {},
25209
+ ...session.sessionName ? { "pi_coding_agent.session_name": session.sessionName } : {},
25210
+ ...session.thinkingLevel ? { "pi_coding_agent.thinking_level": session.thinkingLevel } : {},
25211
+ ...typeof session.getActiveToolNames === "function" ? { "pi_coding_agent.active_tools": session.getActiveToolNames() } : {}
25212
+ };
25213
+ }
25214
+ function extractPromptOptionsMetadata(options) {
25215
+ if (!options) {
25216
+ return {};
25217
+ }
25218
+ return {
25219
+ ...options.source ? { "pi_coding_agent.source": options.source } : {},
25220
+ ...options.streamingBehavior ? { "pi_coding_agent.streaming_behavior": options.streamingBehavior } : {},
25221
+ ...options.expandPromptTemplates !== void 0 ? {
25222
+ "pi_coding_agent.expand_prompt_templates": options.expandPromptTemplates
25223
+ } : {}
25224
+ };
25225
+ }
25226
+ function extractStreamOptionsMetadata(options) {
25227
+ if (!options) {
25228
+ return {};
25229
+ }
25230
+ return {
25231
+ ...options.temperature !== void 0 ? { temperature: options.temperature } : {},
25232
+ ...options.maxTokens !== void 0 ? { max_tokens: options.maxTokens } : {},
25233
+ ...options.reasoning ? { "pi_coding_agent.reasoning": options.reasoning } : {},
25234
+ ...options.transport ? { "pi_coding_agent.transport": options.transport } : {},
25235
+ ...options.cacheRetention ? { "pi_coding_agent.cache_retention": options.cacheRetention } : {},
25236
+ ...options.sessionId ? { "pi_coding_agent.session_id": options.sessionId } : {},
25237
+ ...options.timeoutMs !== void 0 ? { "pi_coding_agent.timeout_ms": options.timeoutMs } : {},
25238
+ ...options.maxRetries !== void 0 ? { "pi_coding_agent.max_retries": options.maxRetries } : {},
25239
+ ...options.maxRetryDelayMs !== void 0 ? { "pi_coding_agent.max_retry_delay_ms": options.maxRetryDelayMs } : {},
25240
+ ...options.metadata ? { "pi_coding_agent.metadata": options.metadata } : {}
25241
+ };
25242
+ }
25243
+ function getLlmSpanName(model) {
25244
+ switch (model.api) {
25245
+ case "anthropic-messages":
25246
+ return "anthropic.messages.create";
25247
+ case "openai-completions":
25248
+ return "Chat Completion";
25249
+ case "openai-responses":
25250
+ case "azure-openai-responses":
25251
+ case "openai-codex-responses":
25252
+ return "openai.responses.create";
25253
+ case "google-generative-ai":
25254
+ case "google-vertex":
25255
+ return "generate_content";
25256
+ case "mistral-conversations":
25257
+ return "mistral.chat.stream";
25258
+ case "bedrock-converse-stream":
25259
+ return "bedrock.converse_stream";
25260
+ default:
25261
+ return "pi_ai.streamSimple";
25262
+ }
25263
+ }
25264
+ function extractUsageMetrics2(usage) {
25265
+ if (!usage) {
25266
+ return {};
25267
+ }
25268
+ return cleanMetrics5({
25269
+ completion_tokens: usage.output,
25270
+ prompt_cache_creation_tokens: usage.cacheWrite,
25271
+ prompt_cached_tokens: usage.cacheRead,
25272
+ prompt_tokens: usage.input,
25273
+ tokens: usage.totalTokens ?? usage.tokens
25274
+ });
25275
+ }
25276
+ function addMetrics(target, source) {
25277
+ for (const [key, value] of Object.entries(source)) {
25278
+ target[key] = (target[key] ?? 0) + value;
25279
+ }
25280
+ }
25281
+ function buildDurationMetrics3(startTime) {
25282
+ const end = getCurrentUnixTimestamp();
25283
+ return {
25284
+ duration: end - startTime,
25285
+ end,
25286
+ start: startTime
25287
+ };
25288
+ }
25289
+ function cleanMetrics5(metrics) {
25290
+ const cleaned = {};
25291
+ for (const [key, value] of Object.entries(metrics)) {
25292
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
25293
+ cleaned[key] = value;
25294
+ }
25295
+ }
25296
+ return cleaned;
25297
+ }
25298
+ function stringifyArguments(value) {
25299
+ if (typeof value === "string") {
25300
+ return value;
25301
+ }
25302
+ try {
25303
+ return JSON.stringify(value);
25304
+ } catch {
25305
+ return stringifyUnknown2(value);
25306
+ }
25307
+ }
25308
+ function stringifyUnknown2(value) {
25309
+ if (value instanceof Error) {
25310
+ return value.message;
25311
+ }
25312
+ if (typeof value === "string") {
25313
+ return value;
25314
+ }
25315
+ try {
25316
+ return JSON.stringify(value);
25317
+ } catch {
25318
+ return String(value);
25319
+ }
25320
+ }
25321
+ function safeLog4(span, event) {
25322
+ try {
25323
+ span.log(event);
25324
+ } catch (error) {
25325
+ logInstrumentationError4("Pi Coding Agent span log", error);
25326
+ }
25327
+ }
25328
+ function logInstrumentationError4(context, error) {
25329
+ debugLogger.debug(`${context}:`, error);
25330
+ }
25331
+
23281
25332
  // src/instrumentation/braintrust-plugin.ts
23282
25333
  function getIntegrationConfig(integrations, key) {
23283
25334
  return integrations[key];
@@ -23303,6 +25354,7 @@ var BraintrustPlugin = class extends BasePlugin {
23303
25354
  gitHubCopilotPlugin = null;
23304
25355
  fluePlugin = null;
23305
25356
  langChainPlugin = null;
25357
+ piCodingAgentPlugin = null;
23306
25358
  constructor(config = {}) {
23307
25359
  super();
23308
25360
  this.config = config;
@@ -23377,6 +25429,10 @@ var BraintrustPlugin = class extends BasePlugin {
23377
25429
  this.gitHubCopilotPlugin = new GitHubCopilotPlugin();
23378
25430
  this.gitHubCopilotPlugin.enable();
23379
25431
  }
25432
+ if (integrations.piCodingAgent !== false) {
25433
+ this.piCodingAgentPlugin = new PiCodingAgentPlugin();
25434
+ this.piCodingAgentPlugin.enable();
25435
+ }
23380
25436
  if (getIntegrationConfig(integrations, "flue") !== false) {
23381
25437
  this.fluePlugin = new FluePlugin();
23382
25438
  this.fluePlugin.enable();
@@ -23455,6 +25511,10 @@ var BraintrustPlugin = class extends BasePlugin {
23455
25511
  this.gitHubCopilotPlugin.disable();
23456
25512
  this.gitHubCopilotPlugin = null;
23457
25513
  }
25514
+ if (this.piCodingAgentPlugin) {
25515
+ this.piCodingAgentPlugin.disable();
25516
+ this.piCodingAgentPlugin = null;
25517
+ }
23458
25518
  if (this.fluePlugin) {
23459
25519
  this.fluePlugin.disable();
23460
25520
  this.fluePlugin = null;
@@ -23474,6 +25534,11 @@ var envIntegrationAliases = {
23474
25534
  openaicodexsdk: "openaiCodexSDK",
23475
25535
  codex: "openaiCodexSDK",
23476
25536
  "codex-sdk": "openaiCodexSDK",
25537
+ "pi-coding-agent": "piCodingAgent",
25538
+ "pi-coding-agent-sdk": "piCodingAgent",
25539
+ picodingagent: "piCodingAgent",
25540
+ picodingagentsdk: "piCodingAgent",
25541
+ "@earendil-works/pi-coding-agent": "piCodingAgent",
23477
25542
  anthropic: "anthropic",
23478
25543
  aisdk: "aisdk",
23479
25544
  "ai-sdk": "aisdk",
@@ -23539,7 +25604,8 @@ function getDefaultInstrumentationIntegrations() {
23539
25604
  genkit: true,
23540
25605
  gitHubCopilot: true,
23541
25606
  langchain: true,
23542
- langgraph: true
25607
+ langgraph: true,
25608
+ piCodingAgent: true
23543
25609
  };
23544
25610
  }
23545
25611
  function readDisabledInstrumentationEnvConfig(disabledList) {