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
@@ -7133,6 +7133,10 @@ var Experiment2 = class extends ObjectFetcher {
7133
7133
  return (await this.lazyMetadata.get()).project;
7134
7134
  })();
7135
7135
  }
7136
+ async _getBaseExperimentId() {
7137
+ const baseExperimentId = (await this.lazyMetadata.get()).experiment.fullInfo["base_exp_id"];
7138
+ return typeof baseExperimentId === "string" && baseExperimentId ? baseExperimentId : void 0;
7139
+ }
7136
7140
  parentObjectType() {
7137
7141
  return 1 /* EXPERIMENT */;
7138
7142
  }
@@ -7273,6 +7277,14 @@ var Experiment2 = class extends ObjectFetcher {
7273
7277
  comparisonExperimentId = baseExperiment.id;
7274
7278
  comparisonExperimentName = baseExperiment.name;
7275
7279
  }
7280
+ } else {
7281
+ try {
7282
+ const comparisonExperiment = await state.apiConn().get_json(`v1/experiment/${comparisonExperimentId}`);
7283
+ if (typeof comparisonExperiment["name"] === "string") {
7284
+ comparisonExperimentName = comparisonExperiment["name"];
7285
+ }
7286
+ } catch {
7287
+ }
7276
7288
  }
7277
7289
  try {
7278
7290
  const results = await state.apiConn().get_json(
@@ -8532,6 +8544,64 @@ function isValidChannelName(channelName) {
8532
8544
  return /^braintrust:[^:]+:.+$/.test(channelName);
8533
8545
  }
8534
8546
 
8547
+ // src/instrumentation/auto-instrumentation-suppression.ts
8548
+ var autoInstrumentationSuppressionStore;
8549
+ function suppressionStore() {
8550
+ autoInstrumentationSuppressionStore ??= isomorph_default.newAsyncLocalStorage();
8551
+ return autoInstrumentationSuppressionStore;
8552
+ }
8553
+ function currentFrames() {
8554
+ return suppressionStore().getStore()?.frames ?? [];
8555
+ }
8556
+ function isAutoInstrumentationSuppressed() {
8557
+ const frames = currentFrames();
8558
+ return frames[frames.length - 1]?.mode === "suppress";
8559
+ }
8560
+ function runWithAutoInstrumentationSuppressed(callback) {
8561
+ const frame = {
8562
+ id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-suppress"),
8563
+ mode: "suppress"
8564
+ };
8565
+ return suppressionStore().run(
8566
+ { frames: [...currentFrames(), frame] },
8567
+ callback
8568
+ );
8569
+ }
8570
+ function bindAutoInstrumentationSuppressionToStart(tracingChannel) {
8571
+ const startChannel = tracingChannel.start;
8572
+ if (!startChannel) {
8573
+ return void 0;
8574
+ }
8575
+ const store = suppressionStore();
8576
+ startChannel.bindStore(store, () => ({
8577
+ frames: [
8578
+ ...currentFrames(),
8579
+ {
8580
+ id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-suppress"),
8581
+ mode: "suppress"
8582
+ }
8583
+ ]
8584
+ }));
8585
+ return () => {
8586
+ startChannel.unbindStore(store);
8587
+ };
8588
+ }
8589
+ function enterAutoInstrumentationAllowed() {
8590
+ const frame = {
8591
+ id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-allow"),
8592
+ mode: "allow"
8593
+ };
8594
+ suppressionStore().enterWith({
8595
+ frames: [...currentFrames(), frame]
8596
+ });
8597
+ return () => {
8598
+ const frames = currentFrames().filter(
8599
+ (candidate) => candidate.id !== frame.id
8600
+ );
8601
+ suppressionStore().enterWith(frames.length > 0 ? { frames } : void 0);
8602
+ };
8603
+ }
8604
+
8535
8605
  // src/instrumentation/core/channel-tracing.ts
8536
8606
  function isSyncStreamLike(value) {
8537
8607
  return !!value && typeof value === "object" && typeof value.on === "function";
@@ -8563,16 +8633,33 @@ function startSpanForEvent(config, event, channelName) {
8563
8633
  metadata: mergeInputMetadata(metadata, spanInfoMetadata)
8564
8634
  });
8565
8635
  } catch (error) {
8566
- console.error(`Error extracting input for ${channelName}:`, error);
8636
+ debugLogger.error(`Error extracting input for ${channelName}:`, error);
8567
8637
  }
8568
8638
  return { span, startTime };
8569
8639
  }
8640
+ function shouldTraceEvent(config, event, channelName) {
8641
+ if (!config.shouldTrace) {
8642
+ return true;
8643
+ }
8644
+ try {
8645
+ return config.shouldTrace(event.arguments, event);
8646
+ } catch (error) {
8647
+ debugLogger.error(
8648
+ `Error checking trace predicate for ${channelName}:`,
8649
+ error
8650
+ );
8651
+ return true;
8652
+ }
8653
+ }
8570
8654
  function ensureSpanStateForEvent(states, config, event, channelName) {
8571
8655
  const key = event;
8572
8656
  const existing = states.get(key);
8573
8657
  if (existing) {
8574
8658
  return existing;
8575
8659
  }
8660
+ if (!shouldTraceEvent(config, event, channelName)) {
8661
+ return void 0;
8662
+ }
8576
8663
  const created = startSpanForEvent(config, event, channelName);
8577
8664
  states.set(key, created);
8578
8665
  return created;
@@ -8588,13 +8675,16 @@ function bindCurrentSpanStoreToStart(tracingChannel, states, config, channelName
8588
8675
  startChannel.bindStore(
8589
8676
  currentSpanStore,
8590
8677
  (event) => {
8591
- const span = ensureSpanStateForEvent(
8678
+ if (isAutoInstrumentationSuppressed()) {
8679
+ return currentSpanStore.getStore();
8680
+ }
8681
+ const spanState = ensureSpanStateForEvent(
8592
8682
  states,
8593
8683
  config,
8594
8684
  event,
8595
8685
  channelName
8596
- ).span;
8597
- return contextManager.wrapSpanForStore(span);
8686
+ );
8687
+ return spanState ? contextManager.wrapSpanForStore(spanState.span) : currentSpanStore.getStore();
8598
8688
  }
8599
8689
  );
8600
8690
  return () => {
@@ -8629,7 +8719,10 @@ function runStreamingCompletionHook(args) {
8629
8719
  startTime: args.startTime
8630
8720
  });
8631
8721
  } catch (error) {
8632
- console.error(`Error in onComplete hook for ${args.channelName}:`, error);
8722
+ debugLogger.error(
8723
+ `Error in onComplete hook for ${args.channelName}:`,
8724
+ error
8725
+ );
8633
8726
  }
8634
8727
  }
8635
8728
  function traceAsyncChannel(channel2, config) {
@@ -8644,6 +8737,9 @@ function traceAsyncChannel(channel2, config) {
8644
8737
  );
8645
8738
  const handlers = {
8646
8739
  start: (event) => {
8740
+ if (isAutoInstrumentationSuppressed()) {
8741
+ return;
8742
+ }
8647
8743
  ensureSpanStateForEvent(
8648
8744
  states,
8649
8745
  config,
@@ -8678,7 +8774,7 @@ function traceAsyncChannel(channel2, config) {
8678
8774
  metrics
8679
8775
  });
8680
8776
  } catch (error) {
8681
- console.error(`Error extracting output for ${channelName}:`, error);
8777
+ debugLogger.error(`Error extracting output for ${channelName}:`, error);
8682
8778
  } finally {
8683
8779
  span.end();
8684
8780
  states.delete(event);
@@ -8706,6 +8802,9 @@ function traceStreamingChannel(channel2, config) {
8706
8802
  );
8707
8803
  const handlers = {
8708
8804
  start: (event) => {
8805
+ if (isAutoInstrumentationSuppressed()) {
8806
+ return;
8807
+ }
8709
8808
  ensureSpanStateForEvent(
8710
8809
  states,
8711
8810
  config,
@@ -8777,7 +8876,7 @@ function traceStreamingChannel(channel2, config) {
8777
8876
  metrics
8778
8877
  });
8779
8878
  } catch (error) {
8780
- console.error(
8879
+ debugLogger.error(
8781
8880
  `Error extracting output for ${channelName}:`,
8782
8881
  error
8783
8882
  );
@@ -8837,7 +8936,7 @@ function traceStreamingChannel(channel2, config) {
8837
8936
  metrics
8838
8937
  });
8839
8938
  } catch (error) {
8840
- console.error(`Error extracting output for ${channelName}:`, error);
8939
+ debugLogger.error(`Error extracting output for ${channelName}:`, error);
8841
8940
  } finally {
8842
8941
  span.end();
8843
8942
  states.delete(event);
@@ -8865,6 +8964,9 @@ function traceSyncStreamChannel(channel2, config) {
8865
8964
  );
8866
8965
  const handlers = {
8867
8966
  start: (event) => {
8967
+ if (isAutoInstrumentationSuppressed()) {
8968
+ return;
8969
+ }
8868
8970
  ensureSpanStateForEvent(
8869
8971
  states,
8870
8972
  config,
@@ -8918,7 +9020,7 @@ function traceSyncStreamChannel(channel2, config) {
8918
9020
  });
8919
9021
  }
8920
9022
  } catch (error) {
8921
- console.error(
9023
+ debugLogger.error(
8922
9024
  `Error extracting chatCompletion for ${channelName}:`,
8923
9025
  error
8924
9026
  );
@@ -8942,7 +9044,10 @@ function traceSyncStreamChannel(channel2, config) {
8942
9044
  span.log(extracted);
8943
9045
  }
8944
9046
  } catch (error) {
8945
- console.error(`Error extracting event for ${channelName}:`, error);
9047
+ debugLogger.error(
9048
+ `Error extracting event for ${channelName}:`,
9049
+ error
9050
+ );
8946
9051
  }
8947
9052
  });
8948
9053
  stream.on("end", () => {
@@ -10471,6 +10576,9 @@ var AnthropicPlugin = class extends BasePlugin {
10471
10576
  const states = /* @__PURE__ */ new WeakMap();
10472
10577
  const handlers = {
10473
10578
  start: (event) => {
10579
+ if (isAutoInstrumentationSuppressed()) {
10580
+ return;
10581
+ }
10474
10582
  const params = event.arguments[0] ?? {};
10475
10583
  const span = startSpan({
10476
10584
  name: "anthropic.beta.messages.toolRunner",
@@ -11200,6 +11308,520 @@ function serializeAISDKToolsForLogging(tools) {
11200
11308
  return serialized;
11201
11309
  }
11202
11310
 
11311
+ // src/wrappers/ai-sdk/telemetry.ts
11312
+ function braintrustAISDKTelemetry() {
11313
+ const operations = /* @__PURE__ */ new Map();
11314
+ const modelSpans = /* @__PURE__ */ new Map();
11315
+ const objectSpans = /* @__PURE__ */ new Map();
11316
+ const embedSpans = /* @__PURE__ */ new Map();
11317
+ const rerankSpans = /* @__PURE__ */ new Map();
11318
+ const toolSpans = /* @__PURE__ */ new Map();
11319
+ const runSafely = (name, callback) => {
11320
+ try {
11321
+ callback();
11322
+ } catch (error) {
11323
+ console.error(`Error in Braintrust AI SDK telemetry ${name}:`, error);
11324
+ }
11325
+ };
11326
+ const startChildSpan = (callId, name, type, event) => {
11327
+ const parent = operations.get(callId)?.span;
11328
+ const spanArgs = {
11329
+ name,
11330
+ spanAttributes: { type },
11331
+ ...event ? { event } : {}
11332
+ };
11333
+ const span = parent ? parent.startSpan(spanArgs) : startSpan(spanArgs);
11334
+ const state = operations.get(callId);
11335
+ if (state && type === "llm" /* LLM */) {
11336
+ state.hadModelChild = true;
11337
+ }
11338
+ return span;
11339
+ };
11340
+ return {
11341
+ onStart(event) {
11342
+ runSafely("onStart", () => {
11343
+ const operationName = operationNameFromId(event.operationId);
11344
+ const span = startSpan({
11345
+ name: operationName,
11346
+ spanAttributes: { type: "function" /* FUNCTION */ }
11347
+ });
11348
+ operations.set(event.callId, {
11349
+ hadModelChild: false,
11350
+ operationName,
11351
+ span,
11352
+ startTime: getCurrentUnixTimestamp()
11353
+ });
11354
+ const metadata = metadataFromEvent(event);
11355
+ const logPayload = { metadata };
11356
+ if (shouldRecordInputs(event)) {
11357
+ const { input, outputPromise } = processAISDKCallInput(
11358
+ operationInput(event, operationName)
11359
+ );
11360
+ logPayload.input = input;
11361
+ if (outputPromise && input && typeof input === "object") {
11362
+ outputPromise.then((resolvedData) => {
11363
+ span.log({
11364
+ input: {
11365
+ ...input,
11366
+ ...resolvedData
11367
+ }
11368
+ });
11369
+ }).catch(() => {
11370
+ });
11371
+ }
11372
+ }
11373
+ span.log(logPayload);
11374
+ });
11375
+ },
11376
+ onLanguageModelCallStart(event) {
11377
+ runSafely("onLanguageModelCallStart", () => {
11378
+ const state = operations.get(event.callId);
11379
+ const openSpans = modelSpans.get(event.callId);
11380
+ if (openSpans) {
11381
+ for (const span2 of openSpans) {
11382
+ span2.end();
11383
+ }
11384
+ modelSpans.delete(event.callId);
11385
+ }
11386
+ const span = startChildSpan(
11387
+ event.callId,
11388
+ state?.operationName === "streamText" ? "doStream" : "doGenerate",
11389
+ "llm" /* LLM */,
11390
+ {
11391
+ ...shouldRecordInputs(event) ? {
11392
+ input: processAISDKCallInput(
11393
+ operationInput(
11394
+ event,
11395
+ state?.operationName ?? "generateText"
11396
+ )
11397
+ ).input
11398
+ } : {},
11399
+ metadata: metadataFromEvent(event)
11400
+ }
11401
+ );
11402
+ const spans = modelSpans.get(event.callId) ?? [];
11403
+ spans.push(span);
11404
+ modelSpans.set(event.callId, spans);
11405
+ });
11406
+ },
11407
+ onLanguageModelCallEnd(event) {
11408
+ runSafely("onLanguageModelCallEnd", () => {
11409
+ const span = shiftModelSpan(modelSpans, event.callId);
11410
+ if (!span) {
11411
+ return;
11412
+ }
11413
+ const result = {
11414
+ ...event,
11415
+ response: event.responseId ? { id: event.responseId } : void 0
11416
+ };
11417
+ span.log({
11418
+ ...shouldRecordOutputs(event) ? {
11419
+ output: processAISDKOutput(result, DEFAULT_DENY_OUTPUT_PATHS)
11420
+ } : {},
11421
+ metrics: extractTokenMetrics(result)
11422
+ });
11423
+ span.end();
11424
+ });
11425
+ },
11426
+ onObjectStepStart(event) {
11427
+ runSafely("onObjectStepStart", () => {
11428
+ const state = operations.get(event.callId);
11429
+ const openSpan = objectSpans.get(event.callId);
11430
+ if (openSpan) {
11431
+ openSpan.end();
11432
+ objectSpans.delete(event.callId);
11433
+ }
11434
+ const span = startChildSpan(
11435
+ event.callId,
11436
+ state?.operationName === "streamObject" ? "doStream" : "doGenerate",
11437
+ "llm" /* LLM */,
11438
+ {
11439
+ ...shouldRecordInputs(event) ? {
11440
+ input: {
11441
+ prompt: event.promptMessages
11442
+ }
11443
+ } : {},
11444
+ metadata: metadataFromEvent(event)
11445
+ }
11446
+ );
11447
+ objectSpans.set(event.callId, span);
11448
+ });
11449
+ },
11450
+ onObjectStepFinish(event) {
11451
+ runSafely("onObjectStepFinish", () => {
11452
+ const span = objectSpans.get(event.callId);
11453
+ if (!span) {
11454
+ return;
11455
+ }
11456
+ const result = {
11457
+ ...event,
11458
+ text: event.objectText
11459
+ };
11460
+ span.log({
11461
+ ...shouldRecordOutputs(event) ? {
11462
+ output: processAISDKOutput(result, DEFAULT_DENY_OUTPUT_PATHS)
11463
+ } : {},
11464
+ metrics: extractTokenMetrics(result)
11465
+ });
11466
+ span.end();
11467
+ objectSpans.delete(event.callId);
11468
+ });
11469
+ },
11470
+ onEmbedStart(event) {
11471
+ runSafely("onEmbedStart", () => {
11472
+ const state = operations.get(event.callId);
11473
+ for (const [embedCallId, embedState] of embedSpans) {
11474
+ if (embedState.callId === event.callId && (state?.operationName === "embed" || embedState.values === event.values)) {
11475
+ embedState.span.end();
11476
+ embedSpans.delete(embedCallId);
11477
+ }
11478
+ }
11479
+ const span = startChildSpan(
11480
+ event.callId,
11481
+ "doEmbed",
11482
+ "llm" /* LLM */,
11483
+ {
11484
+ ...shouldRecordInputs(event) ? {
11485
+ input: {
11486
+ values: event.values
11487
+ }
11488
+ } : {},
11489
+ metadata: metadataFromEvent(event)
11490
+ }
11491
+ );
11492
+ embedSpans.set(event.embedCallId, {
11493
+ callId: event.callId,
11494
+ span,
11495
+ values: event.values
11496
+ });
11497
+ });
11498
+ },
11499
+ onEmbedFinish(event) {
11500
+ runSafely("onEmbedFinish", () => {
11501
+ const state = embedSpans.get(event.embedCallId);
11502
+ if (!state) {
11503
+ return;
11504
+ }
11505
+ const result = {
11506
+ ...event,
11507
+ embeddings: event.embeddings
11508
+ };
11509
+ state.span.log({
11510
+ ...shouldRecordOutputs(event) ? {
11511
+ output: processAISDKEmbeddingOutput(
11512
+ result,
11513
+ DEFAULT_DENY_OUTPUT_PATHS
11514
+ )
11515
+ } : {},
11516
+ metrics: extractTokenMetrics(result)
11517
+ });
11518
+ state.span.end();
11519
+ embedSpans.delete(event.embedCallId);
11520
+ });
11521
+ },
11522
+ onRerankStart(event) {
11523
+ runSafely("onRerankStart", () => {
11524
+ const openSpan = rerankSpans.get(event.callId);
11525
+ if (openSpan) {
11526
+ openSpan.end();
11527
+ rerankSpans.delete(event.callId);
11528
+ }
11529
+ const span = startChildSpan(
11530
+ event.callId,
11531
+ "doRerank",
11532
+ "llm" /* LLM */,
11533
+ {
11534
+ ...shouldRecordInputs(event) ? {
11535
+ input: {
11536
+ documents: event.documents,
11537
+ query: event.query,
11538
+ topN: event.topN
11539
+ }
11540
+ } : {},
11541
+ metadata: metadataFromEvent(event)
11542
+ }
11543
+ );
11544
+ rerankSpans.set(event.callId, span);
11545
+ });
11546
+ },
11547
+ onRerankFinish(event) {
11548
+ runSafely("onRerankFinish", () => {
11549
+ const span = rerankSpans.get(event.callId);
11550
+ if (!span) {
11551
+ return;
11552
+ }
11553
+ const result = {
11554
+ ranking: event.ranking?.map((entry) => ({
11555
+ originalIndex: entry.index,
11556
+ score: entry.relevanceScore
11557
+ }))
11558
+ };
11559
+ span.log({
11560
+ ...shouldRecordOutputs(event) ? {
11561
+ output: processAISDKRerankOutput(
11562
+ result,
11563
+ DEFAULT_DENY_OUTPUT_PATHS
11564
+ )
11565
+ } : {}
11566
+ });
11567
+ span.end();
11568
+ rerankSpans.delete(event.callId);
11569
+ });
11570
+ },
11571
+ onToolExecutionStart(event) {
11572
+ runSafely("onToolExecutionStart", () => {
11573
+ const toolCallId = event.toolCall.toolCallId;
11574
+ if (!toolCallId) {
11575
+ return;
11576
+ }
11577
+ const span = startChildSpan(
11578
+ event.callId,
11579
+ event.toolCall.toolName || "tool",
11580
+ "tool" /* TOOL */,
11581
+ {
11582
+ ...shouldRecordInputs(event) ? {
11583
+ input: event.toolCall.input
11584
+ } : {},
11585
+ metadata: {
11586
+ ...createAISDKIntegrationMetadata(),
11587
+ toolCallId,
11588
+ ...typeof event.toolCall.toolName === "string" ? { toolName: event.toolCall.toolName } : {}
11589
+ }
11590
+ }
11591
+ );
11592
+ toolSpans.set(toolCallId, { callId: event.callId, span });
11593
+ });
11594
+ },
11595
+ onToolExecutionEnd(event) {
11596
+ runSafely("onToolExecutionEnd", () => {
11597
+ const toolCallId = event.toolCall.toolCallId;
11598
+ const state = toolCallId ? toolSpans.get(toolCallId) : void 0;
11599
+ if (!toolCallId || !state) {
11600
+ return;
11601
+ }
11602
+ const toolOutput = event.toolOutput;
11603
+ if (toolOutput?.type === "tool-error") {
11604
+ state.span.log({
11605
+ error: toolOutput.error instanceof Error ? toolOutput.error.message : String(toolOutput?.error),
11606
+ metrics: typeof event.durationMs === "number" ? { duration_ms: event.durationMs } : {}
11607
+ });
11608
+ } else {
11609
+ state.span.log({
11610
+ ...shouldRecordOutputs(event) ? { output: toolOutput?.output } : {},
11611
+ metrics: typeof event.durationMs === "number" ? { duration_ms: event.durationMs } : {}
11612
+ });
11613
+ }
11614
+ state.span.end();
11615
+ toolSpans.delete(toolCallId);
11616
+ });
11617
+ },
11618
+ onChunk(event) {
11619
+ runSafely("onChunk", () => {
11620
+ const callId = event.chunk?.callId;
11621
+ if (!callId) {
11622
+ return;
11623
+ }
11624
+ const state = operations.get(callId);
11625
+ if (!state || state.firstChunkTime !== void 0) {
11626
+ return;
11627
+ }
11628
+ state.firstChunkTime = getCurrentUnixTimestamp();
11629
+ });
11630
+ },
11631
+ onFinish(event) {
11632
+ runSafely("onFinish", () => {
11633
+ const state = operations.get(event.callId);
11634
+ if (!state) {
11635
+ return;
11636
+ }
11637
+ const result = finishResult(event, state.operationName);
11638
+ const metrics = state.hadModelChild ? {} : extractTokenMetrics(result);
11639
+ if (state.firstChunkTime !== void 0) {
11640
+ metrics.time_to_first_token = state.firstChunkTime - state.startTime;
11641
+ }
11642
+ state.span.log({
11643
+ ...shouldRecordOutputs(event) ? {
11644
+ output: finishOutput(result, state.operationName)
11645
+ } : {},
11646
+ metrics
11647
+ });
11648
+ state.span.end();
11649
+ operations.delete(event.callId);
11650
+ });
11651
+ },
11652
+ onError(event) {
11653
+ runSafely("onError", () => {
11654
+ const errorEvent = isObject(event) ? event : {};
11655
+ const callId = typeof errorEvent.callId === "string" ? errorEvent.callId : void 0;
11656
+ if (!callId) {
11657
+ return;
11658
+ }
11659
+ const state = operations.get(callId);
11660
+ if (!state) {
11661
+ return;
11662
+ }
11663
+ const error = errorEvent.error ?? event;
11664
+ const openModelSpans = modelSpans.get(callId);
11665
+ if (openModelSpans) {
11666
+ for (const span of openModelSpans) {
11667
+ logError(span, error);
11668
+ span.end();
11669
+ }
11670
+ modelSpans.delete(callId);
11671
+ }
11672
+ const openObjectSpan = objectSpans.get(callId);
11673
+ if (openObjectSpan) {
11674
+ logError(openObjectSpan, error);
11675
+ openObjectSpan.end();
11676
+ objectSpans.delete(callId);
11677
+ }
11678
+ for (const [embedCallId, embedState] of embedSpans) {
11679
+ if (embedState.callId === callId) {
11680
+ logError(embedState.span, error);
11681
+ embedState.span.end();
11682
+ embedSpans.delete(embedCallId);
11683
+ }
11684
+ }
11685
+ const openRerankSpan = rerankSpans.get(callId);
11686
+ if (openRerankSpan) {
11687
+ logError(openRerankSpan, error);
11688
+ openRerankSpan.end();
11689
+ rerankSpans.delete(callId);
11690
+ }
11691
+ for (const [toolCallId, toolState] of toolSpans) {
11692
+ if (toolState.callId === callId) {
11693
+ logError(toolState.span, error);
11694
+ toolState.span.end();
11695
+ toolSpans.delete(toolCallId);
11696
+ }
11697
+ }
11698
+ logError(state.span, error);
11699
+ state.span.end();
11700
+ operations.delete(callId);
11701
+ });
11702
+ },
11703
+ executeTool({ toolCallId, execute }) {
11704
+ const state = toolSpans.get(toolCallId);
11705
+ return state ? withCurrent(state.span, () => execute()) : execute();
11706
+ }
11707
+ };
11708
+ }
11709
+ function shouldRecordInputs(event) {
11710
+ return event.recordInputs !== false;
11711
+ }
11712
+ function shouldRecordOutputs(event) {
11713
+ return event.recordOutputs !== false;
11714
+ }
11715
+ function operationNameFromId(operationId) {
11716
+ return operationId?.startsWith("ai.") ? operationId.slice("ai.".length) : operationId || "ai-sdk";
11717
+ }
11718
+ function modelFromEvent(event) {
11719
+ return event.modelId ? {
11720
+ modelId: event.modelId,
11721
+ ...event.provider ? { provider: event.provider } : {}
11722
+ } : void 0;
11723
+ }
11724
+ function metadataFromEvent(event) {
11725
+ const metadata = createAISDKIntegrationMetadata();
11726
+ const { model, provider } = serializeModelWithProvider(modelFromEvent(event));
11727
+ if (model) {
11728
+ metadata.model = model;
11729
+ }
11730
+ if (provider) {
11731
+ metadata.provider = provider;
11732
+ }
11733
+ if (typeof event.functionId === "string") {
11734
+ metadata.functionId = event.functionId;
11735
+ }
11736
+ return metadata;
11737
+ }
11738
+ function operationInput(event, operationName) {
11739
+ if (operationName === "embed") {
11740
+ return {
11741
+ model: modelFromEvent(event),
11742
+ value: event.value
11743
+ };
11744
+ }
11745
+ if (operationName === "embedMany") {
11746
+ return {
11747
+ model: modelFromEvent(event),
11748
+ values: Array.isArray(event.value) ? event.value ?? [] : void 0
11749
+ };
11750
+ }
11751
+ if (operationName === "rerank") {
11752
+ return {
11753
+ model: modelFromEvent(event),
11754
+ documents: event.documents,
11755
+ query: event.query,
11756
+ topN: event.topN
11757
+ };
11758
+ }
11759
+ return {
11760
+ model: modelFromEvent(event),
11761
+ system: event.system,
11762
+ prompt: event.prompt,
11763
+ messages: event.messages,
11764
+ tools: event.tools,
11765
+ toolChoice: event.toolChoice,
11766
+ activeTools: event.activeTools,
11767
+ output: event.output,
11768
+ schema: event.schema,
11769
+ schemaName: event.schemaName,
11770
+ schemaDescription: event.schemaDescription,
11771
+ maxOutputTokens: event.maxOutputTokens,
11772
+ temperature: event.temperature,
11773
+ topP: event.topP,
11774
+ topK: event.topK,
11775
+ presencePenalty: event.presencePenalty,
11776
+ frequencyPenalty: event.frequencyPenalty,
11777
+ seed: event.seed,
11778
+ maxRetries: event.maxRetries,
11779
+ headers: event.headers,
11780
+ providerOptions: event.providerOptions
11781
+ };
11782
+ }
11783
+ function shiftModelSpan(modelSpans, callId) {
11784
+ const spans = modelSpans.get(callId);
11785
+ const span = spans?.shift();
11786
+ if (spans && spans.length === 0) {
11787
+ modelSpans.delete(callId);
11788
+ }
11789
+ return span;
11790
+ }
11791
+ function finishResult(event, operationName) {
11792
+ if (operationName === "embed") {
11793
+ return {
11794
+ ...event,
11795
+ embedding: event.embedding
11796
+ };
11797
+ }
11798
+ if (operationName === "embedMany") {
11799
+ return {
11800
+ ...event,
11801
+ embeddings: event.embedding
11802
+ };
11803
+ }
11804
+ if (operationName === "rerank") {
11805
+ return event;
11806
+ }
11807
+ return event;
11808
+ }
11809
+ function finishOutput(result, operationName) {
11810
+ if (operationName === "embed" || operationName === "embedMany") {
11811
+ return processAISDKEmbeddingOutput(
11812
+ result,
11813
+ DEFAULT_DENY_OUTPUT_PATHS
11814
+ );
11815
+ }
11816
+ if (operationName === "rerank") {
11817
+ return processAISDKRerankOutput(
11818
+ result,
11819
+ DEFAULT_DENY_OUTPUT_PATHS
11820
+ );
11821
+ }
11822
+ return processAISDKOutput(result, DEFAULT_DENY_OUTPUT_PATHS);
11823
+ }
11824
+
11203
11825
  // src/instrumentation/plugins/ai-sdk-channels.ts
11204
11826
  var aiSDKChannels = defineChannels("ai", {
11205
11827
  generateText: channel({
@@ -11259,6 +11881,10 @@ var aiSDKChannels = defineChannels("ai", {
11259
11881
  toolLoopAgentStream: channel({
11260
11882
  channelName: "ToolLoopAgent.stream",
11261
11883
  kind: "async"
11884
+ }),
11885
+ v7CreateTelemetryDispatcher: channel({
11886
+ channelName: "createTelemetryDispatcher",
11887
+ kind: "sync-stream"
11262
11888
  })
11263
11889
  });
11264
11890
 
@@ -11279,9 +11905,30 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
11279
11905
  ];
11280
11906
  var AUTO_PATCHED_MODEL = /* @__PURE__ */ Symbol.for("braintrust.ai-sdk.auto-patched-model");
11281
11907
  var AUTO_PATCHED_TOOL = /* @__PURE__ */ Symbol.for("braintrust.ai-sdk.auto-patched-tool");
11908
+ var AUTO_PATCHED_V7_TELEMETRY_DISPATCHER = /* @__PURE__ */ Symbol.for(
11909
+ "braintrust.ai-sdk.v7.auto-patched-telemetry-dispatcher"
11910
+ );
11282
11911
  var RUNTIME_DENY_OUTPUT_PATHS = /* @__PURE__ */ Symbol.for(
11283
11912
  "braintrust.ai-sdk.deny-output-paths"
11284
11913
  );
11914
+ var AI_SDK_V7_TELEMETRY_CALLBACKS = [
11915
+ "onStart",
11916
+ "onStepStart",
11917
+ "onLanguageModelCallStart",
11918
+ "onLanguageModelCallEnd",
11919
+ "onToolExecutionStart",
11920
+ "onToolExecutionEnd",
11921
+ "onChunk",
11922
+ "onStepFinish",
11923
+ "onObjectStepStart",
11924
+ "onObjectStepFinish",
11925
+ "onEmbedStart",
11926
+ "onEmbedFinish",
11927
+ "onRerankStart",
11928
+ "onRerankFinish",
11929
+ "onFinish",
11930
+ "onError"
11931
+ ];
11285
11932
  var AISDKPlugin = class extends BasePlugin {
11286
11933
  config;
11287
11934
  constructor(config = {}) {
@@ -11296,6 +11943,7 @@ var AISDKPlugin = class extends BasePlugin {
11296
11943
  }
11297
11944
  subscribeToAISDK() {
11298
11945
  const denyOutputPaths = this.config.denyOutputPaths || DEFAULT_DENY_OUTPUT_PATHS;
11946
+ this.unsubscribers.push(subscribeToAISDKV7TelemetryDispatcher());
11299
11947
  this.unsubscribers.push(
11300
11948
  traceStreamingChannel(aiSDKChannels.generateText, {
11301
11949
  name: "generateText",
@@ -11520,17 +12168,68 @@ var AISDKPlugin = class extends BasePlugin {
11520
12168
  );
11521
12169
  }
11522
12170
  };
11523
- function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
11524
- if (Array.isArray(event?.denyOutputPaths)) {
11525
- return event.denyOutputPaths;
11526
- }
11527
- const firstArgument2 = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
11528
- if (!firstArgument2 || typeof firstArgument2 !== "object") {
11529
- return defaultDenyOutputPaths;
11530
- }
11531
- const runtimeDenyOutputPaths = firstArgument2[RUNTIME_DENY_OUTPUT_PATHS];
11532
- if (Array.isArray(runtimeDenyOutputPaths) && runtimeDenyOutputPaths.every((path) => typeof path === "string")) {
11533
- return runtimeDenyOutputPaths;
12171
+ function subscribeToAISDKV7TelemetryDispatcher() {
12172
+ const channel2 = aiSDKChannels.v7CreateTelemetryDispatcher.tracingChannel();
12173
+ const telemetry = braintrustAISDKTelemetry();
12174
+ const handlers = {
12175
+ end: (event) => {
12176
+ const telemetryOptions = event.arguments?.[0]?.telemetry;
12177
+ if (telemetryOptions?.isEnabled === false) {
12178
+ return;
12179
+ }
12180
+ patchAISDKV7TelemetryDispatcher(event.result, telemetry);
12181
+ }
12182
+ };
12183
+ channel2.subscribe(handlers);
12184
+ return () => {
12185
+ channel2.unsubscribe(handlers);
12186
+ };
12187
+ }
12188
+ function patchAISDKV7TelemetryDispatcher(dispatcher, telemetry) {
12189
+ if (!isObject(dispatcher)) {
12190
+ return;
12191
+ }
12192
+ const dispatcherRecord = dispatcher;
12193
+ if (dispatcherRecord[AUTO_PATCHED_V7_TELEMETRY_DISPATCHER]) {
12194
+ return;
12195
+ }
12196
+ dispatcherRecord[AUTO_PATCHED_V7_TELEMETRY_DISPATCHER] = true;
12197
+ for (const key of AI_SDK_V7_TELEMETRY_CALLBACKS) {
12198
+ const braintrustCallback = telemetry[key];
12199
+ if (typeof braintrustCallback !== "function") {
12200
+ continue;
12201
+ }
12202
+ const existingCallback = dispatcherRecord[key];
12203
+ dispatcherRecord[key] = (event) => {
12204
+ const existingResult = typeof existingCallback === "function" ? existingCallback.call(dispatcher, event) : void 0;
12205
+ const braintrustResult = braintrustCallback.call(telemetry, event);
12206
+ const pending = [existingResult, braintrustResult].filter(isPromiseLike);
12207
+ if (pending.length > 0) {
12208
+ return Promise.allSettled(pending).then(() => void 0);
12209
+ }
12210
+ };
12211
+ }
12212
+ const braintrustExecuteTool = telemetry.executeTool;
12213
+ if (typeof braintrustExecuteTool !== "function") {
12214
+ return;
12215
+ }
12216
+ const existingExecuteTool = dispatcherRecord.executeTool;
12217
+ dispatcherRecord.executeTool = (args) => braintrustExecuteTool.call(telemetry, {
12218
+ ...args,
12219
+ execute: () => typeof existingExecuteTool === "function" ? existingExecuteTool.call(dispatcher, args) : args.execute()
12220
+ });
12221
+ }
12222
+ function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
12223
+ if (Array.isArray(event?.denyOutputPaths)) {
12224
+ return event.denyOutputPaths;
12225
+ }
12226
+ const firstArgument2 = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
12227
+ if (!firstArgument2 || typeof firstArgument2 !== "object") {
12228
+ return defaultDenyOutputPaths;
12229
+ }
12230
+ const runtimeDenyOutputPaths = firstArgument2[RUNTIME_DENY_OUTPUT_PATHS];
12231
+ if (Array.isArray(runtimeDenyOutputPaths) && runtimeDenyOutputPaths.every((path) => typeof path === "string")) {
12232
+ return runtimeDenyOutputPaths;
11534
12233
  }
11535
12234
  return defaultDenyOutputPaths;
11536
12235
  }
@@ -11971,11 +12670,11 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
11971
12670
  metadata: baseMetadata
11972
12671
  }
11973
12672
  });
12673
+ const streamStartTime = getCurrentUnixTimestamp();
11974
12674
  const result = await withCurrent(
11975
12675
  span,
11976
12676
  () => Reflect.apply(originalDoStream, resolvedModel, [options])
11977
12677
  );
11978
- const streamStartTime = getCurrentUnixTimestamp();
11979
12678
  let firstChunkTime;
11980
12679
  const output = {};
11981
12680
  let text = "";
@@ -11984,7 +12683,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
11984
12683
  let object = void 0;
11985
12684
  const transformStream = new TransformStream({
11986
12685
  transform(chunk, controller) {
11987
- if (firstChunkTime === void 0) {
12686
+ if (firstChunkTime === void 0 && isAISDKContentStreamChunk(chunk)) {
11988
12687
  firstChunkTime = getCurrentUnixTimestamp();
11989
12688
  }
11990
12689
  switch (chunk.type) {
@@ -12176,6 +12875,78 @@ function finalizeAISDKChildTracing(event) {
12176
12875
  delete event.__braintrust_ai_sdk_cleanup;
12177
12876
  }
12178
12877
  }
12878
+ function extractAISDKStreamPart(chunk) {
12879
+ if (!isObject(chunk) || !isObject(chunk.part)) {
12880
+ return chunk;
12881
+ }
12882
+ return chunk.part;
12883
+ }
12884
+ function stringContent(value) {
12885
+ return typeof value === "string" && value.length > 0 ? value : void 0;
12886
+ }
12887
+ function rawValueHasAISDKContent(value) {
12888
+ if (value === void 0 || value === null) {
12889
+ return false;
12890
+ }
12891
+ if (typeof value === "string") {
12892
+ return value.length > 0;
12893
+ }
12894
+ if (!isObject(value)) {
12895
+ return true;
12896
+ }
12897
+ const delta = value.delta;
12898
+ 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) => {
12899
+ if (!isObject(choice) || !isObject(choice.delta)) {
12900
+ return false;
12901
+ }
12902
+ const choiceDelta = choice.delta;
12903
+ if (stringContent(choiceDelta.content) || stringContent(choiceDelta.text)) {
12904
+ return true;
12905
+ }
12906
+ if (isObject(choiceDelta.function_call) && stringContent(choiceDelta.function_call.arguments)) {
12907
+ return true;
12908
+ }
12909
+ return Array.isArray(choiceDelta.tool_calls) && choiceDelta.tool_calls.some(
12910
+ (toolCall) => isObject(toolCall) && isObject(toolCall.function) && stringContent(toolCall.function.arguments)
12911
+ );
12912
+ })) {
12913
+ return true;
12914
+ }
12915
+ return false;
12916
+ }
12917
+ function isAISDKContentStreamChunk(chunk) {
12918
+ const part = extractAISDKStreamPart(chunk);
12919
+ if (typeof part === "string") {
12920
+ return part.length > 0;
12921
+ }
12922
+ if (!isObject(part) || typeof part.type !== "string") {
12923
+ return false;
12924
+ }
12925
+ switch (part.type) {
12926
+ case "text-delta":
12927
+ return stringContent(part.textDelta) !== void 0 || stringContent(part.delta) !== void 0 || stringContent(part.text) !== void 0 || stringContent(part.content) !== void 0;
12928
+ case "reasoning-delta":
12929
+ return stringContent(part.delta) !== void 0 || stringContent(part.text) !== void 0 || stringContent(part.content) !== void 0;
12930
+ case "tool-call":
12931
+ case "object":
12932
+ case "file":
12933
+ return true;
12934
+ case "tool-input-delta":
12935
+ case "tool-call-delta":
12936
+ 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;
12937
+ case "raw":
12938
+ return rawValueHasAISDKContent(part.rawValue);
12939
+ default:
12940
+ return false;
12941
+ }
12942
+ }
12943
+ function isAISDKContentAsyncIterableChunk(chunk) {
12944
+ if (isAISDKContentStreamChunk(chunk)) {
12945
+ return true;
12946
+ }
12947
+ const part = extractAISDKStreamPart(chunk);
12948
+ return isObject(part) && typeof part.type !== "string";
12949
+ }
12179
12950
  function patchAISDKStreamingResult(args) {
12180
12951
  const { defaultDenyOutputPaths, endEvent, result, span, startTime } = args;
12181
12952
  if (!result || typeof result !== "object") {
@@ -12188,7 +12959,7 @@ function patchAISDKStreamingResult(args) {
12188
12959
  const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
12189
12960
  new TransformStream({
12190
12961
  transform(chunk, controller) {
12191
- if (firstChunkTime2 === void 0) {
12962
+ if (firstChunkTime2 === void 0 && isAISDKContentStreamChunk(chunk)) {
12192
12963
  firstChunkTime2 = getCurrentUnixTimestamp();
12193
12964
  }
12194
12965
  controller.enqueue(chunk);
@@ -12232,8 +13003,8 @@ function patchAISDKStreamingResult(args) {
12232
13003
  }
12233
13004
  let firstChunkTime;
12234
13005
  const wrappedStream = createPatchedAsyncIterable(streamField.stream, {
12235
- onChunk: () => {
12236
- if (firstChunkTime === void 0) {
13006
+ onChunk: (chunk) => {
13007
+ if (firstChunkTime === void 0 && isAISDKContentAsyncIterableChunk(chunk)) {
12237
13008
  firstChunkTime = getCurrentUnixTimestamp();
12238
13009
  }
12239
13010
  },
@@ -13205,15 +13976,17 @@ function collectLocalMcpServerToolHookNames(serverName, serverConfig) {
13205
13976
 
13206
13977
  // src/instrumentation/plugins/claude-agent-sdk-plugin.ts
13207
13978
  var ROOT_LLM_PARENT_KEY = "__root__";
13979
+ var SUB_AGENT_PROMPT_SOURCE_PRIORITY = {
13980
+ delegation: 0,
13981
+ lifecycle: 1,
13982
+ sidechain: 2
13983
+ };
13208
13984
  function llmParentKey(parentToolUseId) {
13209
13985
  return parentToolUseId ?? ROOT_LLM_PARENT_KEY;
13210
13986
  }
13211
13987
  function isSubAgentDelegationToolName(toolName) {
13212
13988
  return toolName === "Agent" || toolName === "Task";
13213
13989
  }
13214
- function shouldParentToolAsTaskSibling(toolName) {
13215
- return toolName === "Agent" || toolName === "Task" || toolName === "Bash";
13216
- }
13217
13990
  function filterSerializableOptions(options) {
13218
13991
  const allowedKeys = [
13219
13992
  "model",
@@ -13350,26 +14123,39 @@ function extractUsageFromMessage(message) {
13350
14123
  }
13351
14124
  return metrics;
13352
14125
  }
13353
- function buildLLMInput(prompt, conversationHistory, capturedPromptMessages) {
13354
- const promptMessages = [];
13355
- if (typeof prompt === "string") {
13356
- promptMessages.push({ content: prompt, role: "user" });
13357
- } else if (capturedPromptMessages && capturedPromptMessages.length > 0) {
13358
- for (const msg of capturedPromptMessages) {
13359
- const role = msg.message?.role;
13360
- const content = msg.message?.content;
13361
- if (role && content !== void 0) {
13362
- promptMessages.push({ content, role });
13363
- }
13364
- }
13365
- }
14126
+ function buildLLMInput(promptMessages, conversationHistory) {
13366
14127
  const inputParts = [...promptMessages, ...conversationHistory];
13367
14128
  return inputParts.length > 0 ? inputParts : void 0;
13368
14129
  }
14130
+ function conversationMessageFromSDKMessage(message) {
14131
+ const role = message.message?.role;
14132
+ const content = message.message?.content;
14133
+ if (role && content !== void 0) {
14134
+ return { content, role };
14135
+ }
14136
+ return void 0;
14137
+ }
14138
+ function messageContentHasBlockType(message, blockType) {
14139
+ const content = message.message?.content;
14140
+ return Array.isArray(content) && content.some(
14141
+ (block) => typeof block === "object" && block !== null && "type" in block && block.type === blockType
14142
+ );
14143
+ }
14144
+ function buildRootPromptMessages(prompt, capturedPromptMessages) {
14145
+ if (typeof prompt === "string") {
14146
+ return [{ content: prompt, role: "user" }];
14147
+ }
14148
+ if (!capturedPromptMessages || capturedPromptMessages.length === 0) {
14149
+ return [];
14150
+ }
14151
+ return capturedPromptMessages.map(conversationMessageFromSDKMessage).filter(
14152
+ (message) => message !== void 0
14153
+ );
14154
+ }
13369
14155
  function formatCapturedMessages(messages) {
13370
14156
  return messages.length > 0 ? messages : [];
13371
14157
  }
13372
- async function createLLMSpanForMessages(messages, prompt, conversationHistory, options, startTime, capturedPromptMessages, parentSpan, existingSpan) {
14158
+ async function createLLMSpanForMessages(messages, promptMessages, conversationHistory, options, startTime, parentSpan, existingSpan) {
13373
14159
  if (messages.length === 0) {
13374
14160
  return void 0;
13375
14161
  }
@@ -13379,11 +14165,7 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
13379
14165
  }
13380
14166
  const model = lastMessage.message.model || options.model;
13381
14167
  const usage = extractUsageFromMessage(lastMessage);
13382
- const input = buildLLMInput(
13383
- prompt,
13384
- conversationHistory,
13385
- capturedPromptMessages
13386
- );
14168
+ const input = buildLLMInput(promptMessages, conversationHistory);
13387
14169
  const outputs = messages.map(
13388
14170
  (m) => m.message?.content && m.message?.role ? { content: m.message.content, role: m.message.role } : void 0
13389
14171
  ).filter(
@@ -13514,8 +14296,7 @@ function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers,
13514
14296
  },
13515
14297
  name: parsed.displayName,
13516
14298
  parent: await resolveParentSpan(toolUseID, {
13517
- agentId: input.agent_id,
13518
- preferTaskSiblingParent: shouldParentToolAsTaskSibling(parsed.toolName)
14299
+ agentId: input.agent_id
13519
14300
  }),
13520
14301
  spanAttributes: { type: "tool" /* TOOL */ }
13521
14302
  });
@@ -13761,12 +14542,37 @@ function injectTracingHooks(options, resolveParentSpan, activeToolSpans, localTo
13761
14542
  }
13762
14543
  };
13763
14544
  }
14545
+ function setSubAgentPromptMessages(state, parentToolUseId, promptMessages, source) {
14546
+ if (promptMessages.length === 0) {
14547
+ return;
14548
+ }
14549
+ const parentKey = llmParentKey(parentToolUseId);
14550
+ const sourcePriority = SUB_AGENT_PROMPT_SOURCE_PRIORITY[source];
14551
+ const currentPriority = state.promptSourcePriorityByParentKey.get(parentKey) ?? -1;
14552
+ if (sourcePriority > currentPriority) {
14553
+ state.promptMessagesByParentKey.set(parentKey, promptMessages);
14554
+ state.promptSourcePriorityByParentKey.set(parentKey, sourcePriority);
14555
+ }
14556
+ }
14557
+ function getConversationHistory(state, parentKey) {
14558
+ let conversationHistory = state.conversationHistoryByParentKey.get(parentKey);
14559
+ if (!conversationHistory) {
14560
+ conversationHistory = [];
14561
+ state.conversationHistoryByParentKey.set(parentKey, conversationHistory);
14562
+ }
14563
+ return conversationHistory;
14564
+ }
13764
14565
  async function finalizeCurrentMessageGroup(state) {
13765
14566
  if (state.currentMessages.length === 0) {
13766
14567
  return;
13767
14568
  }
13768
14569
  const parentToolUseId = state.currentMessages[0]?.parent_tool_use_id ?? null;
13769
14570
  const parentKey = llmParentKey(parentToolUseId);
14571
+ const conversationHistory = getConversationHistory(state, parentKey);
14572
+ const promptMessages = parentToolUseId ? state.promptMessagesByParentKey.get(parentKey) ?? [] : buildRootPromptMessages(
14573
+ state.originalPrompt,
14574
+ state.capturedPromptMessages
14575
+ );
13770
14576
  let parentSpan = await state.span.export();
13771
14577
  if (parentToolUseId) {
13772
14578
  const subAgentSpan = state.subAgentSpans.get(parentToolUseId);
@@ -13777,11 +14583,10 @@ async function finalizeCurrentMessageGroup(state) {
13777
14583
  const existingLlmSpan = state.activeLlmSpansByParentToolUse.get(parentKey);
13778
14584
  const llmSpanResult = await createLLMSpanForMessages(
13779
14585
  state.currentMessages,
13780
- state.originalPrompt,
13781
- state.finalResults,
14586
+ promptMessages,
14587
+ conversationHistory,
13782
14588
  state.options,
13783
14589
  state.currentMessageStartTime,
13784
- state.capturedPromptMessages,
13785
14590
  parentSpan,
13786
14591
  existingLlmSpan
13787
14592
  );
@@ -13795,6 +14600,7 @@ async function finalizeCurrentMessageGroup(state) {
13795
14600
  state.latestRootLlmParentRef.value = llmSpanResult.spanExport;
13796
14601
  }
13797
14602
  if (llmSpanResult.finalMessage) {
14603
+ conversationHistory.push(llmSpanResult.finalMessage);
13798
14604
  state.finalResults.push(llmSpanResult.finalMessage);
13799
14605
  }
13800
14606
  }
@@ -13822,6 +14628,15 @@ function maybeTrackToolUseContext(state, message) {
13822
14628
  description: getStringProperty(block.input, "description"),
13823
14629
  toolUseId: block.id
13824
14630
  });
14631
+ const prompt = getStringProperty(block.input, "prompt");
14632
+ if (prompt) {
14633
+ setSubAgentPromptMessages(
14634
+ state,
14635
+ block.id,
14636
+ [{ content: prompt, role: "user" }],
14637
+ "delegation"
14638
+ );
14639
+ }
13825
14640
  }
13826
14641
  }
13827
14642
  }
@@ -13940,6 +14755,14 @@ async function maybeHandleTaskLifecycleMessage(state, message) {
13940
14755
  };
13941
14756
  if (message.subtype === "task_started") {
13942
14757
  const prompt = getStringProperty(message, "prompt");
14758
+ if (prompt) {
14759
+ setSubAgentPromptMessages(
14760
+ state,
14761
+ toolUseId,
14762
+ [{ content: prompt, role: "user" }],
14763
+ "lifecycle"
14764
+ );
14765
+ }
13943
14766
  subAgentSpan.log({
13944
14767
  input: prompt,
13945
14768
  metadata
@@ -13990,6 +14813,28 @@ async function handleStreamMessage(state, message) {
13990
14813
  return;
13991
14814
  }
13992
14815
  await maybeStartSubAgentSpan(state, message);
14816
+ const messageParentToolUseId = message.parent_tool_use_id;
14817
+ if (messageParentToolUseId && message.type === "user") {
14818
+ const conversationMessage = conversationMessageFromSDKMessage(message);
14819
+ if (conversationMessage?.role === "user") {
14820
+ await finalizeCurrentMessageGroup(state);
14821
+ const parentKey = llmParentKey(messageParentToolUseId);
14822
+ const conversationHistory = getConversationHistory(state, parentKey);
14823
+ const currentPromptSourcePriority = state.promptSourcePriorityByParentKey.get(parentKey) ?? -1;
14824
+ const canUseAsInitialSidechainPrompt = conversationHistory.length === 0 && currentPromptSourcePriority < SUB_AGENT_PROMPT_SOURCE_PRIORITY.sidechain && !messageContentHasBlockType(message, "tool_result");
14825
+ if (!canUseAsInitialSidechainPrompt) {
14826
+ conversationHistory.push(conversationMessage);
14827
+ return;
14828
+ }
14829
+ setSubAgentPromptMessages(
14830
+ state,
14831
+ messageParentToolUseId,
14832
+ [conversationMessage],
14833
+ "sidechain"
14834
+ );
14835
+ return;
14836
+ }
14837
+ }
13993
14838
  const messageId = message.message?.id;
13994
14839
  if (messageId && messageId !== state.currentMessageId) {
13995
14840
  await finalizeCurrentMessageGroup(state);
@@ -14141,6 +14986,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
14141
14986
  }
14142
14987
  const activeToolSpans = /* @__PURE__ */ new Map();
14143
14988
  const activeLlmSpansByParentToolUse = /* @__PURE__ */ new Map();
14989
+ const conversationHistoryByParentKey = /* @__PURE__ */ new Map();
14144
14990
  const subAgentSpans = /* @__PURE__ */ new Map();
14145
14991
  const endedSubAgentSpans = /* @__PURE__ */ new Set();
14146
14992
  const toolUseToParent = /* @__PURE__ */ new Map();
@@ -14150,6 +14996,8 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
14150
14996
  };
14151
14997
  const subAgentDetailsByToolUseId = /* @__PURE__ */ new Map();
14152
14998
  const taskIdToToolUseId = /* @__PURE__ */ new Map();
14999
+ const promptMessagesByParentKey = /* @__PURE__ */ new Map();
15000
+ const promptSourcePriorityByParentKey = /* @__PURE__ */ new Map();
14153
15001
  const localToolContext = createClaudeLocalToolContext();
14154
15002
  const { hasLocalToolHandlers, localToolHookNames } = prepareLocalToolHandlersInMcpServers(options.mcpServers);
14155
15003
  const skipLocalToolHooks = options[CLAUDE_AGENT_SDK_SKIP_LOCAL_TOOL_HOOKS_OPTION] === true || hasLocalToolHandlers;
@@ -14158,38 +15006,19 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
14158
15006
  const parentToolUseId = trackedParentToolUseId ?? (context?.agentId ? taskIdToToolUseId.get(context.agentId) ?? null : null);
14159
15007
  const parentKey = llmParentKey(parentToolUseId);
14160
15008
  const activeLlmSpan = activeLlmSpansByParentToolUse.get(parentKey);
14161
- if (context?.preferTaskSiblingParent) {
14162
- if (!activeLlmSpan) {
14163
- await ensureActiveLlmSpanForParentToolUse(
14164
- span,
14165
- activeLlmSpansByParentToolUse,
14166
- subAgentDetailsByToolUseId,
14167
- activeToolSpans,
14168
- subAgentSpans,
14169
- parentToolUseId,
14170
- getCurrentUnixTimestamp()
14171
- );
14172
- }
14173
- if (parentToolUseId) {
14174
- const subAgentSpan = await ensureSubAgentSpan(
14175
- subAgentDetailsByToolUseId,
14176
- span,
14177
- activeToolSpans,
14178
- subAgentSpans,
14179
- parentToolUseId
14180
- );
14181
- return subAgentSpan.export();
14182
- }
14183
- return span.export();
14184
- }
14185
- if (activeLlmSpan) {
14186
- return activeLlmSpan.export();
15009
+ const latestLlmParent = parentToolUseId ? latestLlmParentBySubAgentToolUse.get(parentToolUseId) : latestRootLlmParentRef.value;
15010
+ if (!activeLlmSpan && !latestLlmParent) {
15011
+ await ensureActiveLlmSpanForParentToolUse(
15012
+ span,
15013
+ activeLlmSpansByParentToolUse,
15014
+ subAgentDetailsByToolUseId,
15015
+ activeToolSpans,
15016
+ subAgentSpans,
15017
+ parentToolUseId,
15018
+ getCurrentUnixTimestamp()
15019
+ );
14187
15020
  }
14188
15021
  if (parentToolUseId) {
14189
- const parentLlm = latestLlmParentBySubAgentToolUse.get(parentToolUseId);
14190
- if (parentLlm) {
14191
- return parentLlm;
14192
- }
14193
15022
  const subAgentSpan = await ensureSubAgentSpan(
14194
15023
  subAgentDetailsByToolUseId,
14195
15024
  span,
@@ -14199,9 +15028,6 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
14199
15028
  );
14200
15029
  return subAgentSpan.export();
14201
15030
  }
14202
- if (latestRootLlmParentRef.value) {
14203
- return latestRootLlmParentRef.value;
14204
- }
14205
15031
  return span.export();
14206
15032
  };
14207
15033
  localToolContext.resolveLocalToolParent = resolveToolUseParentSpan;
@@ -14222,6 +15048,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
14222
15048
  accumulatedOutputTokens: 0,
14223
15049
  activeLlmSpansByParentToolUse,
14224
15050
  activeToolSpans,
15051
+ conversationHistoryByParentKey,
14225
15052
  capturedPromptMessages,
14226
15053
  currentMessageId: void 0,
14227
15054
  currentMessageStartTime: startTime,
@@ -14232,7 +15059,9 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
14232
15059
  originalPrompt,
14233
15060
  processing: Promise.resolve(),
14234
15061
  promptDone,
15062
+ promptMessagesByParentKey,
14235
15063
  promptStarted: () => promptStarted,
15064
+ promptSourcePriorityByParentKey,
14236
15065
  span,
14237
15066
  subAgentDetailsByToolUseId,
14238
15067
  subAgentSpans,
@@ -15661,6 +16490,10 @@ var googleGenAIChannels = defineChannels("@google/genai", {
15661
16490
  embedContent: channel({
15662
16491
  channelName: "models.embedContent",
15663
16492
  kind: "async"
16493
+ }),
16494
+ interactionsCreate: channel({
16495
+ channelName: "interactions.create",
16496
+ kind: "async"
15664
16497
  })
15665
16498
  });
15666
16499
 
@@ -15688,6 +16521,7 @@ var GoogleGenAIPlugin = class extends BasePlugin {
15688
16521
  this.subscribeToGenerateContentChannel();
15689
16522
  this.subscribeToGenerateContentStreamChannel();
15690
16523
  this.subscribeToEmbedContentChannel();
16524
+ this.subscribeToInteractionsCreateChannel();
15691
16525
  }
15692
16526
  subscribeToGenerateContentChannel() {
15693
16527
  const tracingChannel = googleGenAIChannels.generateContent.tracingChannel();
@@ -15860,7 +16694,30 @@ var GoogleGenAIPlugin = class extends BasePlugin {
15860
16694
  tracingChannel.unsubscribe(handlers);
15861
16695
  });
15862
16696
  }
16697
+ subscribeToInteractionsCreateChannel() {
16698
+ this.unsubscribers.push(
16699
+ traceStreamingChannel(
16700
+ googleGenAIChannels.interactionsCreate,
16701
+ {
16702
+ name: "create_interaction",
16703
+ shouldTrace: ([params]) => !isBackgroundInteractionCreate(params),
16704
+ type: "llm" /* LLM */,
16705
+ extractInput: ([params]) => ({
16706
+ input: serializeInteractionInput(params),
16707
+ metadata: extractInteractionMetadata(params)
16708
+ }),
16709
+ extractOutput: (result) => serializeInteractionValue(result),
16710
+ extractMetadata: (result) => extractInteractionResponseMetadata(result),
16711
+ extractMetrics: (result, startTime) => cleanMetrics3(extractInteractionMetrics(result, startTime)),
16712
+ aggregateChunks: (chunks, _result, _event, startTime) => aggregateInteractionEvents(chunks, startTime)
16713
+ }
16714
+ )
16715
+ );
16716
+ }
15863
16717
  };
16718
+ function isBackgroundInteractionCreate(params) {
16719
+ return tryToDict(params)?.background === true;
16720
+ }
15864
16721
  function ensureSpanState(states, event, create) {
15865
16722
  const existing = states.get(event);
15866
16723
  if (existing) {
@@ -16068,6 +16925,60 @@ function serializeEmbedContentInput(params) {
16068
16925
  }
16069
16926
  return input;
16070
16927
  }
16928
+ function serializeInteractionInput(params) {
16929
+ const input = {
16930
+ input: serializeInteractionValue(params.input)
16931
+ };
16932
+ for (const key of [
16933
+ "model",
16934
+ "agent",
16935
+ "agent_config",
16936
+ "api_version",
16937
+ "background",
16938
+ "environment",
16939
+ "generation_config",
16940
+ "previous_interaction_id",
16941
+ "response_format",
16942
+ "response_mime_type",
16943
+ "response_modalities",
16944
+ "service_tier",
16945
+ "store",
16946
+ "stream",
16947
+ "system_instruction",
16948
+ "webhook_config"
16949
+ ]) {
16950
+ const value = params[key];
16951
+ if (value !== void 0) {
16952
+ input[key] = serializeInteractionValue(value);
16953
+ }
16954
+ }
16955
+ return input;
16956
+ }
16957
+ function extractInteractionMetadata(params) {
16958
+ const metadata = {};
16959
+ for (const key of [
16960
+ "model",
16961
+ "agent",
16962
+ "agent_config",
16963
+ "generation_config",
16964
+ "system_instruction",
16965
+ "response_format",
16966
+ "response_mime_type",
16967
+ "response_modalities",
16968
+ "service_tier"
16969
+ ]) {
16970
+ const value = params[key];
16971
+ if (value !== void 0) {
16972
+ metadata[key] = serializeInteractionValue(value);
16973
+ }
16974
+ }
16975
+ if (Array.isArray(params.tools)) {
16976
+ metadata.tools = params.tools.map(
16977
+ (tool) => serializeInteractionValue(tool)
16978
+ );
16979
+ }
16980
+ return metadata;
16981
+ }
16071
16982
  function serializeContentCollection(contents) {
16072
16983
  if (contents === null || contents === void 0) {
16073
16984
  return null;
@@ -16098,21 +17009,8 @@ function serializePart(part) {
16098
17009
  }
16099
17010
  if (part.inlineData && part.inlineData.data) {
16100
17011
  const { data, mimeType } = part.inlineData;
16101
- if (data instanceof Uint8Array || typeof Buffer !== "undefined" && Buffer.isBuffer(data) || typeof data === "string") {
16102
- const extension = mimeType ? mimeType.split("/")[1] : "bin";
16103
- const filename = `file.${extension}`;
16104
- const buffer = typeof data === "string" ? typeof Buffer !== "undefined" ? Buffer.from(data, "base64") : new Uint8Array(
16105
- atob(data).split("").map((c) => c.charCodeAt(0))
16106
- ) : typeof Buffer !== "undefined" ? Buffer.from(data) : new Uint8Array(data);
16107
- const arrayBuffer = buffer instanceof Uint8Array ? buffer.buffer.slice(
16108
- buffer.byteOffset,
16109
- buffer.byteOffset + buffer.byteLength
16110
- ) : buffer;
16111
- const attachment = new Attachment({
16112
- data: arrayBuffer,
16113
- filename,
16114
- contentType: mimeType || "application/octet-stream"
16115
- });
17012
+ const attachment = createAttachmentFromInlineData(data, mimeType);
17013
+ if (attachment) {
16116
17014
  return {
16117
17015
  image_url: { url: attachment }
16118
17016
  };
@@ -16120,6 +17018,59 @@ function serializePart(part) {
16120
17018
  }
16121
17019
  return part;
16122
17020
  }
17021
+ function serializeInteractionValue(value, seen = /* @__PURE__ */ new WeakSet()) {
17022
+ if (value === null || value === void 0 || typeof value !== "object") {
17023
+ return value;
17024
+ }
17025
+ if (Array.isArray(value)) {
17026
+ return value.map((item) => serializeInteractionValue(item, seen));
17027
+ }
17028
+ const dict = tryToDict(value);
17029
+ if (dict === null || dict === void 0 || typeof dict !== "object") {
17030
+ return dict;
17031
+ }
17032
+ if (Array.isArray(dict)) {
17033
+ return dict.map((item) => serializeInteractionValue(item, seen));
17034
+ }
17035
+ if (seen.has(dict)) {
17036
+ return "[Circular]";
17037
+ }
17038
+ seen.add(dict);
17039
+ try {
17040
+ const serialized = {};
17041
+ 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;
17042
+ const attachment = mimeType && "data" in dict && dict.data !== void 0 ? createAttachmentFromInlineData(dict.data, mimeType) : null;
17043
+ for (const [key, entry] of Object.entries(dict)) {
17044
+ if (key === "data" && attachment) {
17045
+ serialized[key] = attachment;
17046
+ } else {
17047
+ serialized[key] = serializeInteractionValue(entry, seen);
17048
+ }
17049
+ }
17050
+ return serialized;
17051
+ } finally {
17052
+ seen.delete(dict);
17053
+ }
17054
+ }
17055
+ function createAttachmentFromInlineData(data, mimeType) {
17056
+ if (!(data instanceof Uint8Array || typeof Buffer !== "undefined" && Buffer.isBuffer(data) || typeof data === "string")) {
17057
+ return null;
17058
+ }
17059
+ const extension = mimeType ? mimeType.split("/")[1] : "bin";
17060
+ const filename = `file.${extension}`;
17061
+ const buffer = typeof data === "string" ? typeof Buffer !== "undefined" ? Buffer.from(data, "base64") : new Uint8Array(
17062
+ atob(data).split("").map((c) => c.charCodeAt(0))
17063
+ ) : typeof Buffer !== "undefined" ? Buffer.from(data) : new Uint8Array(data);
17064
+ const arrayBuffer = buffer instanceof Uint8Array ? buffer.buffer.slice(
17065
+ buffer.byteOffset,
17066
+ buffer.byteOffset + buffer.byteLength
17067
+ ) : buffer;
17068
+ return new Attachment({
17069
+ data: arrayBuffer,
17070
+ filename,
17071
+ contentType: mimeType || "application/octet-stream"
17072
+ });
17073
+ }
16123
17074
  function serializeGenerateContentTools(params) {
16124
17075
  const config = params.config ? tryToDict(params.config) : null;
16125
17076
  const tools = config?.tools;
@@ -16204,6 +17155,33 @@ function extractEmbedContentMetrics(response, startTime) {
16204
17155
  }
16205
17156
  return metrics;
16206
17157
  }
17158
+ function extractInteractionMetrics(response, startTime) {
17159
+ const metrics = {};
17160
+ if (startTime !== void 0) {
17161
+ const end = getCurrentUnixTimestamp();
17162
+ metrics.start = startTime;
17163
+ metrics.end = end;
17164
+ metrics.duration = end - startTime;
17165
+ }
17166
+ if (response?.usage) {
17167
+ populateInteractionUsageMetrics(metrics, response.usage);
17168
+ }
17169
+ return metrics;
17170
+ }
17171
+ function extractInteractionResponseMetadata(response) {
17172
+ const responseDict = tryToDict(response);
17173
+ if (!responseDict) {
17174
+ return void 0;
17175
+ }
17176
+ const metadata = {};
17177
+ if (typeof responseDict.id === "string") {
17178
+ metadata.interaction_id = responseDict.id;
17179
+ }
17180
+ if (typeof responseDict.status === "string") {
17181
+ metadata.status = responseDict.status;
17182
+ }
17183
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
17184
+ }
16207
17185
  function extractEmbedPromptTokenCount(response) {
16208
17186
  if (!response) {
16209
17187
  return void 0;
@@ -16266,6 +17244,23 @@ function populateUsageMetrics(metrics, usage) {
16266
17244
  metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
16267
17245
  }
16268
17246
  }
17247
+ function populateInteractionUsageMetrics(metrics, usage) {
17248
+ if (typeof usage.total_input_tokens === "number") {
17249
+ metrics.prompt_tokens = usage.total_input_tokens;
17250
+ }
17251
+ if (typeof usage.total_output_tokens === "number") {
17252
+ metrics.completion_tokens = usage.total_output_tokens;
17253
+ }
17254
+ if (typeof usage.total_tokens === "number") {
17255
+ metrics.tokens = usage.total_tokens;
17256
+ }
17257
+ if (typeof usage.total_cached_tokens === "number") {
17258
+ metrics.prompt_cached_tokens = usage.total_cached_tokens;
17259
+ }
17260
+ if (typeof usage.total_thought_tokens === "number") {
17261
+ metrics.completion_reasoning_tokens = usage.total_thought_tokens;
17262
+ }
17263
+ }
16269
17264
  function aggregateGenerateContentChunks(chunks, startTime, firstTokenTime) {
16270
17265
  const end = getCurrentUnixTimestamp();
16271
17266
  const metrics = {
@@ -16363,6 +17358,146 @@ function aggregateGenerateContentChunks(chunks, startTime, firstTokenTime) {
16363
17358
  }
16364
17359
  return { aggregated, metrics };
16365
17360
  }
17361
+ function aggregateInteractionEvents(chunks, startTime) {
17362
+ const end = getCurrentUnixTimestamp();
17363
+ const metrics = {};
17364
+ if (startTime !== void 0) {
17365
+ metrics.start = startTime;
17366
+ metrics.end = end;
17367
+ metrics.duration = end - startTime;
17368
+ }
17369
+ let latestInteraction;
17370
+ let latestUsage;
17371
+ let status;
17372
+ let outputText = "";
17373
+ const steps = /* @__PURE__ */ new Map();
17374
+ for (const chunk of chunks) {
17375
+ const event = tryToDict(chunk);
17376
+ if (!event) {
17377
+ continue;
17378
+ }
17379
+ const usage = extractInteractionUsageFromEvent(event);
17380
+ if (usage) {
17381
+ latestUsage = usage;
17382
+ }
17383
+ const interaction = tryToDict(event.interaction);
17384
+ if (interaction) {
17385
+ latestInteraction = serializeInteractionValue(interaction);
17386
+ if (typeof interaction.status === "string") {
17387
+ status = interaction.status;
17388
+ }
17389
+ }
17390
+ if (typeof event.status === "string") {
17391
+ status = event.status;
17392
+ }
17393
+ const index = typeof event.index === "number" ? event.index : void 0;
17394
+ if (index === void 0) {
17395
+ continue;
17396
+ }
17397
+ if (event.event_type === "step.start") {
17398
+ const compact = compactInteractionStep(event.step);
17399
+ compact.index = index;
17400
+ steps.set(index, compact);
17401
+ continue;
17402
+ }
17403
+ if (event.event_type === "step.delta") {
17404
+ const step = steps.get(index) ?? { index };
17405
+ const textDelta = applyInteractionDelta(step, event.delta);
17406
+ if (textDelta) {
17407
+ outputText += textDelta;
17408
+ }
17409
+ steps.set(index, step);
17410
+ }
17411
+ }
17412
+ if (latestUsage) {
17413
+ populateInteractionUsageMetrics(metrics, latestUsage);
17414
+ }
17415
+ const output = latestInteraction ? { ...latestInteraction } : {};
17416
+ if (status) {
17417
+ output.status = status;
17418
+ }
17419
+ if (outputText) {
17420
+ output.output_text = outputText;
17421
+ }
17422
+ if (latestUsage) {
17423
+ output.usage = serializeInteractionValue(latestUsage);
17424
+ }
17425
+ const compactSteps = Array.from(steps.values()).sort(
17426
+ (left, right) => Number(left.index ?? 0) - Number(right.index ?? 0)
17427
+ );
17428
+ if (compactSteps.length > 0) {
17429
+ output.steps = compactSteps;
17430
+ }
17431
+ const metadata = {};
17432
+ if (typeof output.id === "string") {
17433
+ metadata.interaction_id = output.id;
17434
+ }
17435
+ if (typeof output.status === "string") {
17436
+ metadata.status = output.status;
17437
+ }
17438
+ return {
17439
+ output,
17440
+ metrics: cleanMetrics3(metrics),
17441
+ ...Object.keys(metadata).length > 0 ? { metadata } : {}
17442
+ };
17443
+ }
17444
+ function extractInteractionUsageFromEvent(event) {
17445
+ const metadata = tryToDict(event.metadata);
17446
+ const metadataUsage = tryToDict(metadata?.usage);
17447
+ if (metadataUsage) {
17448
+ return metadataUsage;
17449
+ }
17450
+ const metadataTotalUsage = tryToDict(metadata?.total_usage);
17451
+ if (metadataTotalUsage) {
17452
+ return metadataTotalUsage;
17453
+ }
17454
+ const interaction = tryToDict(event.interaction);
17455
+ const interactionUsage = tryToDict(interaction?.usage);
17456
+ return interactionUsage ? interactionUsage : void 0;
17457
+ }
17458
+ function compactInteractionStep(step) {
17459
+ const stepDict = tryToDict(step);
17460
+ if (!stepDict) {
17461
+ return {};
17462
+ }
17463
+ const compact = {};
17464
+ for (const key of [
17465
+ "type",
17466
+ "content",
17467
+ "name",
17468
+ "server_name",
17469
+ "arguments",
17470
+ "result",
17471
+ "is_error"
17472
+ ]) {
17473
+ if (stepDict[key] !== void 0) {
17474
+ compact[key] = serializeInteractionValue(stepDict[key]);
17475
+ }
17476
+ }
17477
+ return Object.keys(compact).length > 0 ? compact : serializeInteractionValue(stepDict);
17478
+ }
17479
+ function applyInteractionDelta(step, delta) {
17480
+ const deltaDict = tryToDict(delta);
17481
+ if (!deltaDict) {
17482
+ return void 0;
17483
+ }
17484
+ const deltaType = deltaDict.type;
17485
+ if (typeof deltaType === "string" && typeof step.type !== "string") {
17486
+ step.type = deltaType === "text" ? "model_output" : deltaType;
17487
+ }
17488
+ if (deltaType === "text" && typeof deltaDict.text === "string") {
17489
+ step.text = `${typeof step.text === "string" ? step.text : ""}${deltaDict.text}`;
17490
+ return deltaDict.text;
17491
+ }
17492
+ if (deltaType === "arguments_delta" && typeof deltaDict.arguments === "string") {
17493
+ step.arguments = `${typeof step.arguments === "string" ? step.arguments : ""}${deltaDict.arguments}`;
17494
+ return void 0;
17495
+ }
17496
+ const deltas = Array.isArray(step.deltas) ? step.deltas : [];
17497
+ deltas.push(serializeInteractionValue(deltaDict));
17498
+ step.deltas = deltas;
17499
+ return void 0;
17500
+ }
16366
17501
  function cleanMetrics3(metrics) {
16367
17502
  const cleaned = {};
16368
17503
  for (const [key, value] of Object.entries(metrics)) {
@@ -22189,7 +23324,8 @@ var FlueObserveBridge = class {
22189
23324
  metadata
22190
23325
  }
22191
23326
  });
22192
- this.runsById.set(event.runId, { metadata, span });
23327
+ const activeContext = enterCurrentFlueSpan(span);
23328
+ this.runsById.set(event.runId, { activeContext, metadata, span });
22193
23329
  }
22194
23330
  handleRunEnd(event) {
22195
23331
  const state = this.runsById.get(event.runId);
@@ -22207,6 +23343,7 @@ var FlueObserveBridge = class {
22207
23343
  });
22208
23344
  safeEnd(state.span, eventTime(event.timestamp));
22209
23345
  this.runsById.delete(event.runId);
23346
+ restoreCurrentFlueSpan(state.activeContext);
22210
23347
  }
22211
23348
  void flush().catch((error) => {
22212
23349
  logInstrumentationError3("Flue flush", error);
@@ -22333,7 +23470,8 @@ var FlueObserveBridge = class {
22333
23470
  metadata
22334
23471
  }
22335
23472
  });
22336
- this.toolsByKey.set(toolKey(event), { metadata, span });
23473
+ const activeContext = enterCurrentFlueSpan(span);
23474
+ this.toolsByKey.set(toolKey(event), { activeContext, metadata, span });
22337
23475
  }
22338
23476
  handleToolCall(event) {
22339
23477
  if (!event.toolCallId) {
@@ -22356,6 +23494,7 @@ var FlueObserveBridge = class {
22356
23494
  });
22357
23495
  safeEnd(state.span, eventTime(event.timestamp));
22358
23496
  this.toolsByKey.delete(key);
23497
+ restoreCurrentFlueSpan(state.activeContext);
22359
23498
  }
22360
23499
  handleTaskStart(event) {
22361
23500
  if (!event.taskId) {
@@ -22760,7 +23899,35 @@ function stateMatchesRun(state, runId) {
22760
23899
  function startFlueSpan(parent, args) {
22761
23900
  return parent ? withCurrent(parent, () => startSpan(args)) : startSpan(args);
22762
23901
  }
22763
- function safeLog3(span, event) {
23902
+ function enterCurrentFlueSpan(span) {
23903
+ const contextManager = _internalGetGlobalState()?.contextManager;
23904
+ const store = contextManager ? Reflect.get(contextManager, BRAINTRUST_CURRENT_SPAN_STORE) : void 0;
23905
+ if (!contextManager || !isCurrentSpanStore(store)) {
23906
+ return void 0;
23907
+ }
23908
+ const previous = store.getStore();
23909
+ try {
23910
+ store.enterWith(contextManager.wrapSpanForStore(span));
23911
+ return { previous, store };
23912
+ } catch (error) {
23913
+ logInstrumentationError3("Flue context propagation", error);
23914
+ return void 0;
23915
+ }
23916
+ }
23917
+ function isCurrentSpanStore(value) {
23918
+ return isObjectLike(value) && typeof Reflect.get(value, "enterWith") === "function" && typeof Reflect.get(value, "getStore") === "function";
23919
+ }
23920
+ function restoreCurrentFlueSpan(activeContext) {
23921
+ if (!activeContext) {
23922
+ return;
23923
+ }
23924
+ try {
23925
+ activeContext.store.enterWith(activeContext.previous);
23926
+ } catch (error) {
23927
+ logInstrumentationError3("Flue context restoration", error);
23928
+ }
23929
+ }
23930
+ function safeLog3(span, event) {
22764
23931
  try {
22765
23932
  span.log(event);
22766
23933
  } catch (error) {
@@ -23237,6 +24404,890 @@ function isBraintrustHandler(handler) {
23237
24404
  return Reflect.get(handler, "name") === BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME;
23238
24405
  }
23239
24406
 
24407
+ // src/instrumentation/plugins/pi-coding-agent-channels.ts
24408
+ var piCodingAgentChannels = defineChannels(
24409
+ "@earendil-works/pi-coding-agent",
24410
+ {
24411
+ prompt: channel({
24412
+ channelName: "AgentSession.prompt",
24413
+ kind: "async"
24414
+ })
24415
+ }
24416
+ );
24417
+
24418
+ // src/instrumentation/plugins/pi-coding-agent-plugin.ts
24419
+ var piStreamPatchStates = /* @__PURE__ */ new WeakMap();
24420
+ var piPromptContextStore;
24421
+ var PiCodingAgentPlugin = class extends BasePlugin {
24422
+ activePromptStates = /* @__PURE__ */ new Set();
24423
+ onEnable() {
24424
+ this.subscribeToPrompt();
24425
+ }
24426
+ onDisable() {
24427
+ for (const unsubscribe of this.unsubscribers) {
24428
+ unsubscribe();
24429
+ }
24430
+ this.unsubscribers = [];
24431
+ for (const state of [...this.activePromptStates]) {
24432
+ void finalizePiPromptRun(state).catch((error) => {
24433
+ logInstrumentationError4("Pi Coding Agent disable cleanup", error);
24434
+ });
24435
+ }
24436
+ }
24437
+ subscribeToPrompt() {
24438
+ const channel2 = piCodingAgentChannels.prompt.tracingChannel();
24439
+ const states = /* @__PURE__ */ new WeakMap();
24440
+ const unbindAutoInstrumentationSuppression = bindAutoInstrumentationSuppressionToStart(channel2);
24441
+ const handlers = {
24442
+ start: (event) => {
24443
+ const state = startPiPromptRun(event, (state2) => {
24444
+ this.activePromptStates.delete(state2);
24445
+ });
24446
+ if (state) {
24447
+ this.activePromptStates.add(state);
24448
+ states.set(event, state);
24449
+ }
24450
+ },
24451
+ asyncEnd: async (event) => {
24452
+ const state = states.get(event);
24453
+ if (!state) {
24454
+ return;
24455
+ }
24456
+ states.delete(event);
24457
+ state.promptCallEnded = true;
24458
+ if (!state.finalized && state.deferCompletionUntilTurnEnd && !state.turnEnded) {
24459
+ if (!state.sawStreamFn) {
24460
+ state.queued = true;
24461
+ if (!state.streamPatchState.queuedPromptStates.includes(state)) {
24462
+ state.streamPatchState.queuedPromptStates.push(state);
24463
+ }
24464
+ }
24465
+ return;
24466
+ }
24467
+ await finalizePiPromptRun(state);
24468
+ },
24469
+ error: async (event) => {
24470
+ const state = states.get(event);
24471
+ if (!state) {
24472
+ return;
24473
+ }
24474
+ states.delete(event);
24475
+ await finalizePiPromptRun(state, event.error);
24476
+ }
24477
+ };
24478
+ channel2.subscribe(handlers);
24479
+ this.unsubscribers.push(() => {
24480
+ unbindAutoInstrumentationSuppression?.();
24481
+ channel2.unsubscribe(handlers);
24482
+ });
24483
+ }
24484
+ };
24485
+ function startPiPromptRun(event, onFinalize) {
24486
+ const session = extractSession(event);
24487
+ const agent = session?.agent;
24488
+ if (!session || !isPiAgent(agent)) {
24489
+ return void 0;
24490
+ }
24491
+ const metadata = {
24492
+ ...extractSessionMetadata(session),
24493
+ ...extractPromptOptionsMetadata(event.arguments[1]),
24494
+ "pi_coding_agent.operation": "AgentSession.prompt",
24495
+ provider: session.model?.provider ?? agent.state?.model?.provider ?? "pi",
24496
+ ...session.model?.id || agent.state?.model?.id ? { model: session.model?.id ?? agent.state?.model?.id } : {},
24497
+ ...event.moduleVersion ? { "pi_coding_agent.version": event.moduleVersion } : {}
24498
+ };
24499
+ const span = startSpan({
24500
+ event: {
24501
+ input: extractPromptInput(event.arguments[0], event.arguments[1]),
24502
+ metadata
24503
+ },
24504
+ name: "AgentSession.prompt",
24505
+ spanAttributes: { type: "task" /* TASK */ }
24506
+ });
24507
+ const streamPatchState = installPiStreamPatch(agent);
24508
+ const options = event.arguments[1];
24509
+ const promptText = event.arguments[0];
24510
+ const state = {
24511
+ activeLlmSpans: /* @__PURE__ */ new Set(),
24512
+ activeToolSpans: /* @__PURE__ */ new Map(),
24513
+ agent,
24514
+ collectedLlmUsageMetrics: false,
24515
+ deferCompletionUntilTurnEnd: options?.streamingBehavior === "followUp" || options?.streamingBehavior === "steer",
24516
+ finalized: false,
24517
+ metadata,
24518
+ metrics: {},
24519
+ onFinalize,
24520
+ promptCallEnded: false,
24521
+ ...typeof promptText === "string" ? { promptText } : {},
24522
+ queued: false,
24523
+ span,
24524
+ sawStreamFn: false,
24525
+ startTime: getCurrentUnixTimestamp(),
24526
+ streamPatchState,
24527
+ turnEnded: false
24528
+ };
24529
+ state.restorePromptContext = enterPiPromptContext(state);
24530
+ streamPatchState.activePromptStates.add(state);
24531
+ try {
24532
+ state.unsubscribeAgent = agent.subscribe(async (agentEvent) => {
24533
+ try {
24534
+ await runWithAutoInstrumentationSuppressed(
24535
+ () => handlePiAgentEvent(state, agentEvent)
24536
+ );
24537
+ } catch (error) {
24538
+ logInstrumentationError4("Pi Coding Agent event", error);
24539
+ }
24540
+ });
24541
+ } catch (error) {
24542
+ logInstrumentationError4("Pi Coding Agent event subscription", error);
24543
+ }
24544
+ return state;
24545
+ }
24546
+ function extractSession(event) {
24547
+ const candidate = event.session ?? event.self;
24548
+ return isObject(candidate) && typeof candidate.prompt === "function" ? candidate : void 0;
24549
+ }
24550
+ function isPiAgent(value) {
24551
+ return isObject(value) && typeof value.streamFn === "function" && typeof value.subscribe === "function";
24552
+ }
24553
+ function promptContextStore() {
24554
+ piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
24555
+ return piPromptContextStore;
24556
+ }
24557
+ function currentPromptContextFrames() {
24558
+ return promptContextStore().getStore()?.frames ?? [];
24559
+ }
24560
+ function currentPiPromptState() {
24561
+ const frames = currentPromptContextFrames();
24562
+ return frames[frames.length - 1]?.state;
24563
+ }
24564
+ function enterPiPromptContext(state) {
24565
+ const frame = {
24566
+ id: /* @__PURE__ */ Symbol("braintrust.pi-coding-agent.prompt"),
24567
+ state
24568
+ };
24569
+ promptContextStore().enterWith({
24570
+ frames: [...currentPromptContextFrames(), frame]
24571
+ });
24572
+ return () => {
24573
+ const frames = currentPromptContextFrames().filter(
24574
+ (candidate) => candidate.id !== frame.id
24575
+ );
24576
+ promptContextStore().enterWith(frames.length > 0 ? { frames } : void 0);
24577
+ };
24578
+ }
24579
+ function installPiStreamPatch(agent) {
24580
+ const existing = piStreamPatchStates.get(agent);
24581
+ if (existing) {
24582
+ if (agent.streamFn !== existing.wrappedStreamFn) {
24583
+ debugLogger.debug(
24584
+ "Pi Coding Agent streamFn changed while Braintrust instrumentation was active; preserving existing patch state."
24585
+ );
24586
+ }
24587
+ return existing;
24588
+ }
24589
+ const patchState = {
24590
+ activePromptStates: /* @__PURE__ */ new Set(),
24591
+ agent,
24592
+ originalStreamFn: agent.streamFn,
24593
+ queuedPromptStates: [],
24594
+ wrappedStreamFn: agent.streamFn
24595
+ };
24596
+ patchState.wrappedStreamFn = makeSharedInstrumentedStreamFn(patchState);
24597
+ agent.streamFn = patchState.wrappedStreamFn;
24598
+ piStreamPatchStates.set(agent, patchState);
24599
+ return patchState;
24600
+ }
24601
+ function resolveStreamPromptState(patchState, context) {
24602
+ let lastUserText;
24603
+ if (Array.isArray(context.messages)) {
24604
+ for (let i = context.messages.length - 1; i >= 0; i--) {
24605
+ const message = context.messages[i];
24606
+ if (isPiUserMessage(message)) {
24607
+ if (typeof message.content === "string") {
24608
+ lastUserText = message.content;
24609
+ } else {
24610
+ lastUserText = message.content.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
24611
+ }
24612
+ break;
24613
+ }
24614
+ }
24615
+ }
24616
+ if (lastUserText !== void 0) {
24617
+ const queuedMatch = patchState.queuedPromptStates.find(
24618
+ (state) => state.promptText === lastUserText
24619
+ );
24620
+ if (queuedMatch) {
24621
+ return queuedMatch;
24622
+ }
24623
+ const matches = [...patchState.activePromptStates].filter(
24624
+ (state) => state.promptText === lastUserText
24625
+ );
24626
+ if (matches.length === 1) {
24627
+ return matches[0];
24628
+ }
24629
+ }
24630
+ const contextState = currentPiPromptState();
24631
+ if (contextState && patchState.activePromptStates.has(contextState) && (!contextState.queued || lastUserText !== void 0 && contextState.promptText === lastUserText)) {
24632
+ return contextState;
24633
+ }
24634
+ if (patchState.activePromptStates.size === 1) {
24635
+ return [...patchState.activePromptStates][0];
24636
+ }
24637
+ return void 0;
24638
+ }
24639
+ function makeSharedInstrumentedStreamFn(patchState) {
24640
+ return async function instrumentedPiStreamFn(model, context, options) {
24641
+ const state = resolveStreamPromptState(patchState, context);
24642
+ if (!state) {
24643
+ const invokeOriginal = () => Reflect.apply(patchState.originalStreamFn, this, [
24644
+ model,
24645
+ context,
24646
+ options
24647
+ ]);
24648
+ return patchState.activePromptStates.size > 0 ? runWithAutoInstrumentationSuppressed(invokeOriginal) : invokeOriginal();
24649
+ }
24650
+ state.sawStreamFn = true;
24651
+ removeQueuedPromptState(state);
24652
+ state.streamPatchState.eventPromptState = state;
24653
+ const llmState = await startPiLlmSpan(state, model, context, options);
24654
+ try {
24655
+ const stream = await runWithAutoInstrumentationSuppressed(
24656
+ () => Reflect.apply(patchState.originalStreamFn, this, [
24657
+ model,
24658
+ context,
24659
+ options
24660
+ ])
24661
+ );
24662
+ return patchAssistantMessageStream(stream, state, llmState);
24663
+ } catch (error) {
24664
+ finishPiLlmSpan(state, llmState, void 0, error);
24665
+ throw error;
24666
+ }
24667
+ };
24668
+ }
24669
+ async function startPiLlmSpan(state, model, context, options) {
24670
+ const metadata = {
24671
+ ...extractModelMetadata2(model),
24672
+ ...extractStreamOptionsMetadata(options),
24673
+ ...extractToolMetadata(context.tools),
24674
+ "pi_coding_agent.operation": "agent.streamFn"
24675
+ };
24676
+ const span = startSpan({
24677
+ event: {
24678
+ input: processInputAttachments(normalizePiContextInput(context)),
24679
+ metadata
24680
+ },
24681
+ name: getLlmSpanName(model),
24682
+ parent: await state.span.export(),
24683
+ spanAttributes: { type: "llm" /* LLM */ }
24684
+ });
24685
+ const llmState = {
24686
+ finalized: false,
24687
+ metadata,
24688
+ metrics: {},
24689
+ span,
24690
+ startTime: getCurrentUnixTimestamp()
24691
+ };
24692
+ state.activeLlmSpans.add(llmState);
24693
+ return llmState;
24694
+ }
24695
+ function patchAssistantMessageStream(stream, promptState, llmState) {
24696
+ if (!isObject(stream)) {
24697
+ return stream;
24698
+ }
24699
+ const streamRecord = stream;
24700
+ const originalResult = stream.result;
24701
+ if (typeof originalResult === "function") {
24702
+ streamRecord.result = function patchedPiResult() {
24703
+ return Promise.resolve(Reflect.apply(originalResult, this, [])).then(
24704
+ (message) => {
24705
+ finishPiLlmSpan(promptState, llmState, message);
24706
+ return message;
24707
+ },
24708
+ (error) => {
24709
+ finishPiLlmSpan(promptState, llmState, void 0, error);
24710
+ throw error;
24711
+ }
24712
+ );
24713
+ };
24714
+ }
24715
+ const originalIterator = stream[Symbol.asyncIterator];
24716
+ if (typeof originalIterator === "function") {
24717
+ streamRecord[Symbol.asyncIterator] = function patchedPiIterator() {
24718
+ const iterator = Reflect.apply(
24719
+ originalIterator,
24720
+ this,
24721
+ []
24722
+ );
24723
+ return {
24724
+ async next() {
24725
+ try {
24726
+ const result = await iterator.next();
24727
+ if (result.done) {
24728
+ finishPiLlmSpan(promptState, llmState);
24729
+ return result;
24730
+ }
24731
+ recordPiAssistantMessageEvent(promptState, llmState, result.value);
24732
+ return result;
24733
+ } catch (error) {
24734
+ finishPiLlmSpan(promptState, llmState, void 0, error);
24735
+ if (typeof iterator.return === "function") {
24736
+ try {
24737
+ await iterator.return();
24738
+ } catch (cleanupError) {
24739
+ logInstrumentationError4(
24740
+ "Pi Coding Agent stream cleanup",
24741
+ cleanupError
24742
+ );
24743
+ }
24744
+ }
24745
+ throw error;
24746
+ }
24747
+ },
24748
+ async return(value) {
24749
+ try {
24750
+ if (typeof iterator.return === "function") {
24751
+ return await iterator.return(value);
24752
+ }
24753
+ return {
24754
+ done: true,
24755
+ value
24756
+ };
24757
+ } catch (error) {
24758
+ finishPiLlmSpan(promptState, llmState, void 0, error);
24759
+ throw error;
24760
+ } finally {
24761
+ finishPiLlmSpan(promptState, llmState);
24762
+ }
24763
+ },
24764
+ async throw(error) {
24765
+ try {
24766
+ if (typeof iterator.throw === "function") {
24767
+ return await iterator.throw(error);
24768
+ }
24769
+ throw error;
24770
+ } catch (thrownError) {
24771
+ finishPiLlmSpan(promptState, llmState, void 0, thrownError);
24772
+ throw thrownError;
24773
+ }
24774
+ },
24775
+ [Symbol.asyncIterator]() {
24776
+ return this;
24777
+ }
24778
+ };
24779
+ };
24780
+ }
24781
+ return stream;
24782
+ }
24783
+ function recordPiAssistantMessageEvent(promptState, llmState, event) {
24784
+ recordFirstTokenMetric(llmState, event);
24785
+ const message = "message" in event ? event.message : void 0;
24786
+ const errorMessage2 = "error" in event ? event.error : void 0;
24787
+ if (event.type === "done" && isPiAssistantMessage(message)) {
24788
+ finishPiLlmSpan(promptState, llmState, message);
24789
+ } else if (event.type === "error" && isPiAssistantMessage(errorMessage2)) {
24790
+ finishPiLlmSpan(promptState, llmState, errorMessage2);
24791
+ }
24792
+ }
24793
+ function recordFirstTokenMetric(state, event) {
24794
+ if (state.metrics.time_to_first_token !== void 0 || event.type === "start") {
24795
+ return;
24796
+ }
24797
+ state.metrics.time_to_first_token = getCurrentUnixTimestamp() - state.startTime;
24798
+ }
24799
+ async function handlePiAgentEvent(state, event) {
24800
+ if (state.finalized) {
24801
+ return;
24802
+ }
24803
+ const eventPromptState = state.streamPatchState.eventPromptState;
24804
+ if (eventPromptState && eventPromptState !== state) {
24805
+ return;
24806
+ }
24807
+ if (!eventPromptState && (state.queued || state.streamPatchState.activePromptStates.size > 1 && currentPiPromptState() !== state)) {
24808
+ return;
24809
+ }
24810
+ switch (event.type) {
24811
+ case "message_end":
24812
+ if (isPiAssistantMessage(event.message)) {
24813
+ state.output = extractAssistantOutput(event.message);
24814
+ }
24815
+ return;
24816
+ case "turn_end":
24817
+ state.turnEnded = true;
24818
+ if (isPiAssistantMessage(event.message)) {
24819
+ state.output = extractAssistantOutput(event.message);
24820
+ if (!state.collectedLlmUsageMetrics) {
24821
+ addMetrics(state.metrics, extractUsageMetrics2(event.message.usage));
24822
+ }
24823
+ }
24824
+ if (state.streamPatchState.eventPromptState === state) {
24825
+ state.streamPatchState.eventPromptState = void 0;
24826
+ }
24827
+ if (state.promptCallEnded && state.deferCompletionUntilTurnEnd) {
24828
+ await finalizePiPromptRun(state);
24829
+ }
24830
+ return;
24831
+ case "tool_execution_start":
24832
+ await startPiToolSpan(state, event);
24833
+ return;
24834
+ case "tool_execution_end":
24835
+ finishPiToolSpan(state, event);
24836
+ return;
24837
+ default:
24838
+ return;
24839
+ }
24840
+ }
24841
+ async function startPiToolSpan(state, event) {
24842
+ if (!event.toolCallId || state.activeToolSpans.has(event.toolCallId)) {
24843
+ return;
24844
+ }
24845
+ const restoreAutoInstrumentation = enterAutoInstrumentationAllowed();
24846
+ const metadata = {
24847
+ "gen_ai.tool.call.id": event.toolCallId,
24848
+ "gen_ai.tool.name": event.toolName,
24849
+ "pi_coding_agent.tool.name": event.toolName
24850
+ };
24851
+ try {
24852
+ const span = startSpan({
24853
+ event: {
24854
+ input: event.args,
24855
+ metadata
24856
+ },
24857
+ name: event.toolName || "tool",
24858
+ parent: await state.span.export(),
24859
+ spanAttributes: { type: "tool" /* TOOL */ }
24860
+ });
24861
+ state.activeToolSpans.set(event.toolCallId, {
24862
+ restoreAutoInstrumentation,
24863
+ span
24864
+ });
24865
+ } catch (error) {
24866
+ restoreAutoInstrumentation();
24867
+ throw error;
24868
+ }
24869
+ }
24870
+ function finishPiToolSpan(state, event) {
24871
+ const toolState = state.activeToolSpans.get(event.toolCallId);
24872
+ if (!toolState) {
24873
+ return;
24874
+ }
24875
+ state.activeToolSpans.delete(event.toolCallId);
24876
+ const metadata = {
24877
+ "gen_ai.tool.call.id": event.toolCallId,
24878
+ "gen_ai.tool.name": event.toolName,
24879
+ "pi_coding_agent.tool.name": event.toolName,
24880
+ "pi_coding_agent.tool.is_error": event.isError
24881
+ };
24882
+ try {
24883
+ safeLog4(toolState.span, {
24884
+ ...event.isError ? { error: stringifyUnknown2(event.result) } : {},
24885
+ metadata,
24886
+ output: event.result
24887
+ });
24888
+ } finally {
24889
+ try {
24890
+ toolState.span.end();
24891
+ } finally {
24892
+ toolState.restoreAutoInstrumentation?.();
24893
+ }
24894
+ }
24895
+ }
24896
+ async function finalizePiPromptRun(state, error) {
24897
+ if (state.finalized) {
24898
+ return;
24899
+ }
24900
+ state.finalized = true;
24901
+ state.onFinalize?.(state);
24902
+ restorePiStreamFn(state);
24903
+ try {
24904
+ state.unsubscribeAgent?.();
24905
+ } catch (unsubscribeError) {
24906
+ logInstrumentationError4("Pi Coding Agent unsubscribe", unsubscribeError);
24907
+ }
24908
+ await finishOpenLlmSpans(state, error);
24909
+ finishOpenToolSpans(state, error);
24910
+ const metadata = {
24911
+ ...state.metadata,
24912
+ ...extractModelMetadata2(state.agent.state?.model)
24913
+ };
24914
+ try {
24915
+ safeLog4(state.span, {
24916
+ ...error ? { error: stringifyUnknown2(error) } : {},
24917
+ metadata,
24918
+ metrics: {
24919
+ ...cleanMetrics5(state.metrics),
24920
+ ...buildDurationMetrics3(state.startTime)
24921
+ },
24922
+ output: state.output
24923
+ });
24924
+ } finally {
24925
+ state.span.end();
24926
+ }
24927
+ }
24928
+ function restorePiStreamFn(state) {
24929
+ const patchState = state.streamPatchState;
24930
+ patchState.activePromptStates.delete(state);
24931
+ removeQueuedPromptState(state);
24932
+ if (patchState.eventPromptState === state) {
24933
+ patchState.eventPromptState = void 0;
24934
+ }
24935
+ state.restorePromptContext?.();
24936
+ if (patchState.activePromptStates.size > 0) {
24937
+ return;
24938
+ }
24939
+ if (patchState.agent.streamFn === patchState.wrappedStreamFn) {
24940
+ patchState.agent.streamFn = patchState.originalStreamFn;
24941
+ }
24942
+ piStreamPatchStates.delete(patchState.agent);
24943
+ }
24944
+ function removeQueuedPromptState(state) {
24945
+ state.queued = false;
24946
+ const queuedPromptStates = state.streamPatchState.queuedPromptStates;
24947
+ const index = queuedPromptStates.indexOf(state);
24948
+ if (index >= 0) {
24949
+ queuedPromptStates.splice(index, 1);
24950
+ }
24951
+ }
24952
+ async function finishOpenLlmSpans(state, error) {
24953
+ for (const llmState of [...state.activeLlmSpans]) {
24954
+ finishPiLlmSpan(state, llmState, void 0, error);
24955
+ }
24956
+ }
24957
+ function finishPiLlmSpan(promptState, llmState, message, error) {
24958
+ if (llmState.finalized) {
24959
+ return;
24960
+ }
24961
+ llmState.finalized = true;
24962
+ promptState.activeLlmSpans.delete(llmState);
24963
+ const messageError = message?.stopReason === "error" && message.errorMessage;
24964
+ const metrics = {
24965
+ ...extractUsageMetrics2(message?.usage),
24966
+ ...cleanMetrics5(llmState.metrics),
24967
+ ...buildDurationMetrics3(llmState.startTime)
24968
+ };
24969
+ const usageMetrics = extractUsageMetrics2(message?.usage);
24970
+ if (Object.keys(usageMetrics).length > 0) {
24971
+ promptState.collectedLlmUsageMetrics = true;
24972
+ addMetrics(promptState.metrics, usageMetrics);
24973
+ }
24974
+ try {
24975
+ safeLog4(llmState.span, {
24976
+ ...error || messageError ? { error: stringifyUnknown2(error ?? messageError) } : {},
24977
+ metadata: {
24978
+ ...llmState.metadata,
24979
+ ...message ? extractAssistantMetadata(message) : {}
24980
+ },
24981
+ metrics,
24982
+ ...message ? { output: extractAssistantOutput(message) } : {}
24983
+ });
24984
+ } finally {
24985
+ llmState.span.end();
24986
+ }
24987
+ }
24988
+ function finishOpenToolSpans(state, error) {
24989
+ for (const [, toolState] of state.activeToolSpans) {
24990
+ try {
24991
+ safeLog4(toolState.span, {
24992
+ error: error ? stringifyUnknown2(error) : "Pi tool did not complete"
24993
+ });
24994
+ toolState.span.end();
24995
+ } finally {
24996
+ toolState.restoreAutoInstrumentation?.();
24997
+ }
24998
+ }
24999
+ state.activeToolSpans.clear();
25000
+ }
25001
+ function normalizePiContextInput(context) {
25002
+ const messages = context.messages.flatMap(
25003
+ (message) => normalizePiMessage(message)
25004
+ );
25005
+ if (context.systemPrompt) {
25006
+ return [{ role: "system", content: context.systemPrompt }, ...messages];
25007
+ }
25008
+ return messages;
25009
+ }
25010
+ function normalizePiMessage(message) {
25011
+ if (isPiUserMessage(message)) {
25012
+ return [
25013
+ {
25014
+ role: "user",
25015
+ content: normalizeUserContent(message.content)
25016
+ }
25017
+ ];
25018
+ }
25019
+ if (isPiAssistantMessage(message)) {
25020
+ return [normalizeAssistantMessage(message)];
25021
+ }
25022
+ if (isPiToolResultMessage(message)) {
25023
+ return [normalizeToolResultMessage(message)];
25024
+ }
25025
+ return [];
25026
+ }
25027
+ function normalizeAssistantMessage(message) {
25028
+ const text = message.content.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
25029
+ const thinking = message.content.flatMap(
25030
+ (part) => part.type === "thinking" && !part.redacted ? [part.thinking] : []
25031
+ ).join("");
25032
+ const toolCalls = message.content.flatMap(
25033
+ (part) => part.type === "toolCall" ? [normalizeToolCall(part)] : []
25034
+ );
25035
+ return {
25036
+ role: "assistant",
25037
+ content: text || (toolCalls.length > 0 ? null : ""),
25038
+ ...thinking ? { reasoning: thinking } : {},
25039
+ ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
25040
+ };
25041
+ }
25042
+ function normalizeToolResultMessage(message) {
25043
+ return {
25044
+ role: "tool",
25045
+ tool_call_id: message.toolCallId,
25046
+ content: normalizeUserContent(message.content)
25047
+ };
25048
+ }
25049
+ function normalizeUserContent(content) {
25050
+ if (typeof content === "string") {
25051
+ return content;
25052
+ }
25053
+ return content.map((part) => {
25054
+ if (part.type === "text") {
25055
+ return { type: "text", text: part.text };
25056
+ }
25057
+ if (part.type === "image") {
25058
+ return {
25059
+ type: "image_url",
25060
+ image_url: {
25061
+ url: `data:${part.mimeType};base64,${part.data}`
25062
+ }
25063
+ };
25064
+ }
25065
+ return part;
25066
+ });
25067
+ }
25068
+ function normalizeToolCall(toolCall) {
25069
+ return {
25070
+ id: toolCall.id,
25071
+ type: "function",
25072
+ function: {
25073
+ name: toolCall.name,
25074
+ arguments: stringifyArguments(toolCall.arguments)
25075
+ }
25076
+ };
25077
+ }
25078
+ function extractAssistantOutput(message) {
25079
+ return processInputAttachments([
25080
+ {
25081
+ finish_reason: normalizeStopReason(message.stopReason),
25082
+ index: 0,
25083
+ message: normalizeAssistantMessage(message)
25084
+ }
25085
+ ]);
25086
+ }
25087
+ function isPiUserMessage(message) {
25088
+ return message.role === "user" && "content" in message;
25089
+ }
25090
+ function isPiAssistantMessage(message) {
25091
+ return isObject(message) && message.role === "assistant" && Array.isArray(message.content);
25092
+ }
25093
+ function isPiToolResultMessage(message) {
25094
+ return message.role === "toolResult" && Array.isArray(message.content);
25095
+ }
25096
+ function normalizeStopReason(reason) {
25097
+ switch (reason) {
25098
+ case "toolUse":
25099
+ return "tool_calls";
25100
+ case "length":
25101
+ case "stop":
25102
+ return reason;
25103
+ default:
25104
+ return reason ?? "stop";
25105
+ }
25106
+ }
25107
+ function extractPromptInput(text, options) {
25108
+ const images = options?.images;
25109
+ if (!images || images.length === 0) {
25110
+ return text;
25111
+ }
25112
+ return processInputAttachments([
25113
+ {
25114
+ role: "user",
25115
+ content: [
25116
+ { type: "text", text: text ?? "" },
25117
+ ...images.map((image) => ({
25118
+ type: "image_url",
25119
+ image_url: {
25120
+ url: `data:${image.mimeType};base64,${image.data}`
25121
+ }
25122
+ }))
25123
+ ]
25124
+ }
25125
+ ]);
25126
+ }
25127
+ function extractToolMetadata(tools) {
25128
+ if (!tools || tools.length === 0) {
25129
+ return {};
25130
+ }
25131
+ return {
25132
+ tools: tools.map((tool) => ({
25133
+ type: "function",
25134
+ function: {
25135
+ name: tool.name,
25136
+ ...tool.description ? { description: tool.description } : {},
25137
+ ...tool.parameters ? { parameters: tool.parameters } : {}
25138
+ }
25139
+ }))
25140
+ };
25141
+ }
25142
+ function extractModelMetadata2(model) {
25143
+ if (!model) {
25144
+ return {};
25145
+ }
25146
+ return {
25147
+ ...model.provider ? { provider: model.provider } : {},
25148
+ ...model.id ? { model: model.id, "pi_coding_agent.model": model.id } : {},
25149
+ ...model.api ? { "pi_coding_agent.api": model.api } : {},
25150
+ ...model.name ? { "pi_coding_agent.model_name": model.name } : {}
25151
+ };
25152
+ }
25153
+ function extractAssistantMetadata(message) {
25154
+ return {
25155
+ ...message.provider ? { provider: message.provider } : {},
25156
+ ...message.responseModel || message.model ? { model: message.responseModel ?? message.model } : {},
25157
+ ...message.api ? { "pi_coding_agent.api": message.api } : {},
25158
+ ...message.model ? { "pi_coding_agent.model": message.model } : {},
25159
+ ...message.responseModel ? { "pi_coding_agent.response_model": message.responseModel } : {},
25160
+ ...message.responseId ? { "pi_coding_agent.response_id": message.responseId } : {},
25161
+ ...message.stopReason ? { "pi_coding_agent.stop_reason": message.stopReason } : {}
25162
+ };
25163
+ }
25164
+ function extractSessionMetadata(session) {
25165
+ return {
25166
+ ...extractModelMetadata2(session.model),
25167
+ ...session.sessionId ? { "pi_coding_agent.session_id": session.sessionId } : {},
25168
+ ...session.sessionName ? { "pi_coding_agent.session_name": session.sessionName } : {},
25169
+ ...session.thinkingLevel ? { "pi_coding_agent.thinking_level": session.thinkingLevel } : {},
25170
+ ...typeof session.getActiveToolNames === "function" ? { "pi_coding_agent.active_tools": session.getActiveToolNames() } : {}
25171
+ };
25172
+ }
25173
+ function extractPromptOptionsMetadata(options) {
25174
+ if (!options) {
25175
+ return {};
25176
+ }
25177
+ return {
25178
+ ...options.source ? { "pi_coding_agent.source": options.source } : {},
25179
+ ...options.streamingBehavior ? { "pi_coding_agent.streaming_behavior": options.streamingBehavior } : {},
25180
+ ...options.expandPromptTemplates !== void 0 ? {
25181
+ "pi_coding_agent.expand_prompt_templates": options.expandPromptTemplates
25182
+ } : {}
25183
+ };
25184
+ }
25185
+ function extractStreamOptionsMetadata(options) {
25186
+ if (!options) {
25187
+ return {};
25188
+ }
25189
+ return {
25190
+ ...options.temperature !== void 0 ? { temperature: options.temperature } : {},
25191
+ ...options.maxTokens !== void 0 ? { max_tokens: options.maxTokens } : {},
25192
+ ...options.reasoning ? { "pi_coding_agent.reasoning": options.reasoning } : {},
25193
+ ...options.transport ? { "pi_coding_agent.transport": options.transport } : {},
25194
+ ...options.cacheRetention ? { "pi_coding_agent.cache_retention": options.cacheRetention } : {},
25195
+ ...options.sessionId ? { "pi_coding_agent.session_id": options.sessionId } : {},
25196
+ ...options.timeoutMs !== void 0 ? { "pi_coding_agent.timeout_ms": options.timeoutMs } : {},
25197
+ ...options.maxRetries !== void 0 ? { "pi_coding_agent.max_retries": options.maxRetries } : {},
25198
+ ...options.maxRetryDelayMs !== void 0 ? { "pi_coding_agent.max_retry_delay_ms": options.maxRetryDelayMs } : {},
25199
+ ...options.metadata ? { "pi_coding_agent.metadata": options.metadata } : {}
25200
+ };
25201
+ }
25202
+ function getLlmSpanName(model) {
25203
+ switch (model.api) {
25204
+ case "anthropic-messages":
25205
+ return "anthropic.messages.create";
25206
+ case "openai-completions":
25207
+ return "Chat Completion";
25208
+ case "openai-responses":
25209
+ case "azure-openai-responses":
25210
+ case "openai-codex-responses":
25211
+ return "openai.responses.create";
25212
+ case "google-generative-ai":
25213
+ case "google-vertex":
25214
+ return "generate_content";
25215
+ case "mistral-conversations":
25216
+ return "mistral.chat.stream";
25217
+ case "bedrock-converse-stream":
25218
+ return "bedrock.converse_stream";
25219
+ default:
25220
+ return "pi_ai.streamSimple";
25221
+ }
25222
+ }
25223
+ function extractUsageMetrics2(usage) {
25224
+ if (!usage) {
25225
+ return {};
25226
+ }
25227
+ return cleanMetrics5({
25228
+ completion_tokens: usage.output,
25229
+ prompt_cache_creation_tokens: usage.cacheWrite,
25230
+ prompt_cached_tokens: usage.cacheRead,
25231
+ prompt_tokens: usage.input,
25232
+ tokens: usage.totalTokens ?? usage.tokens
25233
+ });
25234
+ }
25235
+ function addMetrics(target, source) {
25236
+ for (const [key, value] of Object.entries(source)) {
25237
+ target[key] = (target[key] ?? 0) + value;
25238
+ }
25239
+ }
25240
+ function buildDurationMetrics3(startTime) {
25241
+ const end = getCurrentUnixTimestamp();
25242
+ return {
25243
+ duration: end - startTime,
25244
+ end,
25245
+ start: startTime
25246
+ };
25247
+ }
25248
+ function cleanMetrics5(metrics) {
25249
+ const cleaned = {};
25250
+ for (const [key, value] of Object.entries(metrics)) {
25251
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
25252
+ cleaned[key] = value;
25253
+ }
25254
+ }
25255
+ return cleaned;
25256
+ }
25257
+ function stringifyArguments(value) {
25258
+ if (typeof value === "string") {
25259
+ return value;
25260
+ }
25261
+ try {
25262
+ return JSON.stringify(value);
25263
+ } catch {
25264
+ return stringifyUnknown2(value);
25265
+ }
25266
+ }
25267
+ function stringifyUnknown2(value) {
25268
+ if (value instanceof Error) {
25269
+ return value.message;
25270
+ }
25271
+ if (typeof value === "string") {
25272
+ return value;
25273
+ }
25274
+ try {
25275
+ return JSON.stringify(value);
25276
+ } catch {
25277
+ return String(value);
25278
+ }
25279
+ }
25280
+ function safeLog4(span, event) {
25281
+ try {
25282
+ span.log(event);
25283
+ } catch (error) {
25284
+ logInstrumentationError4("Pi Coding Agent span log", error);
25285
+ }
25286
+ }
25287
+ function logInstrumentationError4(context, error) {
25288
+ debugLogger.debug(`${context}:`, error);
25289
+ }
25290
+
23240
25291
  // src/instrumentation/braintrust-plugin.ts
23241
25292
  function getIntegrationConfig(integrations, key) {
23242
25293
  return integrations[key];
@@ -23262,6 +25313,7 @@ var BraintrustPlugin = class extends BasePlugin {
23262
25313
  gitHubCopilotPlugin = null;
23263
25314
  fluePlugin = null;
23264
25315
  langChainPlugin = null;
25316
+ piCodingAgentPlugin = null;
23265
25317
  constructor(config = {}) {
23266
25318
  super();
23267
25319
  this.config = config;
@@ -23336,6 +25388,10 @@ var BraintrustPlugin = class extends BasePlugin {
23336
25388
  this.gitHubCopilotPlugin = new GitHubCopilotPlugin();
23337
25389
  this.gitHubCopilotPlugin.enable();
23338
25390
  }
25391
+ if (integrations.piCodingAgent !== false) {
25392
+ this.piCodingAgentPlugin = new PiCodingAgentPlugin();
25393
+ this.piCodingAgentPlugin.enable();
25394
+ }
23339
25395
  if (getIntegrationConfig(integrations, "flue") !== false) {
23340
25396
  this.fluePlugin = new FluePlugin();
23341
25397
  this.fluePlugin.enable();
@@ -23414,6 +25470,10 @@ var BraintrustPlugin = class extends BasePlugin {
23414
25470
  this.gitHubCopilotPlugin.disable();
23415
25471
  this.gitHubCopilotPlugin = null;
23416
25472
  }
25473
+ if (this.piCodingAgentPlugin) {
25474
+ this.piCodingAgentPlugin.disable();
25475
+ this.piCodingAgentPlugin = null;
25476
+ }
23417
25477
  if (this.fluePlugin) {
23418
25478
  this.fluePlugin.disable();
23419
25479
  this.fluePlugin = null;
@@ -23433,6 +25493,11 @@ var envIntegrationAliases = {
23433
25493
  openaicodexsdk: "openaiCodexSDK",
23434
25494
  codex: "openaiCodexSDK",
23435
25495
  "codex-sdk": "openaiCodexSDK",
25496
+ "pi-coding-agent": "piCodingAgent",
25497
+ "pi-coding-agent-sdk": "piCodingAgent",
25498
+ picodingagent: "piCodingAgent",
25499
+ picodingagentsdk: "piCodingAgent",
25500
+ "@earendil-works/pi-coding-agent": "piCodingAgent",
23436
25501
  anthropic: "anthropic",
23437
25502
  aisdk: "aisdk",
23438
25503
  "ai-sdk": "aisdk",
@@ -23498,7 +25563,8 @@ function getDefaultInstrumentationIntegrations() {
23498
25563
  genkit: true,
23499
25564
  gitHubCopilot: true,
23500
25565
  langchain: true,
23501
- langgraph: true
25566
+ langgraph: true,
25567
+ piCodingAgent: true
23502
25568
  };
23503
25569
  }
23504
25570
  function readDisabledInstrumentationEnvConfig(disabledList) {