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
@@ -7936,6 +7936,10 @@ var Experiment2 = class extends ObjectFetcher {
7936
7936
  return (await this.lazyMetadata.get()).project;
7937
7937
  })();
7938
7938
  }
7939
+ async _getBaseExperimentId() {
7940
+ const baseExperimentId = (await this.lazyMetadata.get()).experiment.fullInfo["base_exp_id"];
7941
+ return typeof baseExperimentId === "string" && baseExperimentId ? baseExperimentId : void 0;
7942
+ }
7939
7943
  parentObjectType() {
7940
7944
  return 1 /* EXPERIMENT */;
7941
7945
  }
@@ -8076,6 +8080,14 @@ var Experiment2 = class extends ObjectFetcher {
8076
8080
  comparisonExperimentId = baseExperiment.id;
8077
8081
  comparisonExperimentName = baseExperiment.name;
8078
8082
  }
8083
+ } else {
8084
+ try {
8085
+ const comparisonExperiment = await state.apiConn().get_json(`v1/experiment/${comparisonExperimentId}`);
8086
+ if (typeof comparisonExperiment["name"] === "string") {
8087
+ comparisonExperimentName = comparisonExperiment["name"];
8088
+ }
8089
+ } catch {
8090
+ }
8079
8091
  }
8080
8092
  try {
8081
8093
  const results = await state.apiConn().get_json(
@@ -10021,6 +10033,64 @@ var BasePlugin = class {
10021
10033
  }
10022
10034
  };
10023
10035
 
10036
+ // src/instrumentation/auto-instrumentation-suppression.ts
10037
+ var autoInstrumentationSuppressionStore;
10038
+ function suppressionStore() {
10039
+ autoInstrumentationSuppressionStore ??= isomorph_default.newAsyncLocalStorage();
10040
+ return autoInstrumentationSuppressionStore;
10041
+ }
10042
+ function currentFrames() {
10043
+ return suppressionStore().getStore()?.frames ?? [];
10044
+ }
10045
+ function isAutoInstrumentationSuppressed() {
10046
+ const frames = currentFrames();
10047
+ return frames[frames.length - 1]?.mode === "suppress";
10048
+ }
10049
+ function runWithAutoInstrumentationSuppressed(callback) {
10050
+ const frame = {
10051
+ id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-suppress"),
10052
+ mode: "suppress"
10053
+ };
10054
+ return suppressionStore().run(
10055
+ { frames: [...currentFrames(), frame] },
10056
+ callback
10057
+ );
10058
+ }
10059
+ function bindAutoInstrumentationSuppressionToStart(tracingChannel2) {
10060
+ const startChannel = tracingChannel2.start;
10061
+ if (!startChannel) {
10062
+ return void 0;
10063
+ }
10064
+ const store = suppressionStore();
10065
+ startChannel.bindStore(store, () => ({
10066
+ frames: [
10067
+ ...currentFrames(),
10068
+ {
10069
+ id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-suppress"),
10070
+ mode: "suppress"
10071
+ }
10072
+ ]
10073
+ }));
10074
+ return () => {
10075
+ startChannel.unbindStore(store);
10076
+ };
10077
+ }
10078
+ function enterAutoInstrumentationAllowed() {
10079
+ const frame = {
10080
+ id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-allow"),
10081
+ mode: "allow"
10082
+ };
10083
+ suppressionStore().enterWith({
10084
+ frames: [...currentFrames(), frame]
10085
+ });
10086
+ return () => {
10087
+ const frames = currentFrames().filter(
10088
+ (candidate) => candidate.id !== frame.id
10089
+ );
10090
+ suppressionStore().enterWith(frames.length > 0 ? { frames } : void 0);
10091
+ };
10092
+ }
10093
+
10024
10094
  // src/instrumentation/core/channel-tracing.ts
10025
10095
  function isSyncStreamLike(value) {
10026
10096
  return !!value && typeof value === "object" && typeof value.on === "function";
@@ -10052,16 +10122,33 @@ function startSpanForEvent(config, event, channelName) {
10052
10122
  metadata: mergeInputMetadata(metadata, spanInfoMetadata)
10053
10123
  });
10054
10124
  } catch (error) {
10055
- console.error(`Error extracting input for ${channelName}:`, error);
10125
+ debugLogger.error(`Error extracting input for ${channelName}:`, error);
10056
10126
  }
10057
10127
  return { span, startTime };
10058
10128
  }
10129
+ function shouldTraceEvent(config, event, channelName) {
10130
+ if (!config.shouldTrace) {
10131
+ return true;
10132
+ }
10133
+ try {
10134
+ return config.shouldTrace(event.arguments, event);
10135
+ } catch (error) {
10136
+ debugLogger.error(
10137
+ `Error checking trace predicate for ${channelName}:`,
10138
+ error
10139
+ );
10140
+ return true;
10141
+ }
10142
+ }
10059
10143
  function ensureSpanStateForEvent(states, config, event, channelName) {
10060
10144
  const key = event;
10061
10145
  const existing = states.get(key);
10062
10146
  if (existing) {
10063
10147
  return existing;
10064
10148
  }
10149
+ if (!shouldTraceEvent(config, event, channelName)) {
10150
+ return void 0;
10151
+ }
10065
10152
  const created = startSpanForEvent(config, event, channelName);
10066
10153
  states.set(key, created);
10067
10154
  return created;
@@ -10077,13 +10164,16 @@ function bindCurrentSpanStoreToStart(tracingChannel2, states, config, channelNam
10077
10164
  startChannel.bindStore(
10078
10165
  currentSpanStore,
10079
10166
  (event) => {
10080
- const span = ensureSpanStateForEvent(
10167
+ if (isAutoInstrumentationSuppressed()) {
10168
+ return currentSpanStore.getStore();
10169
+ }
10170
+ const spanState = ensureSpanStateForEvent(
10081
10171
  states,
10082
10172
  config,
10083
10173
  event,
10084
10174
  channelName
10085
- ).span;
10086
- return contextManager.wrapSpanForStore(span);
10175
+ );
10176
+ return spanState ? contextManager.wrapSpanForStore(spanState.span) : currentSpanStore.getStore();
10087
10177
  }
10088
10178
  );
10089
10179
  return () => {
@@ -10118,7 +10208,10 @@ function runStreamingCompletionHook(args) {
10118
10208
  startTime: args.startTime
10119
10209
  });
10120
10210
  } catch (error) {
10121
- console.error(`Error in onComplete hook for ${args.channelName}:`, error);
10211
+ debugLogger.error(
10212
+ `Error in onComplete hook for ${args.channelName}:`,
10213
+ error
10214
+ );
10122
10215
  }
10123
10216
  }
10124
10217
  function traceAsyncChannel(channel2, config) {
@@ -10133,6 +10226,9 @@ function traceAsyncChannel(channel2, config) {
10133
10226
  );
10134
10227
  const handlers = {
10135
10228
  start: (event) => {
10229
+ if (isAutoInstrumentationSuppressed()) {
10230
+ return;
10231
+ }
10136
10232
  ensureSpanStateForEvent(
10137
10233
  states,
10138
10234
  config,
@@ -10167,7 +10263,7 @@ function traceAsyncChannel(channel2, config) {
10167
10263
  metrics
10168
10264
  });
10169
10265
  } catch (error) {
10170
- console.error(`Error extracting output for ${channelName}:`, error);
10266
+ debugLogger.error(`Error extracting output for ${channelName}:`, error);
10171
10267
  } finally {
10172
10268
  span.end();
10173
10269
  states.delete(event);
@@ -10195,6 +10291,9 @@ function traceStreamingChannel(channel2, config) {
10195
10291
  );
10196
10292
  const handlers = {
10197
10293
  start: (event) => {
10294
+ if (isAutoInstrumentationSuppressed()) {
10295
+ return;
10296
+ }
10198
10297
  ensureSpanStateForEvent(
10199
10298
  states,
10200
10299
  config,
@@ -10266,7 +10365,7 @@ function traceStreamingChannel(channel2, config) {
10266
10365
  metrics
10267
10366
  });
10268
10367
  } catch (error) {
10269
- console.error(
10368
+ debugLogger.error(
10270
10369
  `Error extracting output for ${channelName}:`,
10271
10370
  error
10272
10371
  );
@@ -10326,7 +10425,7 @@ function traceStreamingChannel(channel2, config) {
10326
10425
  metrics
10327
10426
  });
10328
10427
  } catch (error) {
10329
- console.error(`Error extracting output for ${channelName}:`, error);
10428
+ debugLogger.error(`Error extracting output for ${channelName}:`, error);
10330
10429
  } finally {
10331
10430
  span.end();
10332
10431
  states.delete(event);
@@ -10354,6 +10453,9 @@ function traceSyncStreamChannel(channel2, config) {
10354
10453
  );
10355
10454
  const handlers = {
10356
10455
  start: (event) => {
10456
+ if (isAutoInstrumentationSuppressed()) {
10457
+ return;
10458
+ }
10357
10459
  ensureSpanStateForEvent(
10358
10460
  states,
10359
10461
  config,
@@ -10407,7 +10509,7 @@ function traceSyncStreamChannel(channel2, config) {
10407
10509
  });
10408
10510
  }
10409
10511
  } catch (error) {
10410
- console.error(
10512
+ debugLogger.error(
10411
10513
  `Error extracting chatCompletion for ${channelName}:`,
10412
10514
  error
10413
10515
  );
@@ -10431,7 +10533,10 @@ function traceSyncStreamChannel(channel2, config) {
10431
10533
  span.log(extracted);
10432
10534
  }
10433
10535
  } catch (error) {
10434
- console.error(`Error extracting event for ${channelName}:`, error);
10536
+ debugLogger.error(
10537
+ `Error extracting event for ${channelName}:`,
10538
+ error
10539
+ );
10435
10540
  }
10436
10541
  });
10437
10542
  stream.on("end", () => {
@@ -11960,6 +12065,9 @@ var AnthropicPlugin = class extends BasePlugin {
11960
12065
  const states = /* @__PURE__ */ new WeakMap();
11961
12066
  const handlers = {
11962
12067
  start: (event) => {
12068
+ if (isAutoInstrumentationSuppressed()) {
12069
+ return;
12070
+ }
11963
12071
  const params = event.arguments[0] ?? {};
11964
12072
  const span = startSpan({
11965
12073
  name: "anthropic.beta.messages.toolRunner",
@@ -12689,6 +12797,520 @@ function serializeAISDKToolsForLogging(tools) {
12689
12797
  return serialized;
12690
12798
  }
12691
12799
 
12800
+ // src/wrappers/ai-sdk/telemetry.ts
12801
+ function braintrustAISDKTelemetry() {
12802
+ const operations = /* @__PURE__ */ new Map();
12803
+ const modelSpans = /* @__PURE__ */ new Map();
12804
+ const objectSpans = /* @__PURE__ */ new Map();
12805
+ const embedSpans = /* @__PURE__ */ new Map();
12806
+ const rerankSpans = /* @__PURE__ */ new Map();
12807
+ const toolSpans = /* @__PURE__ */ new Map();
12808
+ const runSafely = (name, callback) => {
12809
+ try {
12810
+ callback();
12811
+ } catch (error) {
12812
+ console.error(`Error in Braintrust AI SDK telemetry ${name}:`, error);
12813
+ }
12814
+ };
12815
+ const startChildSpan = (callId, name, type, event) => {
12816
+ const parent = operations.get(callId)?.span;
12817
+ const spanArgs = {
12818
+ name,
12819
+ spanAttributes: { type },
12820
+ ...event ? { event } : {}
12821
+ };
12822
+ const span = parent ? parent.startSpan(spanArgs) : startSpan(spanArgs);
12823
+ const state = operations.get(callId);
12824
+ if (state && type === "llm" /* LLM */) {
12825
+ state.hadModelChild = true;
12826
+ }
12827
+ return span;
12828
+ };
12829
+ return {
12830
+ onStart(event) {
12831
+ runSafely("onStart", () => {
12832
+ const operationName = operationNameFromId(event.operationId);
12833
+ const span = startSpan({
12834
+ name: operationName,
12835
+ spanAttributes: { type: "function" /* FUNCTION */ }
12836
+ });
12837
+ operations.set(event.callId, {
12838
+ hadModelChild: false,
12839
+ operationName,
12840
+ span,
12841
+ startTime: getCurrentUnixTimestamp()
12842
+ });
12843
+ const metadata = metadataFromEvent(event);
12844
+ const logPayload = { metadata };
12845
+ if (shouldRecordInputs(event)) {
12846
+ const { input, outputPromise } = processAISDKCallInput(
12847
+ operationInput(event, operationName)
12848
+ );
12849
+ logPayload.input = input;
12850
+ if (outputPromise && input && typeof input === "object") {
12851
+ outputPromise.then((resolvedData) => {
12852
+ span.log({
12853
+ input: {
12854
+ ...input,
12855
+ ...resolvedData
12856
+ }
12857
+ });
12858
+ }).catch(() => {
12859
+ });
12860
+ }
12861
+ }
12862
+ span.log(logPayload);
12863
+ });
12864
+ },
12865
+ onLanguageModelCallStart(event) {
12866
+ runSafely("onLanguageModelCallStart", () => {
12867
+ const state = operations.get(event.callId);
12868
+ const openSpans = modelSpans.get(event.callId);
12869
+ if (openSpans) {
12870
+ for (const span2 of openSpans) {
12871
+ span2.end();
12872
+ }
12873
+ modelSpans.delete(event.callId);
12874
+ }
12875
+ const span = startChildSpan(
12876
+ event.callId,
12877
+ state?.operationName === "streamText" ? "doStream" : "doGenerate",
12878
+ "llm" /* LLM */,
12879
+ {
12880
+ ...shouldRecordInputs(event) ? {
12881
+ input: processAISDKCallInput(
12882
+ operationInput(
12883
+ event,
12884
+ state?.operationName ?? "generateText"
12885
+ )
12886
+ ).input
12887
+ } : {},
12888
+ metadata: metadataFromEvent(event)
12889
+ }
12890
+ );
12891
+ const spans = modelSpans.get(event.callId) ?? [];
12892
+ spans.push(span);
12893
+ modelSpans.set(event.callId, spans);
12894
+ });
12895
+ },
12896
+ onLanguageModelCallEnd(event) {
12897
+ runSafely("onLanguageModelCallEnd", () => {
12898
+ const span = shiftModelSpan(modelSpans, event.callId);
12899
+ if (!span) {
12900
+ return;
12901
+ }
12902
+ const result = {
12903
+ ...event,
12904
+ response: event.responseId ? { id: event.responseId } : void 0
12905
+ };
12906
+ span.log({
12907
+ ...shouldRecordOutputs(event) ? {
12908
+ output: processAISDKOutput(result, DEFAULT_DENY_OUTPUT_PATHS)
12909
+ } : {},
12910
+ metrics: extractTokenMetrics(result)
12911
+ });
12912
+ span.end();
12913
+ });
12914
+ },
12915
+ onObjectStepStart(event) {
12916
+ runSafely("onObjectStepStart", () => {
12917
+ const state = operations.get(event.callId);
12918
+ const openSpan = objectSpans.get(event.callId);
12919
+ if (openSpan) {
12920
+ openSpan.end();
12921
+ objectSpans.delete(event.callId);
12922
+ }
12923
+ const span = startChildSpan(
12924
+ event.callId,
12925
+ state?.operationName === "streamObject" ? "doStream" : "doGenerate",
12926
+ "llm" /* LLM */,
12927
+ {
12928
+ ...shouldRecordInputs(event) ? {
12929
+ input: {
12930
+ prompt: event.promptMessages
12931
+ }
12932
+ } : {},
12933
+ metadata: metadataFromEvent(event)
12934
+ }
12935
+ );
12936
+ objectSpans.set(event.callId, span);
12937
+ });
12938
+ },
12939
+ onObjectStepFinish(event) {
12940
+ runSafely("onObjectStepFinish", () => {
12941
+ const span = objectSpans.get(event.callId);
12942
+ if (!span) {
12943
+ return;
12944
+ }
12945
+ const result = {
12946
+ ...event,
12947
+ text: event.objectText
12948
+ };
12949
+ span.log({
12950
+ ...shouldRecordOutputs(event) ? {
12951
+ output: processAISDKOutput(result, DEFAULT_DENY_OUTPUT_PATHS)
12952
+ } : {},
12953
+ metrics: extractTokenMetrics(result)
12954
+ });
12955
+ span.end();
12956
+ objectSpans.delete(event.callId);
12957
+ });
12958
+ },
12959
+ onEmbedStart(event) {
12960
+ runSafely("onEmbedStart", () => {
12961
+ const state = operations.get(event.callId);
12962
+ for (const [embedCallId, embedState] of embedSpans) {
12963
+ if (embedState.callId === event.callId && (state?.operationName === "embed" || embedState.values === event.values)) {
12964
+ embedState.span.end();
12965
+ embedSpans.delete(embedCallId);
12966
+ }
12967
+ }
12968
+ const span = startChildSpan(
12969
+ event.callId,
12970
+ "doEmbed",
12971
+ "llm" /* LLM */,
12972
+ {
12973
+ ...shouldRecordInputs(event) ? {
12974
+ input: {
12975
+ values: event.values
12976
+ }
12977
+ } : {},
12978
+ metadata: metadataFromEvent(event)
12979
+ }
12980
+ );
12981
+ embedSpans.set(event.embedCallId, {
12982
+ callId: event.callId,
12983
+ span,
12984
+ values: event.values
12985
+ });
12986
+ });
12987
+ },
12988
+ onEmbedFinish(event) {
12989
+ runSafely("onEmbedFinish", () => {
12990
+ const state = embedSpans.get(event.embedCallId);
12991
+ if (!state) {
12992
+ return;
12993
+ }
12994
+ const result = {
12995
+ ...event,
12996
+ embeddings: event.embeddings
12997
+ };
12998
+ state.span.log({
12999
+ ...shouldRecordOutputs(event) ? {
13000
+ output: processAISDKEmbeddingOutput(
13001
+ result,
13002
+ DEFAULT_DENY_OUTPUT_PATHS
13003
+ )
13004
+ } : {},
13005
+ metrics: extractTokenMetrics(result)
13006
+ });
13007
+ state.span.end();
13008
+ embedSpans.delete(event.embedCallId);
13009
+ });
13010
+ },
13011
+ onRerankStart(event) {
13012
+ runSafely("onRerankStart", () => {
13013
+ const openSpan = rerankSpans.get(event.callId);
13014
+ if (openSpan) {
13015
+ openSpan.end();
13016
+ rerankSpans.delete(event.callId);
13017
+ }
13018
+ const span = startChildSpan(
13019
+ event.callId,
13020
+ "doRerank",
13021
+ "llm" /* LLM */,
13022
+ {
13023
+ ...shouldRecordInputs(event) ? {
13024
+ input: {
13025
+ documents: event.documents,
13026
+ query: event.query,
13027
+ topN: event.topN
13028
+ }
13029
+ } : {},
13030
+ metadata: metadataFromEvent(event)
13031
+ }
13032
+ );
13033
+ rerankSpans.set(event.callId, span);
13034
+ });
13035
+ },
13036
+ onRerankFinish(event) {
13037
+ runSafely("onRerankFinish", () => {
13038
+ const span = rerankSpans.get(event.callId);
13039
+ if (!span) {
13040
+ return;
13041
+ }
13042
+ const result = {
13043
+ ranking: event.ranking?.map((entry) => ({
13044
+ originalIndex: entry.index,
13045
+ score: entry.relevanceScore
13046
+ }))
13047
+ };
13048
+ span.log({
13049
+ ...shouldRecordOutputs(event) ? {
13050
+ output: processAISDKRerankOutput(
13051
+ result,
13052
+ DEFAULT_DENY_OUTPUT_PATHS
13053
+ )
13054
+ } : {}
13055
+ });
13056
+ span.end();
13057
+ rerankSpans.delete(event.callId);
13058
+ });
13059
+ },
13060
+ onToolExecutionStart(event) {
13061
+ runSafely("onToolExecutionStart", () => {
13062
+ const toolCallId = event.toolCall.toolCallId;
13063
+ if (!toolCallId) {
13064
+ return;
13065
+ }
13066
+ const span = startChildSpan(
13067
+ event.callId,
13068
+ event.toolCall.toolName || "tool",
13069
+ "tool" /* TOOL */,
13070
+ {
13071
+ ...shouldRecordInputs(event) ? {
13072
+ input: event.toolCall.input
13073
+ } : {},
13074
+ metadata: {
13075
+ ...createAISDKIntegrationMetadata(),
13076
+ toolCallId,
13077
+ ...typeof event.toolCall.toolName === "string" ? { toolName: event.toolCall.toolName } : {}
13078
+ }
13079
+ }
13080
+ );
13081
+ toolSpans.set(toolCallId, { callId: event.callId, span });
13082
+ });
13083
+ },
13084
+ onToolExecutionEnd(event) {
13085
+ runSafely("onToolExecutionEnd", () => {
13086
+ const toolCallId = event.toolCall.toolCallId;
13087
+ const state = toolCallId ? toolSpans.get(toolCallId) : void 0;
13088
+ if (!toolCallId || !state) {
13089
+ return;
13090
+ }
13091
+ const toolOutput = event.toolOutput;
13092
+ if (toolOutput?.type === "tool-error") {
13093
+ state.span.log({
13094
+ error: toolOutput.error instanceof Error ? toolOutput.error.message : String(toolOutput?.error),
13095
+ metrics: typeof event.durationMs === "number" ? { duration_ms: event.durationMs } : {}
13096
+ });
13097
+ } else {
13098
+ state.span.log({
13099
+ ...shouldRecordOutputs(event) ? { output: toolOutput?.output } : {},
13100
+ metrics: typeof event.durationMs === "number" ? { duration_ms: event.durationMs } : {}
13101
+ });
13102
+ }
13103
+ state.span.end();
13104
+ toolSpans.delete(toolCallId);
13105
+ });
13106
+ },
13107
+ onChunk(event) {
13108
+ runSafely("onChunk", () => {
13109
+ const callId = event.chunk?.callId;
13110
+ if (!callId) {
13111
+ return;
13112
+ }
13113
+ const state = operations.get(callId);
13114
+ if (!state || state.firstChunkTime !== void 0) {
13115
+ return;
13116
+ }
13117
+ state.firstChunkTime = getCurrentUnixTimestamp();
13118
+ });
13119
+ },
13120
+ onFinish(event) {
13121
+ runSafely("onFinish", () => {
13122
+ const state = operations.get(event.callId);
13123
+ if (!state) {
13124
+ return;
13125
+ }
13126
+ const result = finishResult(event, state.operationName);
13127
+ const metrics = state.hadModelChild ? {} : extractTokenMetrics(result);
13128
+ if (state.firstChunkTime !== void 0) {
13129
+ metrics.time_to_first_token = state.firstChunkTime - state.startTime;
13130
+ }
13131
+ state.span.log({
13132
+ ...shouldRecordOutputs(event) ? {
13133
+ output: finishOutput(result, state.operationName)
13134
+ } : {},
13135
+ metrics
13136
+ });
13137
+ state.span.end();
13138
+ operations.delete(event.callId);
13139
+ });
13140
+ },
13141
+ onError(event) {
13142
+ runSafely("onError", () => {
13143
+ const errorEvent = isObject(event) ? event : {};
13144
+ const callId = typeof errorEvent.callId === "string" ? errorEvent.callId : void 0;
13145
+ if (!callId) {
13146
+ return;
13147
+ }
13148
+ const state = operations.get(callId);
13149
+ if (!state) {
13150
+ return;
13151
+ }
13152
+ const error = errorEvent.error ?? event;
13153
+ const openModelSpans = modelSpans.get(callId);
13154
+ if (openModelSpans) {
13155
+ for (const span of openModelSpans) {
13156
+ logError(span, error);
13157
+ span.end();
13158
+ }
13159
+ modelSpans.delete(callId);
13160
+ }
13161
+ const openObjectSpan = objectSpans.get(callId);
13162
+ if (openObjectSpan) {
13163
+ logError(openObjectSpan, error);
13164
+ openObjectSpan.end();
13165
+ objectSpans.delete(callId);
13166
+ }
13167
+ for (const [embedCallId, embedState] of embedSpans) {
13168
+ if (embedState.callId === callId) {
13169
+ logError(embedState.span, error);
13170
+ embedState.span.end();
13171
+ embedSpans.delete(embedCallId);
13172
+ }
13173
+ }
13174
+ const openRerankSpan = rerankSpans.get(callId);
13175
+ if (openRerankSpan) {
13176
+ logError(openRerankSpan, error);
13177
+ openRerankSpan.end();
13178
+ rerankSpans.delete(callId);
13179
+ }
13180
+ for (const [toolCallId, toolState] of toolSpans) {
13181
+ if (toolState.callId === callId) {
13182
+ logError(toolState.span, error);
13183
+ toolState.span.end();
13184
+ toolSpans.delete(toolCallId);
13185
+ }
13186
+ }
13187
+ logError(state.span, error);
13188
+ state.span.end();
13189
+ operations.delete(callId);
13190
+ });
13191
+ },
13192
+ executeTool({ toolCallId, execute }) {
13193
+ const state = toolSpans.get(toolCallId);
13194
+ return state ? withCurrent(state.span, () => execute()) : execute();
13195
+ }
13196
+ };
13197
+ }
13198
+ function shouldRecordInputs(event) {
13199
+ return event.recordInputs !== false;
13200
+ }
13201
+ function shouldRecordOutputs(event) {
13202
+ return event.recordOutputs !== false;
13203
+ }
13204
+ function operationNameFromId(operationId) {
13205
+ return operationId?.startsWith("ai.") ? operationId.slice("ai.".length) : operationId || "ai-sdk";
13206
+ }
13207
+ function modelFromEvent(event) {
13208
+ return event.modelId ? {
13209
+ modelId: event.modelId,
13210
+ ...event.provider ? { provider: event.provider } : {}
13211
+ } : void 0;
13212
+ }
13213
+ function metadataFromEvent(event) {
13214
+ const metadata = createAISDKIntegrationMetadata();
13215
+ const { model, provider } = serializeModelWithProvider(modelFromEvent(event));
13216
+ if (model) {
13217
+ metadata.model = model;
13218
+ }
13219
+ if (provider) {
13220
+ metadata.provider = provider;
13221
+ }
13222
+ if (typeof event.functionId === "string") {
13223
+ metadata.functionId = event.functionId;
13224
+ }
13225
+ return metadata;
13226
+ }
13227
+ function operationInput(event, operationName) {
13228
+ if (operationName === "embed") {
13229
+ return {
13230
+ model: modelFromEvent(event),
13231
+ value: event.value
13232
+ };
13233
+ }
13234
+ if (operationName === "embedMany") {
13235
+ return {
13236
+ model: modelFromEvent(event),
13237
+ values: Array.isArray(event.value) ? event.value ?? [] : void 0
13238
+ };
13239
+ }
13240
+ if (operationName === "rerank") {
13241
+ return {
13242
+ model: modelFromEvent(event),
13243
+ documents: event.documents,
13244
+ query: event.query,
13245
+ topN: event.topN
13246
+ };
13247
+ }
13248
+ return {
13249
+ model: modelFromEvent(event),
13250
+ system: event.system,
13251
+ prompt: event.prompt,
13252
+ messages: event.messages,
13253
+ tools: event.tools,
13254
+ toolChoice: event.toolChoice,
13255
+ activeTools: event.activeTools,
13256
+ output: event.output,
13257
+ schema: event.schema,
13258
+ schemaName: event.schemaName,
13259
+ schemaDescription: event.schemaDescription,
13260
+ maxOutputTokens: event.maxOutputTokens,
13261
+ temperature: event.temperature,
13262
+ topP: event.topP,
13263
+ topK: event.topK,
13264
+ presencePenalty: event.presencePenalty,
13265
+ frequencyPenalty: event.frequencyPenalty,
13266
+ seed: event.seed,
13267
+ maxRetries: event.maxRetries,
13268
+ headers: event.headers,
13269
+ providerOptions: event.providerOptions
13270
+ };
13271
+ }
13272
+ function shiftModelSpan(modelSpans, callId) {
13273
+ const spans = modelSpans.get(callId);
13274
+ const span = spans?.shift();
13275
+ if (spans && spans.length === 0) {
13276
+ modelSpans.delete(callId);
13277
+ }
13278
+ return span;
13279
+ }
13280
+ function finishResult(event, operationName) {
13281
+ if (operationName === "embed") {
13282
+ return {
13283
+ ...event,
13284
+ embedding: event.embedding
13285
+ };
13286
+ }
13287
+ if (operationName === "embedMany") {
13288
+ return {
13289
+ ...event,
13290
+ embeddings: event.embedding
13291
+ };
13292
+ }
13293
+ if (operationName === "rerank") {
13294
+ return event;
13295
+ }
13296
+ return event;
13297
+ }
13298
+ function finishOutput(result, operationName) {
13299
+ if (operationName === "embed" || operationName === "embedMany") {
13300
+ return processAISDKEmbeddingOutput(
13301
+ result,
13302
+ DEFAULT_DENY_OUTPUT_PATHS
13303
+ );
13304
+ }
13305
+ if (operationName === "rerank") {
13306
+ return processAISDKRerankOutput(
13307
+ result,
13308
+ DEFAULT_DENY_OUTPUT_PATHS
13309
+ );
13310
+ }
13311
+ return processAISDKOutput(result, DEFAULT_DENY_OUTPUT_PATHS);
13312
+ }
13313
+
12692
13314
  // src/instrumentation/plugins/ai-sdk-channels.ts
12693
13315
  var aiSDKChannels = defineChannels("ai", {
12694
13316
  generateText: channel({
@@ -12748,6 +13370,10 @@ var aiSDKChannels = defineChannels("ai", {
12748
13370
  toolLoopAgentStream: channel({
12749
13371
  channelName: "ToolLoopAgent.stream",
12750
13372
  kind: "async"
13373
+ }),
13374
+ v7CreateTelemetryDispatcher: channel({
13375
+ channelName: "createTelemetryDispatcher",
13376
+ kind: "sync-stream"
12751
13377
  })
12752
13378
  });
12753
13379
 
@@ -12768,9 +13394,30 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
12768
13394
  ];
12769
13395
  var AUTO_PATCHED_MODEL = /* @__PURE__ */ Symbol.for("braintrust.ai-sdk.auto-patched-model");
12770
13396
  var AUTO_PATCHED_TOOL = /* @__PURE__ */ Symbol.for("braintrust.ai-sdk.auto-patched-tool");
13397
+ var AUTO_PATCHED_V7_TELEMETRY_DISPATCHER = /* @__PURE__ */ Symbol.for(
13398
+ "braintrust.ai-sdk.v7.auto-patched-telemetry-dispatcher"
13399
+ );
12771
13400
  var RUNTIME_DENY_OUTPUT_PATHS = /* @__PURE__ */ Symbol.for(
12772
13401
  "braintrust.ai-sdk.deny-output-paths"
12773
13402
  );
13403
+ var AI_SDK_V7_TELEMETRY_CALLBACKS = [
13404
+ "onStart",
13405
+ "onStepStart",
13406
+ "onLanguageModelCallStart",
13407
+ "onLanguageModelCallEnd",
13408
+ "onToolExecutionStart",
13409
+ "onToolExecutionEnd",
13410
+ "onChunk",
13411
+ "onStepFinish",
13412
+ "onObjectStepStart",
13413
+ "onObjectStepFinish",
13414
+ "onEmbedStart",
13415
+ "onEmbedFinish",
13416
+ "onRerankStart",
13417
+ "onRerankFinish",
13418
+ "onFinish",
13419
+ "onError"
13420
+ ];
12774
13421
  var AISDKPlugin = class extends BasePlugin {
12775
13422
  config;
12776
13423
  constructor(config = {}) {
@@ -12785,6 +13432,7 @@ var AISDKPlugin = class extends BasePlugin {
12785
13432
  }
12786
13433
  subscribeToAISDK() {
12787
13434
  const denyOutputPaths = this.config.denyOutputPaths || DEFAULT_DENY_OUTPUT_PATHS;
13435
+ this.unsubscribers.push(subscribeToAISDKV7TelemetryDispatcher());
12788
13436
  this.unsubscribers.push(
12789
13437
  traceStreamingChannel(aiSDKChannels.generateText, {
12790
13438
  name: "generateText",
@@ -13009,14 +13657,65 @@ var AISDKPlugin = class extends BasePlugin {
13009
13657
  );
13010
13658
  }
13011
13659
  };
13012
- function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
13013
- if (Array.isArray(event?.denyOutputPaths)) {
13014
- return event.denyOutputPaths;
13015
- }
13016
- const firstArgument2 = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
13017
- if (!firstArgument2 || typeof firstArgument2 !== "object") {
13018
- return defaultDenyOutputPaths;
13019
- }
13660
+ function subscribeToAISDKV7TelemetryDispatcher() {
13661
+ const channel2 = aiSDKChannels.v7CreateTelemetryDispatcher.tracingChannel();
13662
+ const telemetry = braintrustAISDKTelemetry();
13663
+ const handlers = {
13664
+ end: (event) => {
13665
+ const telemetryOptions = event.arguments?.[0]?.telemetry;
13666
+ if (telemetryOptions?.isEnabled === false) {
13667
+ return;
13668
+ }
13669
+ patchAISDKV7TelemetryDispatcher(event.result, telemetry);
13670
+ }
13671
+ };
13672
+ channel2.subscribe(handlers);
13673
+ return () => {
13674
+ channel2.unsubscribe(handlers);
13675
+ };
13676
+ }
13677
+ function patchAISDKV7TelemetryDispatcher(dispatcher, telemetry) {
13678
+ if (!isObject(dispatcher)) {
13679
+ return;
13680
+ }
13681
+ const dispatcherRecord = dispatcher;
13682
+ if (dispatcherRecord[AUTO_PATCHED_V7_TELEMETRY_DISPATCHER]) {
13683
+ return;
13684
+ }
13685
+ dispatcherRecord[AUTO_PATCHED_V7_TELEMETRY_DISPATCHER] = true;
13686
+ for (const key of AI_SDK_V7_TELEMETRY_CALLBACKS) {
13687
+ const braintrustCallback = telemetry[key];
13688
+ if (typeof braintrustCallback !== "function") {
13689
+ continue;
13690
+ }
13691
+ const existingCallback = dispatcherRecord[key];
13692
+ dispatcherRecord[key] = (event) => {
13693
+ const existingResult = typeof existingCallback === "function" ? existingCallback.call(dispatcher, event) : void 0;
13694
+ const braintrustResult = braintrustCallback.call(telemetry, event);
13695
+ const pending = [existingResult, braintrustResult].filter(isPromiseLike);
13696
+ if (pending.length > 0) {
13697
+ return Promise.allSettled(pending).then(() => void 0);
13698
+ }
13699
+ };
13700
+ }
13701
+ const braintrustExecuteTool = telemetry.executeTool;
13702
+ if (typeof braintrustExecuteTool !== "function") {
13703
+ return;
13704
+ }
13705
+ const existingExecuteTool = dispatcherRecord.executeTool;
13706
+ dispatcherRecord.executeTool = (args) => braintrustExecuteTool.call(telemetry, {
13707
+ ...args,
13708
+ execute: () => typeof existingExecuteTool === "function" ? existingExecuteTool.call(dispatcher, args) : args.execute()
13709
+ });
13710
+ }
13711
+ function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
13712
+ if (Array.isArray(event?.denyOutputPaths)) {
13713
+ return event.denyOutputPaths;
13714
+ }
13715
+ const firstArgument2 = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
13716
+ if (!firstArgument2 || typeof firstArgument2 !== "object") {
13717
+ return defaultDenyOutputPaths;
13718
+ }
13020
13719
  const runtimeDenyOutputPaths = firstArgument2[RUNTIME_DENY_OUTPUT_PATHS];
13021
13720
  if (Array.isArray(runtimeDenyOutputPaths) && runtimeDenyOutputPaths.every((path2) => typeof path2 === "string")) {
13022
13721
  return runtimeDenyOutputPaths;
@@ -13460,11 +14159,11 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
13460
14159
  metadata: baseMetadata
13461
14160
  }
13462
14161
  });
14162
+ const streamStartTime = getCurrentUnixTimestamp();
13463
14163
  const result = await withCurrent(
13464
14164
  span,
13465
14165
  () => Reflect.apply(originalDoStream, resolvedModel, [options])
13466
14166
  );
13467
- const streamStartTime = getCurrentUnixTimestamp();
13468
14167
  let firstChunkTime;
13469
14168
  const output = {};
13470
14169
  let text = "";
@@ -13473,7 +14172,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
13473
14172
  let object = void 0;
13474
14173
  const transformStream = new TransformStream({
13475
14174
  transform(chunk, controller) {
13476
- if (firstChunkTime === void 0) {
14175
+ if (firstChunkTime === void 0 && isAISDKContentStreamChunk(chunk)) {
13477
14176
  firstChunkTime = getCurrentUnixTimestamp();
13478
14177
  }
13479
14178
  switch (chunk.type) {
@@ -13665,6 +14364,78 @@ function finalizeAISDKChildTracing(event) {
13665
14364
  delete event.__braintrust_ai_sdk_cleanup;
13666
14365
  }
13667
14366
  }
14367
+ function extractAISDKStreamPart(chunk) {
14368
+ if (!isObject(chunk) || !isObject(chunk.part)) {
14369
+ return chunk;
14370
+ }
14371
+ return chunk.part;
14372
+ }
14373
+ function stringContent(value) {
14374
+ return typeof value === "string" && value.length > 0 ? value : void 0;
14375
+ }
14376
+ function rawValueHasAISDKContent(value) {
14377
+ if (value === void 0 || value === null) {
14378
+ return false;
14379
+ }
14380
+ if (typeof value === "string") {
14381
+ return value.length > 0;
14382
+ }
14383
+ if (!isObject(value)) {
14384
+ return true;
14385
+ }
14386
+ const delta = value.delta;
14387
+ 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) => {
14388
+ if (!isObject(choice) || !isObject(choice.delta)) {
14389
+ return false;
14390
+ }
14391
+ const choiceDelta = choice.delta;
14392
+ if (stringContent(choiceDelta.content) || stringContent(choiceDelta.text)) {
14393
+ return true;
14394
+ }
14395
+ if (isObject(choiceDelta.function_call) && stringContent(choiceDelta.function_call.arguments)) {
14396
+ return true;
14397
+ }
14398
+ return Array.isArray(choiceDelta.tool_calls) && choiceDelta.tool_calls.some(
14399
+ (toolCall) => isObject(toolCall) && isObject(toolCall.function) && stringContent(toolCall.function.arguments)
14400
+ );
14401
+ })) {
14402
+ return true;
14403
+ }
14404
+ return false;
14405
+ }
14406
+ function isAISDKContentStreamChunk(chunk) {
14407
+ const part = extractAISDKStreamPart(chunk);
14408
+ if (typeof part === "string") {
14409
+ return part.length > 0;
14410
+ }
14411
+ if (!isObject(part) || typeof part.type !== "string") {
14412
+ return false;
14413
+ }
14414
+ switch (part.type) {
14415
+ case "text-delta":
14416
+ return stringContent(part.textDelta) !== void 0 || stringContent(part.delta) !== void 0 || stringContent(part.text) !== void 0 || stringContent(part.content) !== void 0;
14417
+ case "reasoning-delta":
14418
+ return stringContent(part.delta) !== void 0 || stringContent(part.text) !== void 0 || stringContent(part.content) !== void 0;
14419
+ case "tool-call":
14420
+ case "object":
14421
+ case "file":
14422
+ return true;
14423
+ case "tool-input-delta":
14424
+ case "tool-call-delta":
14425
+ 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;
14426
+ case "raw":
14427
+ return rawValueHasAISDKContent(part.rawValue);
14428
+ default:
14429
+ return false;
14430
+ }
14431
+ }
14432
+ function isAISDKContentAsyncIterableChunk(chunk) {
14433
+ if (isAISDKContentStreamChunk(chunk)) {
14434
+ return true;
14435
+ }
14436
+ const part = extractAISDKStreamPart(chunk);
14437
+ return isObject(part) && typeof part.type !== "string";
14438
+ }
13668
14439
  function patchAISDKStreamingResult(args) {
13669
14440
  const { defaultDenyOutputPaths, endEvent, result, span, startTime } = args;
13670
14441
  if (!result || typeof result !== "object") {
@@ -13677,7 +14448,7 @@ function patchAISDKStreamingResult(args) {
13677
14448
  const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
13678
14449
  new TransformStream({
13679
14450
  transform(chunk, controller) {
13680
- if (firstChunkTime2 === void 0) {
14451
+ if (firstChunkTime2 === void 0 && isAISDKContentStreamChunk(chunk)) {
13681
14452
  firstChunkTime2 = getCurrentUnixTimestamp();
13682
14453
  }
13683
14454
  controller.enqueue(chunk);
@@ -13721,8 +14492,8 @@ function patchAISDKStreamingResult(args) {
13721
14492
  }
13722
14493
  let firstChunkTime;
13723
14494
  const wrappedStream = createPatchedAsyncIterable(streamField.stream, {
13724
- onChunk: () => {
13725
- if (firstChunkTime === void 0) {
14495
+ onChunk: (chunk) => {
14496
+ if (firstChunkTime === void 0 && isAISDKContentAsyncIterableChunk(chunk)) {
13726
14497
  firstChunkTime = getCurrentUnixTimestamp();
13727
14498
  }
13728
14499
  },
@@ -14694,15 +15465,17 @@ function collectLocalMcpServerToolHookNames(serverName, serverConfig) {
14694
15465
 
14695
15466
  // src/instrumentation/plugins/claude-agent-sdk-plugin.ts
14696
15467
  var ROOT_LLM_PARENT_KEY = "__root__";
15468
+ var SUB_AGENT_PROMPT_SOURCE_PRIORITY = {
15469
+ delegation: 0,
15470
+ lifecycle: 1,
15471
+ sidechain: 2
15472
+ };
14697
15473
  function llmParentKey(parentToolUseId) {
14698
15474
  return parentToolUseId ?? ROOT_LLM_PARENT_KEY;
14699
15475
  }
14700
15476
  function isSubAgentDelegationToolName(toolName) {
14701
15477
  return toolName === "Agent" || toolName === "Task";
14702
15478
  }
14703
- function shouldParentToolAsTaskSibling(toolName) {
14704
- return toolName === "Agent" || toolName === "Task" || toolName === "Bash";
14705
- }
14706
15479
  function filterSerializableOptions(options) {
14707
15480
  const allowedKeys = [
14708
15481
  "model",
@@ -14839,26 +15612,39 @@ function extractUsageFromMessage(message) {
14839
15612
  }
14840
15613
  return metrics;
14841
15614
  }
14842
- function buildLLMInput(prompt, conversationHistory, capturedPromptMessages) {
14843
- const promptMessages = [];
14844
- if (typeof prompt === "string") {
14845
- promptMessages.push({ content: prompt, role: "user" });
14846
- } else if (capturedPromptMessages && capturedPromptMessages.length > 0) {
14847
- for (const msg of capturedPromptMessages) {
14848
- const role = msg.message?.role;
14849
- const content = msg.message?.content;
14850
- if (role && content !== void 0) {
14851
- promptMessages.push({ content, role });
14852
- }
14853
- }
14854
- }
15615
+ function buildLLMInput(promptMessages, conversationHistory) {
14855
15616
  const inputParts = [...promptMessages, ...conversationHistory];
14856
15617
  return inputParts.length > 0 ? inputParts : void 0;
14857
15618
  }
15619
+ function conversationMessageFromSDKMessage(message) {
15620
+ const role = message.message?.role;
15621
+ const content = message.message?.content;
15622
+ if (role && content !== void 0) {
15623
+ return { content, role };
15624
+ }
15625
+ return void 0;
15626
+ }
15627
+ function messageContentHasBlockType(message, blockType) {
15628
+ const content = message.message?.content;
15629
+ return Array.isArray(content) && content.some(
15630
+ (block) => typeof block === "object" && block !== null && "type" in block && block.type === blockType
15631
+ );
15632
+ }
15633
+ function buildRootPromptMessages(prompt, capturedPromptMessages) {
15634
+ if (typeof prompt === "string") {
15635
+ return [{ content: prompt, role: "user" }];
15636
+ }
15637
+ if (!capturedPromptMessages || capturedPromptMessages.length === 0) {
15638
+ return [];
15639
+ }
15640
+ return capturedPromptMessages.map(conversationMessageFromSDKMessage).filter(
15641
+ (message) => message !== void 0
15642
+ );
15643
+ }
14858
15644
  function formatCapturedMessages(messages) {
14859
15645
  return messages.length > 0 ? messages : [];
14860
15646
  }
14861
- async function createLLMSpanForMessages(messages, prompt, conversationHistory, options, startTime, capturedPromptMessages, parentSpan, existingSpan) {
15647
+ async function createLLMSpanForMessages(messages, promptMessages, conversationHistory, options, startTime, parentSpan, existingSpan) {
14862
15648
  if (messages.length === 0) {
14863
15649
  return void 0;
14864
15650
  }
@@ -14868,11 +15654,7 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
14868
15654
  }
14869
15655
  const model = lastMessage.message.model || options.model;
14870
15656
  const usage = extractUsageFromMessage(lastMessage);
14871
- const input = buildLLMInput(
14872
- prompt,
14873
- conversationHistory,
14874
- capturedPromptMessages
14875
- );
15657
+ const input = buildLLMInput(promptMessages, conversationHistory);
14876
15658
  const outputs = messages.map(
14877
15659
  (m) => m.message?.content && m.message?.role ? { content: m.message.content, role: m.message.role } : void 0
14878
15660
  ).filter(
@@ -15003,8 +15785,7 @@ function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers,
15003
15785
  },
15004
15786
  name: parsed.displayName,
15005
15787
  parent: await resolveParentSpan(toolUseID, {
15006
- agentId: input.agent_id,
15007
- preferTaskSiblingParent: shouldParentToolAsTaskSibling(parsed.toolName)
15788
+ agentId: input.agent_id
15008
15789
  }),
15009
15790
  spanAttributes: { type: "tool" /* TOOL */ }
15010
15791
  });
@@ -15250,12 +16031,37 @@ function injectTracingHooks(options, resolveParentSpan, activeToolSpans, localTo
15250
16031
  }
15251
16032
  };
15252
16033
  }
16034
+ function setSubAgentPromptMessages(state, parentToolUseId, promptMessages, source) {
16035
+ if (promptMessages.length === 0) {
16036
+ return;
16037
+ }
16038
+ const parentKey = llmParentKey(parentToolUseId);
16039
+ const sourcePriority = SUB_AGENT_PROMPT_SOURCE_PRIORITY[source];
16040
+ const currentPriority = state.promptSourcePriorityByParentKey.get(parentKey) ?? -1;
16041
+ if (sourcePriority > currentPriority) {
16042
+ state.promptMessagesByParentKey.set(parentKey, promptMessages);
16043
+ state.promptSourcePriorityByParentKey.set(parentKey, sourcePriority);
16044
+ }
16045
+ }
16046
+ function getConversationHistory(state, parentKey) {
16047
+ let conversationHistory = state.conversationHistoryByParentKey.get(parentKey);
16048
+ if (!conversationHistory) {
16049
+ conversationHistory = [];
16050
+ state.conversationHistoryByParentKey.set(parentKey, conversationHistory);
16051
+ }
16052
+ return conversationHistory;
16053
+ }
15253
16054
  async function finalizeCurrentMessageGroup(state) {
15254
16055
  if (state.currentMessages.length === 0) {
15255
16056
  return;
15256
16057
  }
15257
16058
  const parentToolUseId = state.currentMessages[0]?.parent_tool_use_id ?? null;
15258
16059
  const parentKey = llmParentKey(parentToolUseId);
16060
+ const conversationHistory = getConversationHistory(state, parentKey);
16061
+ const promptMessages = parentToolUseId ? state.promptMessagesByParentKey.get(parentKey) ?? [] : buildRootPromptMessages(
16062
+ state.originalPrompt,
16063
+ state.capturedPromptMessages
16064
+ );
15259
16065
  let parentSpan = await state.span.export();
15260
16066
  if (parentToolUseId) {
15261
16067
  const subAgentSpan = state.subAgentSpans.get(parentToolUseId);
@@ -15266,11 +16072,10 @@ async function finalizeCurrentMessageGroup(state) {
15266
16072
  const existingLlmSpan = state.activeLlmSpansByParentToolUse.get(parentKey);
15267
16073
  const llmSpanResult = await createLLMSpanForMessages(
15268
16074
  state.currentMessages,
15269
- state.originalPrompt,
15270
- state.finalResults,
16075
+ promptMessages,
16076
+ conversationHistory,
15271
16077
  state.options,
15272
16078
  state.currentMessageStartTime,
15273
- state.capturedPromptMessages,
15274
16079
  parentSpan,
15275
16080
  existingLlmSpan
15276
16081
  );
@@ -15284,6 +16089,7 @@ async function finalizeCurrentMessageGroup(state) {
15284
16089
  state.latestRootLlmParentRef.value = llmSpanResult.spanExport;
15285
16090
  }
15286
16091
  if (llmSpanResult.finalMessage) {
16092
+ conversationHistory.push(llmSpanResult.finalMessage);
15287
16093
  state.finalResults.push(llmSpanResult.finalMessage);
15288
16094
  }
15289
16095
  }
@@ -15311,6 +16117,15 @@ function maybeTrackToolUseContext(state, message) {
15311
16117
  description: getStringProperty(block.input, "description"),
15312
16118
  toolUseId: block.id
15313
16119
  });
16120
+ const prompt = getStringProperty(block.input, "prompt");
16121
+ if (prompt) {
16122
+ setSubAgentPromptMessages(
16123
+ state,
16124
+ block.id,
16125
+ [{ content: prompt, role: "user" }],
16126
+ "delegation"
16127
+ );
16128
+ }
15314
16129
  }
15315
16130
  }
15316
16131
  }
@@ -15429,6 +16244,14 @@ async function maybeHandleTaskLifecycleMessage(state, message) {
15429
16244
  };
15430
16245
  if (message.subtype === "task_started") {
15431
16246
  const prompt = getStringProperty(message, "prompt");
16247
+ if (prompt) {
16248
+ setSubAgentPromptMessages(
16249
+ state,
16250
+ toolUseId,
16251
+ [{ content: prompt, role: "user" }],
16252
+ "lifecycle"
16253
+ );
16254
+ }
15432
16255
  subAgentSpan.log({
15433
16256
  input: prompt,
15434
16257
  metadata
@@ -15479,6 +16302,28 @@ async function handleStreamMessage(state, message) {
15479
16302
  return;
15480
16303
  }
15481
16304
  await maybeStartSubAgentSpan(state, message);
16305
+ const messageParentToolUseId = message.parent_tool_use_id;
16306
+ if (messageParentToolUseId && message.type === "user") {
16307
+ const conversationMessage = conversationMessageFromSDKMessage(message);
16308
+ if (conversationMessage?.role === "user") {
16309
+ await finalizeCurrentMessageGroup(state);
16310
+ const parentKey = llmParentKey(messageParentToolUseId);
16311
+ const conversationHistory = getConversationHistory(state, parentKey);
16312
+ const currentPromptSourcePriority = state.promptSourcePriorityByParentKey.get(parentKey) ?? -1;
16313
+ const canUseAsInitialSidechainPrompt = conversationHistory.length === 0 && currentPromptSourcePriority < SUB_AGENT_PROMPT_SOURCE_PRIORITY.sidechain && !messageContentHasBlockType(message, "tool_result");
16314
+ if (!canUseAsInitialSidechainPrompt) {
16315
+ conversationHistory.push(conversationMessage);
16316
+ return;
16317
+ }
16318
+ setSubAgentPromptMessages(
16319
+ state,
16320
+ messageParentToolUseId,
16321
+ [conversationMessage],
16322
+ "sidechain"
16323
+ );
16324
+ return;
16325
+ }
16326
+ }
15482
16327
  const messageId = message.message?.id;
15483
16328
  if (messageId && messageId !== state.currentMessageId) {
15484
16329
  await finalizeCurrentMessageGroup(state);
@@ -15630,6 +16475,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
15630
16475
  }
15631
16476
  const activeToolSpans = /* @__PURE__ */ new Map();
15632
16477
  const activeLlmSpansByParentToolUse = /* @__PURE__ */ new Map();
16478
+ const conversationHistoryByParentKey = /* @__PURE__ */ new Map();
15633
16479
  const subAgentSpans = /* @__PURE__ */ new Map();
15634
16480
  const endedSubAgentSpans = /* @__PURE__ */ new Set();
15635
16481
  const toolUseToParent = /* @__PURE__ */ new Map();
@@ -15639,6 +16485,8 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
15639
16485
  };
15640
16486
  const subAgentDetailsByToolUseId = /* @__PURE__ */ new Map();
15641
16487
  const taskIdToToolUseId = /* @__PURE__ */ new Map();
16488
+ const promptMessagesByParentKey = /* @__PURE__ */ new Map();
16489
+ const promptSourcePriorityByParentKey = /* @__PURE__ */ new Map();
15642
16490
  const localToolContext = createClaudeLocalToolContext();
15643
16491
  const { hasLocalToolHandlers, localToolHookNames } = prepareLocalToolHandlersInMcpServers(options.mcpServers);
15644
16492
  const skipLocalToolHooks = options[CLAUDE_AGENT_SDK_SKIP_LOCAL_TOOL_HOOKS_OPTION] === true || hasLocalToolHandlers;
@@ -15647,38 +16495,19 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
15647
16495
  const parentToolUseId = trackedParentToolUseId ?? (context?.agentId ? taskIdToToolUseId.get(context.agentId) ?? null : null);
15648
16496
  const parentKey = llmParentKey(parentToolUseId);
15649
16497
  const activeLlmSpan = activeLlmSpansByParentToolUse.get(parentKey);
15650
- if (context?.preferTaskSiblingParent) {
15651
- if (!activeLlmSpan) {
15652
- await ensureActiveLlmSpanForParentToolUse(
15653
- span,
15654
- activeLlmSpansByParentToolUse,
15655
- subAgentDetailsByToolUseId,
15656
- activeToolSpans,
15657
- subAgentSpans,
15658
- parentToolUseId,
15659
- getCurrentUnixTimestamp()
15660
- );
15661
- }
15662
- if (parentToolUseId) {
15663
- const subAgentSpan = await ensureSubAgentSpan(
15664
- subAgentDetailsByToolUseId,
15665
- span,
15666
- activeToolSpans,
15667
- subAgentSpans,
15668
- parentToolUseId
15669
- );
15670
- return subAgentSpan.export();
15671
- }
15672
- return span.export();
15673
- }
15674
- if (activeLlmSpan) {
15675
- return activeLlmSpan.export();
16498
+ const latestLlmParent = parentToolUseId ? latestLlmParentBySubAgentToolUse.get(parentToolUseId) : latestRootLlmParentRef.value;
16499
+ if (!activeLlmSpan && !latestLlmParent) {
16500
+ await ensureActiveLlmSpanForParentToolUse(
16501
+ span,
16502
+ activeLlmSpansByParentToolUse,
16503
+ subAgentDetailsByToolUseId,
16504
+ activeToolSpans,
16505
+ subAgentSpans,
16506
+ parentToolUseId,
16507
+ getCurrentUnixTimestamp()
16508
+ );
15676
16509
  }
15677
16510
  if (parentToolUseId) {
15678
- const parentLlm = latestLlmParentBySubAgentToolUse.get(parentToolUseId);
15679
- if (parentLlm) {
15680
- return parentLlm;
15681
- }
15682
16511
  const subAgentSpan = await ensureSubAgentSpan(
15683
16512
  subAgentDetailsByToolUseId,
15684
16513
  span,
@@ -15688,9 +16517,6 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
15688
16517
  );
15689
16518
  return subAgentSpan.export();
15690
16519
  }
15691
- if (latestRootLlmParentRef.value) {
15692
- return latestRootLlmParentRef.value;
15693
- }
15694
16520
  return span.export();
15695
16521
  };
15696
16522
  localToolContext.resolveLocalToolParent = resolveToolUseParentSpan;
@@ -15711,6 +16537,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
15711
16537
  accumulatedOutputTokens: 0,
15712
16538
  activeLlmSpansByParentToolUse,
15713
16539
  activeToolSpans,
16540
+ conversationHistoryByParentKey,
15714
16541
  capturedPromptMessages,
15715
16542
  currentMessageId: void 0,
15716
16543
  currentMessageStartTime: startTime,
@@ -15721,7 +16548,9 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
15721
16548
  originalPrompt,
15722
16549
  processing: Promise.resolve(),
15723
16550
  promptDone,
16551
+ promptMessagesByParentKey,
15724
16552
  promptStarted: () => promptStarted,
16553
+ promptSourcePriorityByParentKey,
15725
16554
  span,
15726
16555
  subAgentDetailsByToolUseId,
15727
16556
  subAgentSpans,
@@ -17150,6 +17979,10 @@ var googleGenAIChannels = defineChannels("@google/genai", {
17150
17979
  embedContent: channel({
17151
17980
  channelName: "models.embedContent",
17152
17981
  kind: "async"
17982
+ }),
17983
+ interactionsCreate: channel({
17984
+ channelName: "interactions.create",
17985
+ kind: "async"
17153
17986
  })
17154
17987
  });
17155
17988
 
@@ -17177,6 +18010,7 @@ var GoogleGenAIPlugin = class extends BasePlugin {
17177
18010
  this.subscribeToGenerateContentChannel();
17178
18011
  this.subscribeToGenerateContentStreamChannel();
17179
18012
  this.subscribeToEmbedContentChannel();
18013
+ this.subscribeToInteractionsCreateChannel();
17180
18014
  }
17181
18015
  subscribeToGenerateContentChannel() {
17182
18016
  const tracingChannel2 = googleGenAIChannels.generateContent.tracingChannel();
@@ -17349,7 +18183,30 @@ var GoogleGenAIPlugin = class extends BasePlugin {
17349
18183
  tracingChannel2.unsubscribe(handlers);
17350
18184
  });
17351
18185
  }
18186
+ subscribeToInteractionsCreateChannel() {
18187
+ this.unsubscribers.push(
18188
+ traceStreamingChannel(
18189
+ googleGenAIChannels.interactionsCreate,
18190
+ {
18191
+ name: "create_interaction",
18192
+ shouldTrace: ([params]) => !isBackgroundInteractionCreate(params),
18193
+ type: "llm" /* LLM */,
18194
+ extractInput: ([params]) => ({
18195
+ input: serializeInteractionInput(params),
18196
+ metadata: extractInteractionMetadata(params)
18197
+ }),
18198
+ extractOutput: (result) => serializeInteractionValue(result),
18199
+ extractMetadata: (result) => extractInteractionResponseMetadata(result),
18200
+ extractMetrics: (result, startTime) => cleanMetrics3(extractInteractionMetrics(result, startTime)),
18201
+ aggregateChunks: (chunks, _result, _event, startTime) => aggregateInteractionEvents(chunks, startTime)
18202
+ }
18203
+ )
18204
+ );
18205
+ }
17352
18206
  };
18207
+ function isBackgroundInteractionCreate(params) {
18208
+ return tryToDict(params)?.background === true;
18209
+ }
17353
18210
  function ensureSpanState(states, event, create) {
17354
18211
  const existing = states.get(event);
17355
18212
  if (existing) {
@@ -17557,6 +18414,60 @@ function serializeEmbedContentInput(params) {
17557
18414
  }
17558
18415
  return input;
17559
18416
  }
18417
+ function serializeInteractionInput(params) {
18418
+ const input = {
18419
+ input: serializeInteractionValue(params.input)
18420
+ };
18421
+ for (const key of [
18422
+ "model",
18423
+ "agent",
18424
+ "agent_config",
18425
+ "api_version",
18426
+ "background",
18427
+ "environment",
18428
+ "generation_config",
18429
+ "previous_interaction_id",
18430
+ "response_format",
18431
+ "response_mime_type",
18432
+ "response_modalities",
18433
+ "service_tier",
18434
+ "store",
18435
+ "stream",
18436
+ "system_instruction",
18437
+ "webhook_config"
18438
+ ]) {
18439
+ const value = params[key];
18440
+ if (value !== void 0) {
18441
+ input[key] = serializeInteractionValue(value);
18442
+ }
18443
+ }
18444
+ return input;
18445
+ }
18446
+ function extractInteractionMetadata(params) {
18447
+ const metadata = {};
18448
+ for (const key of [
18449
+ "model",
18450
+ "agent",
18451
+ "agent_config",
18452
+ "generation_config",
18453
+ "system_instruction",
18454
+ "response_format",
18455
+ "response_mime_type",
18456
+ "response_modalities",
18457
+ "service_tier"
18458
+ ]) {
18459
+ const value = params[key];
18460
+ if (value !== void 0) {
18461
+ metadata[key] = serializeInteractionValue(value);
18462
+ }
18463
+ }
18464
+ if (Array.isArray(params.tools)) {
18465
+ metadata.tools = params.tools.map(
18466
+ (tool) => serializeInteractionValue(tool)
18467
+ );
18468
+ }
18469
+ return metadata;
18470
+ }
17560
18471
  function serializeContentCollection(contents) {
17561
18472
  if (contents === null || contents === void 0) {
17562
18473
  return null;
@@ -17587,21 +18498,8 @@ function serializePart(part) {
17587
18498
  }
17588
18499
  if (part.inlineData && part.inlineData.data) {
17589
18500
  const { data, mimeType } = part.inlineData;
17590
- if (data instanceof Uint8Array || typeof Buffer !== "undefined" && Buffer.isBuffer(data) || typeof data === "string") {
17591
- const extension = mimeType ? mimeType.split("/")[1] : "bin";
17592
- const filename = `file.${extension}`;
17593
- const buffer = typeof data === "string" ? typeof Buffer !== "undefined" ? Buffer.from(data, "base64") : new Uint8Array(
17594
- atob(data).split("").map((c) => c.charCodeAt(0))
17595
- ) : typeof Buffer !== "undefined" ? Buffer.from(data) : new Uint8Array(data);
17596
- const arrayBuffer = buffer instanceof Uint8Array ? buffer.buffer.slice(
17597
- buffer.byteOffset,
17598
- buffer.byteOffset + buffer.byteLength
17599
- ) : buffer;
17600
- const attachment = new Attachment({
17601
- data: arrayBuffer,
17602
- filename,
17603
- contentType: mimeType || "application/octet-stream"
17604
- });
18501
+ const attachment = createAttachmentFromInlineData(data, mimeType);
18502
+ if (attachment) {
17605
18503
  return {
17606
18504
  image_url: { url: attachment }
17607
18505
  };
@@ -17609,6 +18507,59 @@ function serializePart(part) {
17609
18507
  }
17610
18508
  return part;
17611
18509
  }
18510
+ function serializeInteractionValue(value, seen = /* @__PURE__ */ new WeakSet()) {
18511
+ if (value === null || value === void 0 || typeof value !== "object") {
18512
+ return value;
18513
+ }
18514
+ if (Array.isArray(value)) {
18515
+ return value.map((item) => serializeInteractionValue(item, seen));
18516
+ }
18517
+ const dict = tryToDict(value);
18518
+ if (dict === null || dict === void 0 || typeof dict !== "object") {
18519
+ return dict;
18520
+ }
18521
+ if (Array.isArray(dict)) {
18522
+ return dict.map((item) => serializeInteractionValue(item, seen));
18523
+ }
18524
+ if (seen.has(dict)) {
18525
+ return "[Circular]";
18526
+ }
18527
+ seen.add(dict);
18528
+ try {
18529
+ const serialized = {};
18530
+ 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;
18531
+ const attachment = mimeType && "data" in dict && dict.data !== void 0 ? createAttachmentFromInlineData(dict.data, mimeType) : null;
18532
+ for (const [key, entry] of Object.entries(dict)) {
18533
+ if (key === "data" && attachment) {
18534
+ serialized[key] = attachment;
18535
+ } else {
18536
+ serialized[key] = serializeInteractionValue(entry, seen);
18537
+ }
18538
+ }
18539
+ return serialized;
18540
+ } finally {
18541
+ seen.delete(dict);
18542
+ }
18543
+ }
18544
+ function createAttachmentFromInlineData(data, mimeType) {
18545
+ if (!(data instanceof Uint8Array || typeof Buffer !== "undefined" && Buffer.isBuffer(data) || typeof data === "string")) {
18546
+ return null;
18547
+ }
18548
+ const extension = mimeType ? mimeType.split("/")[1] : "bin";
18549
+ const filename = `file.${extension}`;
18550
+ const buffer = typeof data === "string" ? typeof Buffer !== "undefined" ? Buffer.from(data, "base64") : new Uint8Array(
18551
+ atob(data).split("").map((c) => c.charCodeAt(0))
18552
+ ) : typeof Buffer !== "undefined" ? Buffer.from(data) : new Uint8Array(data);
18553
+ const arrayBuffer = buffer instanceof Uint8Array ? buffer.buffer.slice(
18554
+ buffer.byteOffset,
18555
+ buffer.byteOffset + buffer.byteLength
18556
+ ) : buffer;
18557
+ return new Attachment({
18558
+ data: arrayBuffer,
18559
+ filename,
18560
+ contentType: mimeType || "application/octet-stream"
18561
+ });
18562
+ }
17612
18563
  function serializeGenerateContentTools(params) {
17613
18564
  const config = params.config ? tryToDict(params.config) : null;
17614
18565
  const tools = config?.tools;
@@ -17693,6 +18644,33 @@ function extractEmbedContentMetrics(response, startTime) {
17693
18644
  }
17694
18645
  return metrics;
17695
18646
  }
18647
+ function extractInteractionMetrics(response, startTime) {
18648
+ const metrics = {};
18649
+ if (startTime !== void 0) {
18650
+ const end = getCurrentUnixTimestamp();
18651
+ metrics.start = startTime;
18652
+ metrics.end = end;
18653
+ metrics.duration = end - startTime;
18654
+ }
18655
+ if (response?.usage) {
18656
+ populateInteractionUsageMetrics(metrics, response.usage);
18657
+ }
18658
+ return metrics;
18659
+ }
18660
+ function extractInteractionResponseMetadata(response) {
18661
+ const responseDict = tryToDict(response);
18662
+ if (!responseDict) {
18663
+ return void 0;
18664
+ }
18665
+ const metadata = {};
18666
+ if (typeof responseDict.id === "string") {
18667
+ metadata.interaction_id = responseDict.id;
18668
+ }
18669
+ if (typeof responseDict.status === "string") {
18670
+ metadata.status = responseDict.status;
18671
+ }
18672
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
18673
+ }
17696
18674
  function extractEmbedPromptTokenCount(response) {
17697
18675
  if (!response) {
17698
18676
  return void 0;
@@ -17755,6 +18733,23 @@ function populateUsageMetrics(metrics, usage) {
17755
18733
  metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
17756
18734
  }
17757
18735
  }
18736
+ function populateInteractionUsageMetrics(metrics, usage) {
18737
+ if (typeof usage.total_input_tokens === "number") {
18738
+ metrics.prompt_tokens = usage.total_input_tokens;
18739
+ }
18740
+ if (typeof usage.total_output_tokens === "number") {
18741
+ metrics.completion_tokens = usage.total_output_tokens;
18742
+ }
18743
+ if (typeof usage.total_tokens === "number") {
18744
+ metrics.tokens = usage.total_tokens;
18745
+ }
18746
+ if (typeof usage.total_cached_tokens === "number") {
18747
+ metrics.prompt_cached_tokens = usage.total_cached_tokens;
18748
+ }
18749
+ if (typeof usage.total_thought_tokens === "number") {
18750
+ metrics.completion_reasoning_tokens = usage.total_thought_tokens;
18751
+ }
18752
+ }
17758
18753
  function aggregateGenerateContentChunks(chunks, startTime, firstTokenTime) {
17759
18754
  const end = getCurrentUnixTimestamp();
17760
18755
  const metrics = {
@@ -17852,6 +18847,146 @@ function aggregateGenerateContentChunks(chunks, startTime, firstTokenTime) {
17852
18847
  }
17853
18848
  return { aggregated, metrics };
17854
18849
  }
18850
+ function aggregateInteractionEvents(chunks, startTime) {
18851
+ const end = getCurrentUnixTimestamp();
18852
+ const metrics = {};
18853
+ if (startTime !== void 0) {
18854
+ metrics.start = startTime;
18855
+ metrics.end = end;
18856
+ metrics.duration = end - startTime;
18857
+ }
18858
+ let latestInteraction;
18859
+ let latestUsage;
18860
+ let status;
18861
+ let outputText = "";
18862
+ const steps = /* @__PURE__ */ new Map();
18863
+ for (const chunk of chunks) {
18864
+ const event = tryToDict(chunk);
18865
+ if (!event) {
18866
+ continue;
18867
+ }
18868
+ const usage = extractInteractionUsageFromEvent(event);
18869
+ if (usage) {
18870
+ latestUsage = usage;
18871
+ }
18872
+ const interaction = tryToDict(event.interaction);
18873
+ if (interaction) {
18874
+ latestInteraction = serializeInteractionValue(interaction);
18875
+ if (typeof interaction.status === "string") {
18876
+ status = interaction.status;
18877
+ }
18878
+ }
18879
+ if (typeof event.status === "string") {
18880
+ status = event.status;
18881
+ }
18882
+ const index = typeof event.index === "number" ? event.index : void 0;
18883
+ if (index === void 0) {
18884
+ continue;
18885
+ }
18886
+ if (event.event_type === "step.start") {
18887
+ const compact = compactInteractionStep(event.step);
18888
+ compact.index = index;
18889
+ steps.set(index, compact);
18890
+ continue;
18891
+ }
18892
+ if (event.event_type === "step.delta") {
18893
+ const step = steps.get(index) ?? { index };
18894
+ const textDelta = applyInteractionDelta(step, event.delta);
18895
+ if (textDelta) {
18896
+ outputText += textDelta;
18897
+ }
18898
+ steps.set(index, step);
18899
+ }
18900
+ }
18901
+ if (latestUsage) {
18902
+ populateInteractionUsageMetrics(metrics, latestUsage);
18903
+ }
18904
+ const output = latestInteraction ? { ...latestInteraction } : {};
18905
+ if (status) {
18906
+ output.status = status;
18907
+ }
18908
+ if (outputText) {
18909
+ output.output_text = outputText;
18910
+ }
18911
+ if (latestUsage) {
18912
+ output.usage = serializeInteractionValue(latestUsage);
18913
+ }
18914
+ const compactSteps = Array.from(steps.values()).sort(
18915
+ (left, right) => Number(left.index ?? 0) - Number(right.index ?? 0)
18916
+ );
18917
+ if (compactSteps.length > 0) {
18918
+ output.steps = compactSteps;
18919
+ }
18920
+ const metadata = {};
18921
+ if (typeof output.id === "string") {
18922
+ metadata.interaction_id = output.id;
18923
+ }
18924
+ if (typeof output.status === "string") {
18925
+ metadata.status = output.status;
18926
+ }
18927
+ return {
18928
+ output,
18929
+ metrics: cleanMetrics3(metrics),
18930
+ ...Object.keys(metadata).length > 0 ? { metadata } : {}
18931
+ };
18932
+ }
18933
+ function extractInteractionUsageFromEvent(event) {
18934
+ const metadata = tryToDict(event.metadata);
18935
+ const metadataUsage = tryToDict(metadata?.usage);
18936
+ if (metadataUsage) {
18937
+ return metadataUsage;
18938
+ }
18939
+ const metadataTotalUsage = tryToDict(metadata?.total_usage);
18940
+ if (metadataTotalUsage) {
18941
+ return metadataTotalUsage;
18942
+ }
18943
+ const interaction = tryToDict(event.interaction);
18944
+ const interactionUsage = tryToDict(interaction?.usage);
18945
+ return interactionUsage ? interactionUsage : void 0;
18946
+ }
18947
+ function compactInteractionStep(step) {
18948
+ const stepDict = tryToDict(step);
18949
+ if (!stepDict) {
18950
+ return {};
18951
+ }
18952
+ const compact = {};
18953
+ for (const key of [
18954
+ "type",
18955
+ "content",
18956
+ "name",
18957
+ "server_name",
18958
+ "arguments",
18959
+ "result",
18960
+ "is_error"
18961
+ ]) {
18962
+ if (stepDict[key] !== void 0) {
18963
+ compact[key] = serializeInteractionValue(stepDict[key]);
18964
+ }
18965
+ }
18966
+ return Object.keys(compact).length > 0 ? compact : serializeInteractionValue(stepDict);
18967
+ }
18968
+ function applyInteractionDelta(step, delta) {
18969
+ const deltaDict = tryToDict(delta);
18970
+ if (!deltaDict) {
18971
+ return void 0;
18972
+ }
18973
+ const deltaType = deltaDict.type;
18974
+ if (typeof deltaType === "string" && typeof step.type !== "string") {
18975
+ step.type = deltaType === "text" ? "model_output" : deltaType;
18976
+ }
18977
+ if (deltaType === "text" && typeof deltaDict.text === "string") {
18978
+ step.text = `${typeof step.text === "string" ? step.text : ""}${deltaDict.text}`;
18979
+ return deltaDict.text;
18980
+ }
18981
+ if (deltaType === "arguments_delta" && typeof deltaDict.arguments === "string") {
18982
+ step.arguments = `${typeof step.arguments === "string" ? step.arguments : ""}${deltaDict.arguments}`;
18983
+ return void 0;
18984
+ }
18985
+ const deltas = Array.isArray(step.deltas) ? step.deltas : [];
18986
+ deltas.push(serializeInteractionValue(deltaDict));
18987
+ step.deltas = deltas;
18988
+ return void 0;
18989
+ }
17855
18990
  function cleanMetrics3(metrics) {
17856
18991
  const cleaned = {};
17857
18992
  for (const [key, value] of Object.entries(metrics)) {
@@ -23678,7 +24813,8 @@ var FlueObserveBridge = class {
23678
24813
  metadata
23679
24814
  }
23680
24815
  });
23681
- this.runsById.set(event.runId, { metadata, span });
24816
+ const activeContext = enterCurrentFlueSpan(span);
24817
+ this.runsById.set(event.runId, { activeContext, metadata, span });
23682
24818
  }
23683
24819
  handleRunEnd(event) {
23684
24820
  const state = this.runsById.get(event.runId);
@@ -23696,6 +24832,7 @@ var FlueObserveBridge = class {
23696
24832
  });
23697
24833
  safeEnd(state.span, eventTime(event.timestamp));
23698
24834
  this.runsById.delete(event.runId);
24835
+ restoreCurrentFlueSpan(state.activeContext);
23699
24836
  }
23700
24837
  void flush().catch((error) => {
23701
24838
  logInstrumentationError3("Flue flush", error);
@@ -23822,7 +24959,8 @@ var FlueObserveBridge = class {
23822
24959
  metadata
23823
24960
  }
23824
24961
  });
23825
- this.toolsByKey.set(toolKey(event), { metadata, span });
24962
+ const activeContext = enterCurrentFlueSpan(span);
24963
+ this.toolsByKey.set(toolKey(event), { activeContext, metadata, span });
23826
24964
  }
23827
24965
  handleToolCall(event) {
23828
24966
  if (!event.toolCallId) {
@@ -23845,6 +24983,7 @@ var FlueObserveBridge = class {
23845
24983
  });
23846
24984
  safeEnd(state.span, eventTime(event.timestamp));
23847
24985
  this.toolsByKey.delete(key);
24986
+ restoreCurrentFlueSpan(state.activeContext);
23848
24987
  }
23849
24988
  handleTaskStart(event) {
23850
24989
  if (!event.taskId) {
@@ -24249,6 +25388,34 @@ function stateMatchesRun(state, runId) {
24249
25388
  function startFlueSpan(parent, args) {
24250
25389
  return parent ? withCurrent(parent, () => startSpan(args)) : startSpan(args);
24251
25390
  }
25391
+ function enterCurrentFlueSpan(span) {
25392
+ const contextManager = _internalGetGlobalState()?.contextManager;
25393
+ const store = contextManager ? Reflect.get(contextManager, BRAINTRUST_CURRENT_SPAN_STORE) : void 0;
25394
+ if (!contextManager || !isCurrentSpanStore(store)) {
25395
+ return void 0;
25396
+ }
25397
+ const previous = store.getStore();
25398
+ try {
25399
+ store.enterWith(contextManager.wrapSpanForStore(span));
25400
+ return { previous, store };
25401
+ } catch (error) {
25402
+ logInstrumentationError3("Flue context propagation", error);
25403
+ return void 0;
25404
+ }
25405
+ }
25406
+ function isCurrentSpanStore(value) {
25407
+ return isObjectLike(value) && typeof Reflect.get(value, "enterWith") === "function" && typeof Reflect.get(value, "getStore") === "function";
25408
+ }
25409
+ function restoreCurrentFlueSpan(activeContext) {
25410
+ if (!activeContext) {
25411
+ return;
25412
+ }
25413
+ try {
25414
+ activeContext.store.enterWith(activeContext.previous);
25415
+ } catch (error) {
25416
+ logInstrumentationError3("Flue context restoration", error);
25417
+ }
25418
+ }
24252
25419
  function safeLog3(span, event) {
24253
25420
  try {
24254
25421
  span.log(event);
@@ -24726,6 +25893,890 @@ function isBraintrustHandler(handler) {
24726
25893
  return Reflect.get(handler, "name") === BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME;
24727
25894
  }
24728
25895
 
25896
+ // src/instrumentation/plugins/pi-coding-agent-channels.ts
25897
+ var piCodingAgentChannels = defineChannels(
25898
+ "@earendil-works/pi-coding-agent",
25899
+ {
25900
+ prompt: channel({
25901
+ channelName: "AgentSession.prompt",
25902
+ kind: "async"
25903
+ })
25904
+ }
25905
+ );
25906
+
25907
+ // src/instrumentation/plugins/pi-coding-agent-plugin.ts
25908
+ var piStreamPatchStates = /* @__PURE__ */ new WeakMap();
25909
+ var piPromptContextStore;
25910
+ var PiCodingAgentPlugin = class extends BasePlugin {
25911
+ activePromptStates = /* @__PURE__ */ new Set();
25912
+ onEnable() {
25913
+ this.subscribeToPrompt();
25914
+ }
25915
+ onDisable() {
25916
+ for (const unsubscribe of this.unsubscribers) {
25917
+ unsubscribe();
25918
+ }
25919
+ this.unsubscribers = [];
25920
+ for (const state of [...this.activePromptStates]) {
25921
+ void finalizePiPromptRun(state).catch((error) => {
25922
+ logInstrumentationError4("Pi Coding Agent disable cleanup", error);
25923
+ });
25924
+ }
25925
+ }
25926
+ subscribeToPrompt() {
25927
+ const channel2 = piCodingAgentChannels.prompt.tracingChannel();
25928
+ const states = /* @__PURE__ */ new WeakMap();
25929
+ const unbindAutoInstrumentationSuppression = bindAutoInstrumentationSuppressionToStart(channel2);
25930
+ const handlers = {
25931
+ start: (event) => {
25932
+ const state = startPiPromptRun(event, (state2) => {
25933
+ this.activePromptStates.delete(state2);
25934
+ });
25935
+ if (state) {
25936
+ this.activePromptStates.add(state);
25937
+ states.set(event, state);
25938
+ }
25939
+ },
25940
+ asyncEnd: async (event) => {
25941
+ const state = states.get(event);
25942
+ if (!state) {
25943
+ return;
25944
+ }
25945
+ states.delete(event);
25946
+ state.promptCallEnded = true;
25947
+ if (!state.finalized && state.deferCompletionUntilTurnEnd && !state.turnEnded) {
25948
+ if (!state.sawStreamFn) {
25949
+ state.queued = true;
25950
+ if (!state.streamPatchState.queuedPromptStates.includes(state)) {
25951
+ state.streamPatchState.queuedPromptStates.push(state);
25952
+ }
25953
+ }
25954
+ return;
25955
+ }
25956
+ await finalizePiPromptRun(state);
25957
+ },
25958
+ error: async (event) => {
25959
+ const state = states.get(event);
25960
+ if (!state) {
25961
+ return;
25962
+ }
25963
+ states.delete(event);
25964
+ await finalizePiPromptRun(state, event.error);
25965
+ }
25966
+ };
25967
+ channel2.subscribe(handlers);
25968
+ this.unsubscribers.push(() => {
25969
+ unbindAutoInstrumentationSuppression?.();
25970
+ channel2.unsubscribe(handlers);
25971
+ });
25972
+ }
25973
+ };
25974
+ function startPiPromptRun(event, onFinalize) {
25975
+ const session = extractSession(event);
25976
+ const agent = session?.agent;
25977
+ if (!session || !isPiAgent(agent)) {
25978
+ return void 0;
25979
+ }
25980
+ const metadata = {
25981
+ ...extractSessionMetadata(session),
25982
+ ...extractPromptOptionsMetadata(event.arguments[1]),
25983
+ "pi_coding_agent.operation": "AgentSession.prompt",
25984
+ provider: session.model?.provider ?? agent.state?.model?.provider ?? "pi",
25985
+ ...session.model?.id || agent.state?.model?.id ? { model: session.model?.id ?? agent.state?.model?.id } : {},
25986
+ ...event.moduleVersion ? { "pi_coding_agent.version": event.moduleVersion } : {}
25987
+ };
25988
+ const span = startSpan({
25989
+ event: {
25990
+ input: extractPromptInput(event.arguments[0], event.arguments[1]),
25991
+ metadata
25992
+ },
25993
+ name: "AgentSession.prompt",
25994
+ spanAttributes: { type: "task" /* TASK */ }
25995
+ });
25996
+ const streamPatchState = installPiStreamPatch(agent);
25997
+ const options = event.arguments[1];
25998
+ const promptText = event.arguments[0];
25999
+ const state = {
26000
+ activeLlmSpans: /* @__PURE__ */ new Set(),
26001
+ activeToolSpans: /* @__PURE__ */ new Map(),
26002
+ agent,
26003
+ collectedLlmUsageMetrics: false,
26004
+ deferCompletionUntilTurnEnd: options?.streamingBehavior === "followUp" || options?.streamingBehavior === "steer",
26005
+ finalized: false,
26006
+ metadata,
26007
+ metrics: {},
26008
+ onFinalize,
26009
+ promptCallEnded: false,
26010
+ ...typeof promptText === "string" ? { promptText } : {},
26011
+ queued: false,
26012
+ span,
26013
+ sawStreamFn: false,
26014
+ startTime: getCurrentUnixTimestamp(),
26015
+ streamPatchState,
26016
+ turnEnded: false
26017
+ };
26018
+ state.restorePromptContext = enterPiPromptContext(state);
26019
+ streamPatchState.activePromptStates.add(state);
26020
+ try {
26021
+ state.unsubscribeAgent = agent.subscribe(async (agentEvent) => {
26022
+ try {
26023
+ await runWithAutoInstrumentationSuppressed(
26024
+ () => handlePiAgentEvent(state, agentEvent)
26025
+ );
26026
+ } catch (error) {
26027
+ logInstrumentationError4("Pi Coding Agent event", error);
26028
+ }
26029
+ });
26030
+ } catch (error) {
26031
+ logInstrumentationError4("Pi Coding Agent event subscription", error);
26032
+ }
26033
+ return state;
26034
+ }
26035
+ function extractSession(event) {
26036
+ const candidate = event.session ?? event.self;
26037
+ return isObject(candidate) && typeof candidate.prompt === "function" ? candidate : void 0;
26038
+ }
26039
+ function isPiAgent(value) {
26040
+ return isObject(value) && typeof value.streamFn === "function" && typeof value.subscribe === "function";
26041
+ }
26042
+ function promptContextStore() {
26043
+ piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
26044
+ return piPromptContextStore;
26045
+ }
26046
+ function currentPromptContextFrames() {
26047
+ return promptContextStore().getStore()?.frames ?? [];
26048
+ }
26049
+ function currentPiPromptState() {
26050
+ const frames = currentPromptContextFrames();
26051
+ return frames[frames.length - 1]?.state;
26052
+ }
26053
+ function enterPiPromptContext(state) {
26054
+ const frame = {
26055
+ id: /* @__PURE__ */ Symbol("braintrust.pi-coding-agent.prompt"),
26056
+ state
26057
+ };
26058
+ promptContextStore().enterWith({
26059
+ frames: [...currentPromptContextFrames(), frame]
26060
+ });
26061
+ return () => {
26062
+ const frames = currentPromptContextFrames().filter(
26063
+ (candidate) => candidate.id !== frame.id
26064
+ );
26065
+ promptContextStore().enterWith(frames.length > 0 ? { frames } : void 0);
26066
+ };
26067
+ }
26068
+ function installPiStreamPatch(agent) {
26069
+ const existing = piStreamPatchStates.get(agent);
26070
+ if (existing) {
26071
+ if (agent.streamFn !== existing.wrappedStreamFn) {
26072
+ debugLogger.debug(
26073
+ "Pi Coding Agent streamFn changed while Braintrust instrumentation was active; preserving existing patch state."
26074
+ );
26075
+ }
26076
+ return existing;
26077
+ }
26078
+ const patchState = {
26079
+ activePromptStates: /* @__PURE__ */ new Set(),
26080
+ agent,
26081
+ originalStreamFn: agent.streamFn,
26082
+ queuedPromptStates: [],
26083
+ wrappedStreamFn: agent.streamFn
26084
+ };
26085
+ patchState.wrappedStreamFn = makeSharedInstrumentedStreamFn(patchState);
26086
+ agent.streamFn = patchState.wrappedStreamFn;
26087
+ piStreamPatchStates.set(agent, patchState);
26088
+ return patchState;
26089
+ }
26090
+ function resolveStreamPromptState(patchState, context) {
26091
+ let lastUserText;
26092
+ if (Array.isArray(context.messages)) {
26093
+ for (let i = context.messages.length - 1; i >= 0; i--) {
26094
+ const message = context.messages[i];
26095
+ if (isPiUserMessage(message)) {
26096
+ if (typeof message.content === "string") {
26097
+ lastUserText = message.content;
26098
+ } else {
26099
+ lastUserText = message.content.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
26100
+ }
26101
+ break;
26102
+ }
26103
+ }
26104
+ }
26105
+ if (lastUserText !== void 0) {
26106
+ const queuedMatch = patchState.queuedPromptStates.find(
26107
+ (state) => state.promptText === lastUserText
26108
+ );
26109
+ if (queuedMatch) {
26110
+ return queuedMatch;
26111
+ }
26112
+ const matches = [...patchState.activePromptStates].filter(
26113
+ (state) => state.promptText === lastUserText
26114
+ );
26115
+ if (matches.length === 1) {
26116
+ return matches[0];
26117
+ }
26118
+ }
26119
+ const contextState = currentPiPromptState();
26120
+ if (contextState && patchState.activePromptStates.has(contextState) && (!contextState.queued || lastUserText !== void 0 && contextState.promptText === lastUserText)) {
26121
+ return contextState;
26122
+ }
26123
+ if (patchState.activePromptStates.size === 1) {
26124
+ return [...patchState.activePromptStates][0];
26125
+ }
26126
+ return void 0;
26127
+ }
26128
+ function makeSharedInstrumentedStreamFn(patchState) {
26129
+ return async function instrumentedPiStreamFn(model, context, options) {
26130
+ const state = resolveStreamPromptState(patchState, context);
26131
+ if (!state) {
26132
+ const invokeOriginal = () => Reflect.apply(patchState.originalStreamFn, this, [
26133
+ model,
26134
+ context,
26135
+ options
26136
+ ]);
26137
+ return patchState.activePromptStates.size > 0 ? runWithAutoInstrumentationSuppressed(invokeOriginal) : invokeOriginal();
26138
+ }
26139
+ state.sawStreamFn = true;
26140
+ removeQueuedPromptState(state);
26141
+ state.streamPatchState.eventPromptState = state;
26142
+ const llmState = await startPiLlmSpan(state, model, context, options);
26143
+ try {
26144
+ const stream = await runWithAutoInstrumentationSuppressed(
26145
+ () => Reflect.apply(patchState.originalStreamFn, this, [
26146
+ model,
26147
+ context,
26148
+ options
26149
+ ])
26150
+ );
26151
+ return patchAssistantMessageStream(stream, state, llmState);
26152
+ } catch (error) {
26153
+ finishPiLlmSpan(state, llmState, void 0, error);
26154
+ throw error;
26155
+ }
26156
+ };
26157
+ }
26158
+ async function startPiLlmSpan(state, model, context, options) {
26159
+ const metadata = {
26160
+ ...extractModelMetadata2(model),
26161
+ ...extractStreamOptionsMetadata(options),
26162
+ ...extractToolMetadata(context.tools),
26163
+ "pi_coding_agent.operation": "agent.streamFn"
26164
+ };
26165
+ const span = startSpan({
26166
+ event: {
26167
+ input: processInputAttachments(normalizePiContextInput(context)),
26168
+ metadata
26169
+ },
26170
+ name: getLlmSpanName(model),
26171
+ parent: await state.span.export(),
26172
+ spanAttributes: { type: "llm" /* LLM */ }
26173
+ });
26174
+ const llmState = {
26175
+ finalized: false,
26176
+ metadata,
26177
+ metrics: {},
26178
+ span,
26179
+ startTime: getCurrentUnixTimestamp()
26180
+ };
26181
+ state.activeLlmSpans.add(llmState);
26182
+ return llmState;
26183
+ }
26184
+ function patchAssistantMessageStream(stream, promptState, llmState) {
26185
+ if (!isObject(stream)) {
26186
+ return stream;
26187
+ }
26188
+ const streamRecord = stream;
26189
+ const originalResult = stream.result;
26190
+ if (typeof originalResult === "function") {
26191
+ streamRecord.result = function patchedPiResult() {
26192
+ return Promise.resolve(Reflect.apply(originalResult, this, [])).then(
26193
+ (message) => {
26194
+ finishPiLlmSpan(promptState, llmState, message);
26195
+ return message;
26196
+ },
26197
+ (error) => {
26198
+ finishPiLlmSpan(promptState, llmState, void 0, error);
26199
+ throw error;
26200
+ }
26201
+ );
26202
+ };
26203
+ }
26204
+ const originalIterator = stream[Symbol.asyncIterator];
26205
+ if (typeof originalIterator === "function") {
26206
+ streamRecord[Symbol.asyncIterator] = function patchedPiIterator() {
26207
+ const iterator = Reflect.apply(
26208
+ originalIterator,
26209
+ this,
26210
+ []
26211
+ );
26212
+ return {
26213
+ async next() {
26214
+ try {
26215
+ const result = await iterator.next();
26216
+ if (result.done) {
26217
+ finishPiLlmSpan(promptState, llmState);
26218
+ return result;
26219
+ }
26220
+ recordPiAssistantMessageEvent(promptState, llmState, result.value);
26221
+ return result;
26222
+ } catch (error) {
26223
+ finishPiLlmSpan(promptState, llmState, void 0, error);
26224
+ if (typeof iterator.return === "function") {
26225
+ try {
26226
+ await iterator.return();
26227
+ } catch (cleanupError) {
26228
+ logInstrumentationError4(
26229
+ "Pi Coding Agent stream cleanup",
26230
+ cleanupError
26231
+ );
26232
+ }
26233
+ }
26234
+ throw error;
26235
+ }
26236
+ },
26237
+ async return(value) {
26238
+ try {
26239
+ if (typeof iterator.return === "function") {
26240
+ return await iterator.return(value);
26241
+ }
26242
+ return {
26243
+ done: true,
26244
+ value
26245
+ };
26246
+ } catch (error) {
26247
+ finishPiLlmSpan(promptState, llmState, void 0, error);
26248
+ throw error;
26249
+ } finally {
26250
+ finishPiLlmSpan(promptState, llmState);
26251
+ }
26252
+ },
26253
+ async throw(error) {
26254
+ try {
26255
+ if (typeof iterator.throw === "function") {
26256
+ return await iterator.throw(error);
26257
+ }
26258
+ throw error;
26259
+ } catch (thrownError) {
26260
+ finishPiLlmSpan(promptState, llmState, void 0, thrownError);
26261
+ throw thrownError;
26262
+ }
26263
+ },
26264
+ [Symbol.asyncIterator]() {
26265
+ return this;
26266
+ }
26267
+ };
26268
+ };
26269
+ }
26270
+ return stream;
26271
+ }
26272
+ function recordPiAssistantMessageEvent(promptState, llmState, event) {
26273
+ recordFirstTokenMetric(llmState, event);
26274
+ const message = "message" in event ? event.message : void 0;
26275
+ const errorMessage2 = "error" in event ? event.error : void 0;
26276
+ if (event.type === "done" && isPiAssistantMessage(message)) {
26277
+ finishPiLlmSpan(promptState, llmState, message);
26278
+ } else if (event.type === "error" && isPiAssistantMessage(errorMessage2)) {
26279
+ finishPiLlmSpan(promptState, llmState, errorMessage2);
26280
+ }
26281
+ }
26282
+ function recordFirstTokenMetric(state, event) {
26283
+ if (state.metrics.time_to_first_token !== void 0 || event.type === "start") {
26284
+ return;
26285
+ }
26286
+ state.metrics.time_to_first_token = getCurrentUnixTimestamp() - state.startTime;
26287
+ }
26288
+ async function handlePiAgentEvent(state, event) {
26289
+ if (state.finalized) {
26290
+ return;
26291
+ }
26292
+ const eventPromptState = state.streamPatchState.eventPromptState;
26293
+ if (eventPromptState && eventPromptState !== state) {
26294
+ return;
26295
+ }
26296
+ if (!eventPromptState && (state.queued || state.streamPatchState.activePromptStates.size > 1 && currentPiPromptState() !== state)) {
26297
+ return;
26298
+ }
26299
+ switch (event.type) {
26300
+ case "message_end":
26301
+ if (isPiAssistantMessage(event.message)) {
26302
+ state.output = extractAssistantOutput(event.message);
26303
+ }
26304
+ return;
26305
+ case "turn_end":
26306
+ state.turnEnded = true;
26307
+ if (isPiAssistantMessage(event.message)) {
26308
+ state.output = extractAssistantOutput(event.message);
26309
+ if (!state.collectedLlmUsageMetrics) {
26310
+ addMetrics(state.metrics, extractUsageMetrics2(event.message.usage));
26311
+ }
26312
+ }
26313
+ if (state.streamPatchState.eventPromptState === state) {
26314
+ state.streamPatchState.eventPromptState = void 0;
26315
+ }
26316
+ if (state.promptCallEnded && state.deferCompletionUntilTurnEnd) {
26317
+ await finalizePiPromptRun(state);
26318
+ }
26319
+ return;
26320
+ case "tool_execution_start":
26321
+ await startPiToolSpan(state, event);
26322
+ return;
26323
+ case "tool_execution_end":
26324
+ finishPiToolSpan(state, event);
26325
+ return;
26326
+ default:
26327
+ return;
26328
+ }
26329
+ }
26330
+ async function startPiToolSpan(state, event) {
26331
+ if (!event.toolCallId || state.activeToolSpans.has(event.toolCallId)) {
26332
+ return;
26333
+ }
26334
+ const restoreAutoInstrumentation = enterAutoInstrumentationAllowed();
26335
+ const metadata = {
26336
+ "gen_ai.tool.call.id": event.toolCallId,
26337
+ "gen_ai.tool.name": event.toolName,
26338
+ "pi_coding_agent.tool.name": event.toolName
26339
+ };
26340
+ try {
26341
+ const span = startSpan({
26342
+ event: {
26343
+ input: event.args,
26344
+ metadata
26345
+ },
26346
+ name: event.toolName || "tool",
26347
+ parent: await state.span.export(),
26348
+ spanAttributes: { type: "tool" /* TOOL */ }
26349
+ });
26350
+ state.activeToolSpans.set(event.toolCallId, {
26351
+ restoreAutoInstrumentation,
26352
+ span
26353
+ });
26354
+ } catch (error) {
26355
+ restoreAutoInstrumentation();
26356
+ throw error;
26357
+ }
26358
+ }
26359
+ function finishPiToolSpan(state, event) {
26360
+ const toolState = state.activeToolSpans.get(event.toolCallId);
26361
+ if (!toolState) {
26362
+ return;
26363
+ }
26364
+ state.activeToolSpans.delete(event.toolCallId);
26365
+ const metadata = {
26366
+ "gen_ai.tool.call.id": event.toolCallId,
26367
+ "gen_ai.tool.name": event.toolName,
26368
+ "pi_coding_agent.tool.name": event.toolName,
26369
+ "pi_coding_agent.tool.is_error": event.isError
26370
+ };
26371
+ try {
26372
+ safeLog4(toolState.span, {
26373
+ ...event.isError ? { error: stringifyUnknown2(event.result) } : {},
26374
+ metadata,
26375
+ output: event.result
26376
+ });
26377
+ } finally {
26378
+ try {
26379
+ toolState.span.end();
26380
+ } finally {
26381
+ toolState.restoreAutoInstrumentation?.();
26382
+ }
26383
+ }
26384
+ }
26385
+ async function finalizePiPromptRun(state, error) {
26386
+ if (state.finalized) {
26387
+ return;
26388
+ }
26389
+ state.finalized = true;
26390
+ state.onFinalize?.(state);
26391
+ restorePiStreamFn(state);
26392
+ try {
26393
+ state.unsubscribeAgent?.();
26394
+ } catch (unsubscribeError) {
26395
+ logInstrumentationError4("Pi Coding Agent unsubscribe", unsubscribeError);
26396
+ }
26397
+ await finishOpenLlmSpans(state, error);
26398
+ finishOpenToolSpans(state, error);
26399
+ const metadata = {
26400
+ ...state.metadata,
26401
+ ...extractModelMetadata2(state.agent.state?.model)
26402
+ };
26403
+ try {
26404
+ safeLog4(state.span, {
26405
+ ...error ? { error: stringifyUnknown2(error) } : {},
26406
+ metadata,
26407
+ metrics: {
26408
+ ...cleanMetrics5(state.metrics),
26409
+ ...buildDurationMetrics3(state.startTime)
26410
+ },
26411
+ output: state.output
26412
+ });
26413
+ } finally {
26414
+ state.span.end();
26415
+ }
26416
+ }
26417
+ function restorePiStreamFn(state) {
26418
+ const patchState = state.streamPatchState;
26419
+ patchState.activePromptStates.delete(state);
26420
+ removeQueuedPromptState(state);
26421
+ if (patchState.eventPromptState === state) {
26422
+ patchState.eventPromptState = void 0;
26423
+ }
26424
+ state.restorePromptContext?.();
26425
+ if (patchState.activePromptStates.size > 0) {
26426
+ return;
26427
+ }
26428
+ if (patchState.agent.streamFn === patchState.wrappedStreamFn) {
26429
+ patchState.agent.streamFn = patchState.originalStreamFn;
26430
+ }
26431
+ piStreamPatchStates.delete(patchState.agent);
26432
+ }
26433
+ function removeQueuedPromptState(state) {
26434
+ state.queued = false;
26435
+ const queuedPromptStates = state.streamPatchState.queuedPromptStates;
26436
+ const index = queuedPromptStates.indexOf(state);
26437
+ if (index >= 0) {
26438
+ queuedPromptStates.splice(index, 1);
26439
+ }
26440
+ }
26441
+ async function finishOpenLlmSpans(state, error) {
26442
+ for (const llmState of [...state.activeLlmSpans]) {
26443
+ finishPiLlmSpan(state, llmState, void 0, error);
26444
+ }
26445
+ }
26446
+ function finishPiLlmSpan(promptState, llmState, message, error) {
26447
+ if (llmState.finalized) {
26448
+ return;
26449
+ }
26450
+ llmState.finalized = true;
26451
+ promptState.activeLlmSpans.delete(llmState);
26452
+ const messageError = message?.stopReason === "error" && message.errorMessage;
26453
+ const metrics = {
26454
+ ...extractUsageMetrics2(message?.usage),
26455
+ ...cleanMetrics5(llmState.metrics),
26456
+ ...buildDurationMetrics3(llmState.startTime)
26457
+ };
26458
+ const usageMetrics = extractUsageMetrics2(message?.usage);
26459
+ if (Object.keys(usageMetrics).length > 0) {
26460
+ promptState.collectedLlmUsageMetrics = true;
26461
+ addMetrics(promptState.metrics, usageMetrics);
26462
+ }
26463
+ try {
26464
+ safeLog4(llmState.span, {
26465
+ ...error || messageError ? { error: stringifyUnknown2(error ?? messageError) } : {},
26466
+ metadata: {
26467
+ ...llmState.metadata,
26468
+ ...message ? extractAssistantMetadata(message) : {}
26469
+ },
26470
+ metrics,
26471
+ ...message ? { output: extractAssistantOutput(message) } : {}
26472
+ });
26473
+ } finally {
26474
+ llmState.span.end();
26475
+ }
26476
+ }
26477
+ function finishOpenToolSpans(state, error) {
26478
+ for (const [, toolState] of state.activeToolSpans) {
26479
+ try {
26480
+ safeLog4(toolState.span, {
26481
+ error: error ? stringifyUnknown2(error) : "Pi tool did not complete"
26482
+ });
26483
+ toolState.span.end();
26484
+ } finally {
26485
+ toolState.restoreAutoInstrumentation?.();
26486
+ }
26487
+ }
26488
+ state.activeToolSpans.clear();
26489
+ }
26490
+ function normalizePiContextInput(context) {
26491
+ const messages = context.messages.flatMap(
26492
+ (message) => normalizePiMessage(message)
26493
+ );
26494
+ if (context.systemPrompt) {
26495
+ return [{ role: "system", content: context.systemPrompt }, ...messages];
26496
+ }
26497
+ return messages;
26498
+ }
26499
+ function normalizePiMessage(message) {
26500
+ if (isPiUserMessage(message)) {
26501
+ return [
26502
+ {
26503
+ role: "user",
26504
+ content: normalizeUserContent(message.content)
26505
+ }
26506
+ ];
26507
+ }
26508
+ if (isPiAssistantMessage(message)) {
26509
+ return [normalizeAssistantMessage(message)];
26510
+ }
26511
+ if (isPiToolResultMessage(message)) {
26512
+ return [normalizeToolResultMessage(message)];
26513
+ }
26514
+ return [];
26515
+ }
26516
+ function normalizeAssistantMessage(message) {
26517
+ const text = message.content.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
26518
+ const thinking = message.content.flatMap(
26519
+ (part) => part.type === "thinking" && !part.redacted ? [part.thinking] : []
26520
+ ).join("");
26521
+ const toolCalls = message.content.flatMap(
26522
+ (part) => part.type === "toolCall" ? [normalizeToolCall(part)] : []
26523
+ );
26524
+ return {
26525
+ role: "assistant",
26526
+ content: text || (toolCalls.length > 0 ? null : ""),
26527
+ ...thinking ? { reasoning: thinking } : {},
26528
+ ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
26529
+ };
26530
+ }
26531
+ function normalizeToolResultMessage(message) {
26532
+ return {
26533
+ role: "tool",
26534
+ tool_call_id: message.toolCallId,
26535
+ content: normalizeUserContent(message.content)
26536
+ };
26537
+ }
26538
+ function normalizeUserContent(content) {
26539
+ if (typeof content === "string") {
26540
+ return content;
26541
+ }
26542
+ return content.map((part) => {
26543
+ if (part.type === "text") {
26544
+ return { type: "text", text: part.text };
26545
+ }
26546
+ if (part.type === "image") {
26547
+ return {
26548
+ type: "image_url",
26549
+ image_url: {
26550
+ url: `data:${part.mimeType};base64,${part.data}`
26551
+ }
26552
+ };
26553
+ }
26554
+ return part;
26555
+ });
26556
+ }
26557
+ function normalizeToolCall(toolCall) {
26558
+ return {
26559
+ id: toolCall.id,
26560
+ type: "function",
26561
+ function: {
26562
+ name: toolCall.name,
26563
+ arguments: stringifyArguments(toolCall.arguments)
26564
+ }
26565
+ };
26566
+ }
26567
+ function extractAssistantOutput(message) {
26568
+ return processInputAttachments([
26569
+ {
26570
+ finish_reason: normalizeStopReason(message.stopReason),
26571
+ index: 0,
26572
+ message: normalizeAssistantMessage(message)
26573
+ }
26574
+ ]);
26575
+ }
26576
+ function isPiUserMessage(message) {
26577
+ return message.role === "user" && "content" in message;
26578
+ }
26579
+ function isPiAssistantMessage(message) {
26580
+ return isObject(message) && message.role === "assistant" && Array.isArray(message.content);
26581
+ }
26582
+ function isPiToolResultMessage(message) {
26583
+ return message.role === "toolResult" && Array.isArray(message.content);
26584
+ }
26585
+ function normalizeStopReason(reason) {
26586
+ switch (reason) {
26587
+ case "toolUse":
26588
+ return "tool_calls";
26589
+ case "length":
26590
+ case "stop":
26591
+ return reason;
26592
+ default:
26593
+ return reason ?? "stop";
26594
+ }
26595
+ }
26596
+ function extractPromptInput(text, options) {
26597
+ const images = options?.images;
26598
+ if (!images || images.length === 0) {
26599
+ return text;
26600
+ }
26601
+ return processInputAttachments([
26602
+ {
26603
+ role: "user",
26604
+ content: [
26605
+ { type: "text", text: text ?? "" },
26606
+ ...images.map((image) => ({
26607
+ type: "image_url",
26608
+ image_url: {
26609
+ url: `data:${image.mimeType};base64,${image.data}`
26610
+ }
26611
+ }))
26612
+ ]
26613
+ }
26614
+ ]);
26615
+ }
26616
+ function extractToolMetadata(tools) {
26617
+ if (!tools || tools.length === 0) {
26618
+ return {};
26619
+ }
26620
+ return {
26621
+ tools: tools.map((tool) => ({
26622
+ type: "function",
26623
+ function: {
26624
+ name: tool.name,
26625
+ ...tool.description ? { description: tool.description } : {},
26626
+ ...tool.parameters ? { parameters: tool.parameters } : {}
26627
+ }
26628
+ }))
26629
+ };
26630
+ }
26631
+ function extractModelMetadata2(model) {
26632
+ if (!model) {
26633
+ return {};
26634
+ }
26635
+ return {
26636
+ ...model.provider ? { provider: model.provider } : {},
26637
+ ...model.id ? { model: model.id, "pi_coding_agent.model": model.id } : {},
26638
+ ...model.api ? { "pi_coding_agent.api": model.api } : {},
26639
+ ...model.name ? { "pi_coding_agent.model_name": model.name } : {}
26640
+ };
26641
+ }
26642
+ function extractAssistantMetadata(message) {
26643
+ return {
26644
+ ...message.provider ? { provider: message.provider } : {},
26645
+ ...message.responseModel || message.model ? { model: message.responseModel ?? message.model } : {},
26646
+ ...message.api ? { "pi_coding_agent.api": message.api } : {},
26647
+ ...message.model ? { "pi_coding_agent.model": message.model } : {},
26648
+ ...message.responseModel ? { "pi_coding_agent.response_model": message.responseModel } : {},
26649
+ ...message.responseId ? { "pi_coding_agent.response_id": message.responseId } : {},
26650
+ ...message.stopReason ? { "pi_coding_agent.stop_reason": message.stopReason } : {}
26651
+ };
26652
+ }
26653
+ function extractSessionMetadata(session) {
26654
+ return {
26655
+ ...extractModelMetadata2(session.model),
26656
+ ...session.sessionId ? { "pi_coding_agent.session_id": session.sessionId } : {},
26657
+ ...session.sessionName ? { "pi_coding_agent.session_name": session.sessionName } : {},
26658
+ ...session.thinkingLevel ? { "pi_coding_agent.thinking_level": session.thinkingLevel } : {},
26659
+ ...typeof session.getActiveToolNames === "function" ? { "pi_coding_agent.active_tools": session.getActiveToolNames() } : {}
26660
+ };
26661
+ }
26662
+ function extractPromptOptionsMetadata(options) {
26663
+ if (!options) {
26664
+ return {};
26665
+ }
26666
+ return {
26667
+ ...options.source ? { "pi_coding_agent.source": options.source } : {},
26668
+ ...options.streamingBehavior ? { "pi_coding_agent.streaming_behavior": options.streamingBehavior } : {},
26669
+ ...options.expandPromptTemplates !== void 0 ? {
26670
+ "pi_coding_agent.expand_prompt_templates": options.expandPromptTemplates
26671
+ } : {}
26672
+ };
26673
+ }
26674
+ function extractStreamOptionsMetadata(options) {
26675
+ if (!options) {
26676
+ return {};
26677
+ }
26678
+ return {
26679
+ ...options.temperature !== void 0 ? { temperature: options.temperature } : {},
26680
+ ...options.maxTokens !== void 0 ? { max_tokens: options.maxTokens } : {},
26681
+ ...options.reasoning ? { "pi_coding_agent.reasoning": options.reasoning } : {},
26682
+ ...options.transport ? { "pi_coding_agent.transport": options.transport } : {},
26683
+ ...options.cacheRetention ? { "pi_coding_agent.cache_retention": options.cacheRetention } : {},
26684
+ ...options.sessionId ? { "pi_coding_agent.session_id": options.sessionId } : {},
26685
+ ...options.timeoutMs !== void 0 ? { "pi_coding_agent.timeout_ms": options.timeoutMs } : {},
26686
+ ...options.maxRetries !== void 0 ? { "pi_coding_agent.max_retries": options.maxRetries } : {},
26687
+ ...options.maxRetryDelayMs !== void 0 ? { "pi_coding_agent.max_retry_delay_ms": options.maxRetryDelayMs } : {},
26688
+ ...options.metadata ? { "pi_coding_agent.metadata": options.metadata } : {}
26689
+ };
26690
+ }
26691
+ function getLlmSpanName(model) {
26692
+ switch (model.api) {
26693
+ case "anthropic-messages":
26694
+ return "anthropic.messages.create";
26695
+ case "openai-completions":
26696
+ return "Chat Completion";
26697
+ case "openai-responses":
26698
+ case "azure-openai-responses":
26699
+ case "openai-codex-responses":
26700
+ return "openai.responses.create";
26701
+ case "google-generative-ai":
26702
+ case "google-vertex":
26703
+ return "generate_content";
26704
+ case "mistral-conversations":
26705
+ return "mistral.chat.stream";
26706
+ case "bedrock-converse-stream":
26707
+ return "bedrock.converse_stream";
26708
+ default:
26709
+ return "pi_ai.streamSimple";
26710
+ }
26711
+ }
26712
+ function extractUsageMetrics2(usage) {
26713
+ if (!usage) {
26714
+ return {};
26715
+ }
26716
+ return cleanMetrics5({
26717
+ completion_tokens: usage.output,
26718
+ prompt_cache_creation_tokens: usage.cacheWrite,
26719
+ prompt_cached_tokens: usage.cacheRead,
26720
+ prompt_tokens: usage.input,
26721
+ tokens: usage.totalTokens ?? usage.tokens
26722
+ });
26723
+ }
26724
+ function addMetrics(target, source) {
26725
+ for (const [key, value] of Object.entries(source)) {
26726
+ target[key] = (target[key] ?? 0) + value;
26727
+ }
26728
+ }
26729
+ function buildDurationMetrics3(startTime) {
26730
+ const end = getCurrentUnixTimestamp();
26731
+ return {
26732
+ duration: end - startTime,
26733
+ end,
26734
+ start: startTime
26735
+ };
26736
+ }
26737
+ function cleanMetrics5(metrics) {
26738
+ const cleaned = {};
26739
+ for (const [key, value] of Object.entries(metrics)) {
26740
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
26741
+ cleaned[key] = value;
26742
+ }
26743
+ }
26744
+ return cleaned;
26745
+ }
26746
+ function stringifyArguments(value) {
26747
+ if (typeof value === "string") {
26748
+ return value;
26749
+ }
26750
+ try {
26751
+ return JSON.stringify(value);
26752
+ } catch {
26753
+ return stringifyUnknown2(value);
26754
+ }
26755
+ }
26756
+ function stringifyUnknown2(value) {
26757
+ if (value instanceof Error) {
26758
+ return value.message;
26759
+ }
26760
+ if (typeof value === "string") {
26761
+ return value;
26762
+ }
26763
+ try {
26764
+ return JSON.stringify(value);
26765
+ } catch {
26766
+ return String(value);
26767
+ }
26768
+ }
26769
+ function safeLog4(span, event) {
26770
+ try {
26771
+ span.log(event);
26772
+ } catch (error) {
26773
+ logInstrumentationError4("Pi Coding Agent span log", error);
26774
+ }
26775
+ }
26776
+ function logInstrumentationError4(context, error) {
26777
+ debugLogger.debug(`${context}:`, error);
26778
+ }
26779
+
24729
26780
  // src/instrumentation/braintrust-plugin.ts
24730
26781
  function getIntegrationConfig(integrations, key) {
24731
26782
  return integrations[key];
@@ -24751,6 +26802,7 @@ var BraintrustPlugin = class extends BasePlugin {
24751
26802
  gitHubCopilotPlugin = null;
24752
26803
  fluePlugin = null;
24753
26804
  langChainPlugin = null;
26805
+ piCodingAgentPlugin = null;
24754
26806
  constructor(config = {}) {
24755
26807
  super();
24756
26808
  this.config = config;
@@ -24825,6 +26877,10 @@ var BraintrustPlugin = class extends BasePlugin {
24825
26877
  this.gitHubCopilotPlugin = new GitHubCopilotPlugin();
24826
26878
  this.gitHubCopilotPlugin.enable();
24827
26879
  }
26880
+ if (integrations.piCodingAgent !== false) {
26881
+ this.piCodingAgentPlugin = new PiCodingAgentPlugin();
26882
+ this.piCodingAgentPlugin.enable();
26883
+ }
24828
26884
  if (getIntegrationConfig(integrations, "flue") !== false) {
24829
26885
  this.fluePlugin = new FluePlugin();
24830
26886
  this.fluePlugin.enable();
@@ -24903,6 +26959,10 @@ var BraintrustPlugin = class extends BasePlugin {
24903
26959
  this.gitHubCopilotPlugin.disable();
24904
26960
  this.gitHubCopilotPlugin = null;
24905
26961
  }
26962
+ if (this.piCodingAgentPlugin) {
26963
+ this.piCodingAgentPlugin.disable();
26964
+ this.piCodingAgentPlugin = null;
26965
+ }
24906
26966
  if (this.fluePlugin) {
24907
26967
  this.fluePlugin.disable();
24908
26968
  this.fluePlugin = null;
@@ -24922,6 +26982,11 @@ var envIntegrationAliases = {
24922
26982
  openaicodexsdk: "openaiCodexSDK",
24923
26983
  codex: "openaiCodexSDK",
24924
26984
  "codex-sdk": "openaiCodexSDK",
26985
+ "pi-coding-agent": "piCodingAgent",
26986
+ "pi-coding-agent-sdk": "piCodingAgent",
26987
+ picodingagent: "piCodingAgent",
26988
+ picodingagentsdk: "piCodingAgent",
26989
+ "@earendil-works/pi-coding-agent": "piCodingAgent",
24925
26990
  anthropic: "anthropic",
24926
26991
  aisdk: "aisdk",
24927
26992
  "ai-sdk": "aisdk",
@@ -24987,7 +27052,8 @@ function getDefaultInstrumentationIntegrations() {
24987
27052
  genkit: true,
24988
27053
  gitHubCopilot: true,
24989
27054
  langchain: true,
24990
- langgraph: true
27055
+ langgraph: true,
27056
+ piCodingAgent: true
24991
27057
  };
24992
27058
  }
24993
27059
  function readDisabledInstrumentationEnvConfig(disabledList) {
@@ -27048,6 +29114,13 @@ var EvalResultWithSummary = class {
27048
29114
  };
27049
29115
  }
27050
29116
  };
29117
+ async function getPersistedBaseExperimentId(experiment) {
29118
+ try {
29119
+ return await experiment._getBaseExperimentId();
29120
+ } catch {
29121
+ return void 0;
29122
+ }
29123
+ }
27051
29124
  function makeEvalName(projectName, experimentName) {
27052
29125
  let out = projectName;
27053
29126
  if (experimentName) {
@@ -27411,6 +29484,14 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
27411
29484
  return;
27412
29485
  }
27413
29486
  const eventDataset = experiment ? experiment.dataset : Dataset2.isDataset(evaluator.data) ? evaluator.data : void 0;
29487
+ const inlineOrigin = datum.origin === void 0 ? void 0 : ObjectReference.parse(datum.origin);
29488
+ const origin = inlineOrigin ?? (eventDataset && datum.id && datum._xact_id ? {
29489
+ object_type: "dataset",
29490
+ object_id: await eventDataset.id,
29491
+ id: datum.id,
29492
+ created: datum.created,
29493
+ _xact_id: datum._xact_id
29494
+ } : void 0);
27414
29495
  const baseEvent = {
27415
29496
  name: "eval",
27416
29497
  spanAttributes: {
@@ -27420,13 +29501,7 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
27420
29501
  input: datum.input,
27421
29502
  expected: "expected" in datum ? datum.expected : void 0,
27422
29503
  tags: datum.tags,
27423
- origin: eventDataset && datum.id && datum._xact_id ? {
27424
- object_type: "dataset",
27425
- object_id: await eventDataset.id,
27426
- id: datum.id,
27427
- created: datum.created,
27428
- _xact_id: datum._xact_id
27429
- } : void 0,
29504
+ origin,
27430
29505
  ...datum.upsert_id ? { id: datum.upsert_id } : {}
27431
29506
  }
27432
29507
  };
@@ -27783,8 +29858,10 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
27783
29858
  collectedResults.length = 0;
27784
29859
  }
27785
29860
  }
29861
+ const comparisonExperimentId = experiment ? evaluator.baseExperimentId ?? await getPersistedBaseExperimentId(experiment) : void 0;
27786
29862
  const summary = experiment ? await experiment.summarize({
27787
- summarizeScores: evaluator.summarizeScores
29863
+ summarizeScores: evaluator.summarizeScores,
29864
+ ...comparisonExperimentId !== void 0 ? { comparisonExperimentId } : {}
27788
29865
  }) : buildLocalSummary(
27789
29866
  evaluator,
27790
29867
  collectResults ? collectedResults : [],
@@ -28221,7 +30298,7 @@ var serializedParametersContainerSchema = z12.union([
28221
30298
  staticParametersSchema
28222
30299
  ]);
28223
30300
  var evaluatorDefinitionSchema = z12.object({
28224
- parameters: serializedParametersContainerSchema.optional(),
30301
+ parameters: serializedParametersContainerSchema.nullish(),
28225
30302
  scores: z12.array(z12.object({ name: z12.string() })).optional(),
28226
30303
  classifiers: z12.array(z12.object({ name: z12.string() })).optional()
28227
30304
  });
@@ -28250,6 +30327,7 @@ var Project2 = class {
28250
30327
  prompts;
28251
30328
  parameters;
28252
30329
  scorers;
30330
+ classifiers;
28253
30331
  _publishableCodeFunctions = [];
28254
30332
  _publishablePrompts = [];
28255
30333
  _publishableParameters = [];
@@ -28261,6 +30339,7 @@ var Project2 = class {
28261
30339
  this.prompts = new PromptBuilder(this);
28262
30340
  this.parameters = new ParametersBuilder(this);
28263
30341
  this.scorers = new ScorerBuilder(this);
30342
+ this.classifiers = new ClassifierBuilder(this);
28264
30343
  }
28265
30344
  addPrompt(prompt) {
28266
30345
  this._publishablePrompts.push(prompt);
@@ -28395,6 +30474,28 @@ var ScorerBuilder = class {
28395
30474
  }
28396
30475
  }
28397
30476
  };
30477
+ var ClassifierBuilder = class {
30478
+ constructor(project) {
30479
+ this.project = project;
30480
+ }
30481
+ taskCounter = 0;
30482
+ create(opts) {
30483
+ this.taskCounter++;
30484
+ let resolvedName = opts.name ?? opts.handler.name;
30485
+ if (!resolvedName || resolvedName.trim().length === 0) {
30486
+ resolvedName = `Classifier ${isomorph_default.basename(currentFilename)} ${this.taskCounter}`;
30487
+ }
30488
+ const slug = opts.slug ?? slugify(resolvedName, { lower: true, strict: true });
30489
+ const classifier = new CodeFunction(this.project, {
30490
+ ...opts,
30491
+ name: resolvedName,
30492
+ slug,
30493
+ type: "classifier"
30494
+ });
30495
+ this.project.addCodeFunction(classifier);
30496
+ return classifier;
30497
+ }
30498
+ };
28398
30499
  var CodeFunction = class {
28399
30500
  constructor(project, opts) {
28400
30501
  this.project = project;