braintrust 3.16.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 (55) hide show
  1. package/bin/bt +41 -0
  2. package/dev/dist/index.d.mts +13 -0
  3. package/dev/dist/index.d.ts +13 -0
  4. package/dev/dist/index.js +4300 -2199
  5. package/dev/dist/index.mjs +2211 -110
  6. package/dist/apply-auto-instrumentation.js +238 -186
  7. package/dist/apply-auto-instrumentation.mjs +68 -16
  8. package/dist/auto-instrumentations/bundler/esbuild.cjs +90 -15
  9. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  10. package/dist/auto-instrumentations/bundler/next.cjs +90 -15
  11. package/dist/auto-instrumentations/bundler/next.mjs +3 -3
  12. package/dist/auto-instrumentations/bundler/rollup.cjs +90 -15
  13. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  14. package/dist/auto-instrumentations/bundler/vite.cjs +90 -15
  15. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  16. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +89 -14
  17. package/dist/auto-instrumentations/bundler/webpack.cjs +90 -15
  18. package/dist/auto-instrumentations/bundler/webpack.mjs +3 -3
  19. package/dist/auto-instrumentations/{chunk-VXJONZVX.mjs → chunk-GNUEZ2PE.mjs} +13 -3
  20. package/dist/auto-instrumentations/{chunk-CNQ7BUKN.mjs → chunk-MYCHHXOE.mjs} +1 -1
  21. package/dist/auto-instrumentations/{chunk-E5DUYJWK.mjs → chunk-ZYKZEMRT.mjs} +82 -15
  22. package/dist/auto-instrumentations/hook.mjs +102 -15
  23. package/dist/auto-instrumentations/index.cjs +84 -16
  24. package/dist/auto-instrumentations/index.d.mts +3 -1
  25. package/dist/auto-instrumentations/index.d.ts +3 -1
  26. package/dist/auto-instrumentations/index.mjs +5 -3
  27. package/dist/auto-instrumentations/loader/cjs-patch.cjs +1 -1
  28. package/dist/auto-instrumentations/loader/cjs-patch.mjs +1 -1
  29. package/dist/auto-instrumentations/loader/esm-hook.mjs +1 -1
  30. package/dist/browser.d.mts +177 -11
  31. package/dist/browser.d.ts +177 -11
  32. package/dist/browser.js +2296 -104
  33. package/dist/browser.mjs +2455 -263
  34. package/dist/{chunk-VMBQETG3.js → chunk-73IYIIOL.js} +28 -2
  35. package/dist/{chunk-O4ZIWXO3.mjs → chunk-BYFADLEZ.mjs} +27 -1
  36. package/dist/cli.js +2228 -113
  37. package/dist/edge-light.d.mts +1 -1
  38. package/dist/edge-light.d.ts +1 -1
  39. package/dist/edge-light.js +2296 -104
  40. package/dist/edge-light.mjs +2455 -263
  41. package/dist/index.d.mts +177 -11
  42. package/dist/index.d.ts +177 -11
  43. package/dist/index.js +3296 -1128
  44. package/dist/index.mjs +2272 -104
  45. package/dist/instrumentation/index.d.mts +3 -0
  46. package/dist/instrumentation/index.d.ts +3 -0
  47. package/dist/instrumentation/index.js +2171 -105
  48. package/dist/instrumentation/index.mjs +2171 -105
  49. package/dist/workerd.d.mts +1 -1
  50. package/dist/workerd.d.ts +1 -1
  51. package/dist/workerd.js +2296 -104
  52. package/dist/workerd.mjs +2455 -263
  53. package/package.json +19 -5
  54. package/scripts/bt-helper.js +139 -0
  55. package/scripts/install.js +153 -0
package/dist/cli.js CHANGED
@@ -1232,7 +1232,7 @@ var require_package = __commonJS({
1232
1232
  "package.json"(exports2, module2) {
1233
1233
  module2.exports = {
1234
1234
  name: "braintrust",
1235
- version: "3.16.0",
1235
+ version: "3.18.0",
1236
1236
  description: "SDK for integrating Braintrust",
1237
1237
  repository: {
1238
1238
  type: "git",
@@ -1251,7 +1251,8 @@ var require_package = __commonJS({
1251
1251
  "./dist/index.d.mts": "./dist/browser.d.mts"
1252
1252
  },
1253
1253
  bin: {
1254
- braintrust: "./dist/cli.js"
1254
+ braintrust: "./dist/cli.js",
1255
+ bt: "./bin/bt"
1255
1256
  },
1256
1257
  exports: {
1257
1258
  "./package.json": "./package.json",
@@ -1359,9 +1360,13 @@ var require_package = __commonJS({
1359
1360
  files: [
1360
1361
  "dist/**/*",
1361
1362
  "dev/dist/**/*",
1362
- "util/dist/**/*"
1363
+ "util/dist/**/*",
1364
+ "bin/bt",
1365
+ "scripts/bt-helper.js",
1366
+ "scripts/install.js"
1363
1367
  ],
1364
1368
  scripts: {
1369
+ postinstall: "node ./scripts/install.js",
1365
1370
  build: 'cross-env NODE_OPTIONS="--max-old-space-size=8192" tsup',
1366
1371
  "check:typings": "tsc --noEmit",
1367
1372
  watch: "tsup --watch",
@@ -1414,14 +1419,14 @@ var require_package = __commonJS({
1414
1419
  "eslint-plugin-node-import": "^1.0.5",
1415
1420
  openai: "6.25.0",
1416
1421
  rollup: "^4.60.3",
1417
- tar: "^7.5.2",
1422
+ tar: "^7.5.16",
1418
1423
  tinybench: "^4.0.1",
1419
1424
  tsup: "^8.5.1",
1420
1425
  tsx: "^4.21.0",
1421
1426
  typedoc: "^0.25.13",
1422
1427
  "typedoc-plugin-markdown": "^3.17.1",
1423
1428
  typescript: "5.4.4",
1424
- vite: "^6.4.2",
1429
+ vite: "^6.4.3",
1425
1430
  "vite-tsconfig-paths": "^4.3.2",
1426
1431
  vitest: "4.1.5",
1427
1432
  webpack: "^5.106.2",
@@ -1456,6 +1461,15 @@ var require_package = __commonJS({
1456
1461
  peerDependencies: {
1457
1462
  zod: "^3.25.34 || ^4.0"
1458
1463
  },
1464
+ optionalDependencies: {
1465
+ "@braintrust/bt-darwin-arm64": "0.12.0",
1466
+ "@braintrust/bt-darwin-x64": "0.12.0",
1467
+ "@braintrust/bt-linux-arm64": "0.12.0",
1468
+ "@braintrust/bt-linux-x64": "0.12.0",
1469
+ "@braintrust/bt-linux-x64-musl": "0.12.0",
1470
+ "@braintrust/bt-win32-arm64": "0.12.0",
1471
+ "@braintrust/bt-win32-x64": "0.12.0"
1472
+ },
1459
1473
  publishConfig: {
1460
1474
  access: "public",
1461
1475
  registry: "https://registry.npmjs.org/",
@@ -9119,6 +9133,10 @@ var Experiment2 = class extends ObjectFetcher {
9119
9133
  return (await this.lazyMetadata.get()).project;
9120
9134
  })();
9121
9135
  }
9136
+ async _getBaseExperimentId() {
9137
+ const baseExperimentId = (await this.lazyMetadata.get()).experiment.fullInfo["base_exp_id"];
9138
+ return typeof baseExperimentId === "string" && baseExperimentId ? baseExperimentId : void 0;
9139
+ }
9122
9140
  parentObjectType() {
9123
9141
  return 1 /* EXPERIMENT */;
9124
9142
  }
@@ -9259,6 +9277,14 @@ var Experiment2 = class extends ObjectFetcher {
9259
9277
  comparisonExperimentId = baseExperiment.id;
9260
9278
  comparisonExperimentName = baseExperiment.name;
9261
9279
  }
9280
+ } else {
9281
+ try {
9282
+ const comparisonExperiment = await state.apiConn().get_json(`v1/experiment/${comparisonExperimentId}`);
9283
+ if (typeof comparisonExperiment["name"] === "string") {
9284
+ comparisonExperimentName = comparisonExperiment["name"];
9285
+ }
9286
+ } catch {
9287
+ }
9262
9288
  }
9263
9289
  try {
9264
9290
  const results = await state.apiConn().get_json(
@@ -12243,6 +12269,13 @@ var EvalResultWithSummary = class {
12243
12269
  };
12244
12270
  }
12245
12271
  };
12272
+ async function getPersistedBaseExperimentId(experiment) {
12273
+ try {
12274
+ return await experiment._getBaseExperimentId();
12275
+ } catch {
12276
+ return void 0;
12277
+ }
12278
+ }
12246
12279
  function makeEvalName(projectName, experimentName) {
12247
12280
  let out = projectName;
12248
12281
  if (experimentName) {
@@ -12632,6 +12665,14 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
12632
12665
  return;
12633
12666
  }
12634
12667
  const eventDataset = experiment ? experiment.dataset : Dataset2.isDataset(evaluator.data) ? evaluator.data : void 0;
12668
+ const inlineOrigin = datum.origin === void 0 ? void 0 : ObjectReference.parse(datum.origin);
12669
+ const origin = inlineOrigin ?? (eventDataset && datum.id && datum._xact_id ? {
12670
+ object_type: "dataset",
12671
+ object_id: await eventDataset.id,
12672
+ id: datum.id,
12673
+ created: datum.created,
12674
+ _xact_id: datum._xact_id
12675
+ } : void 0);
12635
12676
  const baseEvent = {
12636
12677
  name: "eval",
12637
12678
  spanAttributes: {
@@ -12641,13 +12682,7 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
12641
12682
  input: datum.input,
12642
12683
  expected: "expected" in datum ? datum.expected : void 0,
12643
12684
  tags: datum.tags,
12644
- origin: eventDataset && datum.id && datum._xact_id ? {
12645
- object_type: "dataset",
12646
- object_id: await eventDataset.id,
12647
- id: datum.id,
12648
- created: datum.created,
12649
- _xact_id: datum._xact_id
12650
- } : void 0,
12685
+ origin,
12651
12686
  ...datum.upsert_id ? { id: datum.upsert_id } : {}
12652
12687
  }
12653
12688
  };
@@ -13004,8 +13039,10 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
13004
13039
  collectedResults.length = 0;
13005
13040
  }
13006
13041
  }
13042
+ const comparisonExperimentId = experiment ? evaluator.baseExperimentId ?? await getPersistedBaseExperimentId(experiment) : void 0;
13007
13043
  const summary = experiment ? await experiment.summarize({
13008
- summarizeScores: evaluator.summarizeScores
13044
+ summarizeScores: evaluator.summarizeScores,
13045
+ ...comparisonExperimentId !== void 0 ? { comparisonExperimentId } : {}
13009
13046
  }) : buildLocalSummary(
13010
13047
  evaluator,
13011
13048
  collectResults ? collectedResults : [],
@@ -14273,6 +14310,64 @@ var BasePlugin = class {
14273
14310
  }
14274
14311
  };
14275
14312
 
14313
+ // src/instrumentation/auto-instrumentation-suppression.ts
14314
+ var autoInstrumentationSuppressionStore;
14315
+ function suppressionStore() {
14316
+ autoInstrumentationSuppressionStore ??= isomorph_default.newAsyncLocalStorage();
14317
+ return autoInstrumentationSuppressionStore;
14318
+ }
14319
+ function currentFrames() {
14320
+ return suppressionStore().getStore()?.frames ?? [];
14321
+ }
14322
+ function isAutoInstrumentationSuppressed() {
14323
+ const frames = currentFrames();
14324
+ return frames[frames.length - 1]?.mode === "suppress";
14325
+ }
14326
+ function runWithAutoInstrumentationSuppressed(callback) {
14327
+ const frame = {
14328
+ id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-suppress"),
14329
+ mode: "suppress"
14330
+ };
14331
+ return suppressionStore().run(
14332
+ { frames: [...currentFrames(), frame] },
14333
+ callback
14334
+ );
14335
+ }
14336
+ function bindAutoInstrumentationSuppressionToStart(tracingChannel2) {
14337
+ const startChannel = tracingChannel2.start;
14338
+ if (!startChannel) {
14339
+ return void 0;
14340
+ }
14341
+ const store = suppressionStore();
14342
+ startChannel.bindStore(store, () => ({
14343
+ frames: [
14344
+ ...currentFrames(),
14345
+ {
14346
+ id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-suppress"),
14347
+ mode: "suppress"
14348
+ }
14349
+ ]
14350
+ }));
14351
+ return () => {
14352
+ startChannel.unbindStore(store);
14353
+ };
14354
+ }
14355
+ function enterAutoInstrumentationAllowed() {
14356
+ const frame = {
14357
+ id: /* @__PURE__ */ Symbol("braintrust.auto-instrumentation-allow"),
14358
+ mode: "allow"
14359
+ };
14360
+ suppressionStore().enterWith({
14361
+ frames: [...currentFrames(), frame]
14362
+ });
14363
+ return () => {
14364
+ const frames = currentFrames().filter(
14365
+ (candidate) => candidate.id !== frame.id
14366
+ );
14367
+ suppressionStore().enterWith(frames.length > 0 ? { frames } : void 0);
14368
+ };
14369
+ }
14370
+
14276
14371
  // src/instrumentation/core/channel-tracing.ts
14277
14372
  function isSyncStreamLike(value) {
14278
14373
  return !!value && typeof value === "object" && typeof value.on === "function";
@@ -14304,16 +14399,33 @@ function startSpanForEvent(config3, event, channelName) {
14304
14399
  metadata: mergeInputMetadata(metadata, spanInfoMetadata)
14305
14400
  });
14306
14401
  } catch (error2) {
14307
- console.error(`Error extracting input for ${channelName}:`, error2);
14402
+ debugLogger.error(`Error extracting input for ${channelName}:`, error2);
14308
14403
  }
14309
14404
  return { span, startTime };
14310
14405
  }
14406
+ function shouldTraceEvent(config3, event, channelName) {
14407
+ if (!config3.shouldTrace) {
14408
+ return true;
14409
+ }
14410
+ try {
14411
+ return config3.shouldTrace(event.arguments, event);
14412
+ } catch (error2) {
14413
+ debugLogger.error(
14414
+ `Error checking trace predicate for ${channelName}:`,
14415
+ error2
14416
+ );
14417
+ return true;
14418
+ }
14419
+ }
14311
14420
  function ensureSpanStateForEvent(states, config3, event, channelName) {
14312
14421
  const key = event;
14313
14422
  const existing = states.get(key);
14314
14423
  if (existing) {
14315
14424
  return existing;
14316
14425
  }
14426
+ if (!shouldTraceEvent(config3, event, channelName)) {
14427
+ return void 0;
14428
+ }
14317
14429
  const created = startSpanForEvent(config3, event, channelName);
14318
14430
  states.set(key, created);
14319
14431
  return created;
@@ -14329,13 +14441,16 @@ function bindCurrentSpanStoreToStart(tracingChannel2, states, config3, channelNa
14329
14441
  startChannel.bindStore(
14330
14442
  currentSpanStore,
14331
14443
  (event) => {
14332
- const span = ensureSpanStateForEvent(
14444
+ if (isAutoInstrumentationSuppressed()) {
14445
+ return currentSpanStore.getStore();
14446
+ }
14447
+ const spanState = ensureSpanStateForEvent(
14333
14448
  states,
14334
14449
  config3,
14335
14450
  event,
14336
14451
  channelName
14337
- ).span;
14338
- return contextManager.wrapSpanForStore(span);
14452
+ );
14453
+ return spanState ? contextManager.wrapSpanForStore(spanState.span) : currentSpanStore.getStore();
14339
14454
  }
14340
14455
  );
14341
14456
  return () => {
@@ -14370,7 +14485,10 @@ function runStreamingCompletionHook(args) {
14370
14485
  startTime: args.startTime
14371
14486
  });
14372
14487
  } catch (error2) {
14373
- console.error(`Error in onComplete hook for ${args.channelName}:`, error2);
14488
+ debugLogger.error(
14489
+ `Error in onComplete hook for ${args.channelName}:`,
14490
+ error2
14491
+ );
14374
14492
  }
14375
14493
  }
14376
14494
  function traceAsyncChannel(channel2, config3) {
@@ -14385,6 +14503,9 @@ function traceAsyncChannel(channel2, config3) {
14385
14503
  );
14386
14504
  const handlers = {
14387
14505
  start: (event) => {
14506
+ if (isAutoInstrumentationSuppressed()) {
14507
+ return;
14508
+ }
14388
14509
  ensureSpanStateForEvent(
14389
14510
  states,
14390
14511
  config3,
@@ -14419,7 +14540,7 @@ function traceAsyncChannel(channel2, config3) {
14419
14540
  metrics
14420
14541
  });
14421
14542
  } catch (error2) {
14422
- console.error(`Error extracting output for ${channelName}:`, error2);
14543
+ debugLogger.error(`Error extracting output for ${channelName}:`, error2);
14423
14544
  } finally {
14424
14545
  span.end();
14425
14546
  states.delete(event);
@@ -14447,6 +14568,9 @@ function traceStreamingChannel(channel2, config3) {
14447
14568
  );
14448
14569
  const handlers = {
14449
14570
  start: (event) => {
14571
+ if (isAutoInstrumentationSuppressed()) {
14572
+ return;
14573
+ }
14450
14574
  ensureSpanStateForEvent(
14451
14575
  states,
14452
14576
  config3,
@@ -14518,7 +14642,7 @@ function traceStreamingChannel(channel2, config3) {
14518
14642
  metrics
14519
14643
  });
14520
14644
  } catch (error2) {
14521
- console.error(
14645
+ debugLogger.error(
14522
14646
  `Error extracting output for ${channelName}:`,
14523
14647
  error2
14524
14648
  );
@@ -14578,7 +14702,7 @@ function traceStreamingChannel(channel2, config3) {
14578
14702
  metrics
14579
14703
  });
14580
14704
  } catch (error2) {
14581
- console.error(`Error extracting output for ${channelName}:`, error2);
14705
+ debugLogger.error(`Error extracting output for ${channelName}:`, error2);
14582
14706
  } finally {
14583
14707
  span.end();
14584
14708
  states.delete(event);
@@ -14606,6 +14730,9 @@ function traceSyncStreamChannel(channel2, config3) {
14606
14730
  );
14607
14731
  const handlers = {
14608
14732
  start: (event) => {
14733
+ if (isAutoInstrumentationSuppressed()) {
14734
+ return;
14735
+ }
14609
14736
  ensureSpanStateForEvent(
14610
14737
  states,
14611
14738
  config3,
@@ -14659,7 +14786,7 @@ function traceSyncStreamChannel(channel2, config3) {
14659
14786
  });
14660
14787
  }
14661
14788
  } catch (error2) {
14662
- console.error(
14789
+ debugLogger.error(
14663
14790
  `Error extracting chatCompletion for ${channelName}:`,
14664
14791
  error2
14665
14792
  );
@@ -14683,7 +14810,10 @@ function traceSyncStreamChannel(channel2, config3) {
14683
14810
  span.log(extracted);
14684
14811
  }
14685
14812
  } catch (error2) {
14686
- console.error(`Error extracting event for ${channelName}:`, error2);
14813
+ debugLogger.error(
14814
+ `Error extracting event for ${channelName}:`,
14815
+ error2
14816
+ );
14687
14817
  }
14688
14818
  });
14689
14819
  stream.on("end", () => {
@@ -16212,6 +16342,9 @@ var AnthropicPlugin = class extends BasePlugin {
16212
16342
  const states = /* @__PURE__ */ new WeakMap();
16213
16343
  const handlers = {
16214
16344
  start: (event) => {
16345
+ if (isAutoInstrumentationSuppressed()) {
16346
+ return;
16347
+ }
16215
16348
  const params = event.arguments[0] ?? {};
16216
16349
  const span = startSpan({
16217
16350
  name: "anthropic.beta.messages.toolRunner",
@@ -16941,6 +17074,520 @@ function serializeAISDKToolsForLogging(tools) {
16941
17074
  return serialized;
16942
17075
  }
16943
17076
 
17077
+ // src/wrappers/ai-sdk/telemetry.ts
17078
+ function braintrustAISDKTelemetry() {
17079
+ const operations = /* @__PURE__ */ new Map();
17080
+ const modelSpans = /* @__PURE__ */ new Map();
17081
+ const objectSpans = /* @__PURE__ */ new Map();
17082
+ const embedSpans = /* @__PURE__ */ new Map();
17083
+ const rerankSpans = /* @__PURE__ */ new Map();
17084
+ const toolSpans = /* @__PURE__ */ new Map();
17085
+ const runSafely = (name, callback) => {
17086
+ try {
17087
+ callback();
17088
+ } catch (error2) {
17089
+ console.error(`Error in Braintrust AI SDK telemetry ${name}:`, error2);
17090
+ }
17091
+ };
17092
+ const startChildSpan = (callId, name, type, event) => {
17093
+ const parent = operations.get(callId)?.span;
17094
+ const spanArgs = {
17095
+ name,
17096
+ spanAttributes: { type },
17097
+ ...event ? { event } : {}
17098
+ };
17099
+ const span = parent ? parent.startSpan(spanArgs) : startSpan(spanArgs);
17100
+ const state = operations.get(callId);
17101
+ if (state && type === "llm" /* LLM */) {
17102
+ state.hadModelChild = true;
17103
+ }
17104
+ return span;
17105
+ };
17106
+ return {
17107
+ onStart(event) {
17108
+ runSafely("onStart", () => {
17109
+ const operationName = operationNameFromId(event.operationId);
17110
+ const span = startSpan({
17111
+ name: operationName,
17112
+ spanAttributes: { type: "function" /* FUNCTION */ }
17113
+ });
17114
+ operations.set(event.callId, {
17115
+ hadModelChild: false,
17116
+ operationName,
17117
+ span,
17118
+ startTime: getCurrentUnixTimestamp()
17119
+ });
17120
+ const metadata = metadataFromEvent(event);
17121
+ const logPayload = { metadata };
17122
+ if (shouldRecordInputs(event)) {
17123
+ const { input, outputPromise } = processAISDKCallInput(
17124
+ operationInput(event, operationName)
17125
+ );
17126
+ logPayload.input = input;
17127
+ if (outputPromise && input && typeof input === "object") {
17128
+ outputPromise.then((resolvedData) => {
17129
+ span.log({
17130
+ input: {
17131
+ ...input,
17132
+ ...resolvedData
17133
+ }
17134
+ });
17135
+ }).catch(() => {
17136
+ });
17137
+ }
17138
+ }
17139
+ span.log(logPayload);
17140
+ });
17141
+ },
17142
+ onLanguageModelCallStart(event) {
17143
+ runSafely("onLanguageModelCallStart", () => {
17144
+ const state = operations.get(event.callId);
17145
+ const openSpans = modelSpans.get(event.callId);
17146
+ if (openSpans) {
17147
+ for (const span2 of openSpans) {
17148
+ span2.end();
17149
+ }
17150
+ modelSpans.delete(event.callId);
17151
+ }
17152
+ const span = startChildSpan(
17153
+ event.callId,
17154
+ state?.operationName === "streamText" ? "doStream" : "doGenerate",
17155
+ "llm" /* LLM */,
17156
+ {
17157
+ ...shouldRecordInputs(event) ? {
17158
+ input: processAISDKCallInput(
17159
+ operationInput(
17160
+ event,
17161
+ state?.operationName ?? "generateText"
17162
+ )
17163
+ ).input
17164
+ } : {},
17165
+ metadata: metadataFromEvent(event)
17166
+ }
17167
+ );
17168
+ const spans = modelSpans.get(event.callId) ?? [];
17169
+ spans.push(span);
17170
+ modelSpans.set(event.callId, spans);
17171
+ });
17172
+ },
17173
+ onLanguageModelCallEnd(event) {
17174
+ runSafely("onLanguageModelCallEnd", () => {
17175
+ const span = shiftModelSpan(modelSpans, event.callId);
17176
+ if (!span) {
17177
+ return;
17178
+ }
17179
+ const result = {
17180
+ ...event,
17181
+ response: event.responseId ? { id: event.responseId } : void 0
17182
+ };
17183
+ span.log({
17184
+ ...shouldRecordOutputs(event) ? {
17185
+ output: processAISDKOutput(result, DEFAULT_DENY_OUTPUT_PATHS)
17186
+ } : {},
17187
+ metrics: extractTokenMetrics(result)
17188
+ });
17189
+ span.end();
17190
+ });
17191
+ },
17192
+ onObjectStepStart(event) {
17193
+ runSafely("onObjectStepStart", () => {
17194
+ const state = operations.get(event.callId);
17195
+ const openSpan = objectSpans.get(event.callId);
17196
+ if (openSpan) {
17197
+ openSpan.end();
17198
+ objectSpans.delete(event.callId);
17199
+ }
17200
+ const span = startChildSpan(
17201
+ event.callId,
17202
+ state?.operationName === "streamObject" ? "doStream" : "doGenerate",
17203
+ "llm" /* LLM */,
17204
+ {
17205
+ ...shouldRecordInputs(event) ? {
17206
+ input: {
17207
+ prompt: event.promptMessages
17208
+ }
17209
+ } : {},
17210
+ metadata: metadataFromEvent(event)
17211
+ }
17212
+ );
17213
+ objectSpans.set(event.callId, span);
17214
+ });
17215
+ },
17216
+ onObjectStepFinish(event) {
17217
+ runSafely("onObjectStepFinish", () => {
17218
+ const span = objectSpans.get(event.callId);
17219
+ if (!span) {
17220
+ return;
17221
+ }
17222
+ const result = {
17223
+ ...event,
17224
+ text: event.objectText
17225
+ };
17226
+ span.log({
17227
+ ...shouldRecordOutputs(event) ? {
17228
+ output: processAISDKOutput(result, DEFAULT_DENY_OUTPUT_PATHS)
17229
+ } : {},
17230
+ metrics: extractTokenMetrics(result)
17231
+ });
17232
+ span.end();
17233
+ objectSpans.delete(event.callId);
17234
+ });
17235
+ },
17236
+ onEmbedStart(event) {
17237
+ runSafely("onEmbedStart", () => {
17238
+ const state = operations.get(event.callId);
17239
+ for (const [embedCallId, embedState] of embedSpans) {
17240
+ if (embedState.callId === event.callId && (state?.operationName === "embed" || embedState.values === event.values)) {
17241
+ embedState.span.end();
17242
+ embedSpans.delete(embedCallId);
17243
+ }
17244
+ }
17245
+ const span = startChildSpan(
17246
+ event.callId,
17247
+ "doEmbed",
17248
+ "llm" /* LLM */,
17249
+ {
17250
+ ...shouldRecordInputs(event) ? {
17251
+ input: {
17252
+ values: event.values
17253
+ }
17254
+ } : {},
17255
+ metadata: metadataFromEvent(event)
17256
+ }
17257
+ );
17258
+ embedSpans.set(event.embedCallId, {
17259
+ callId: event.callId,
17260
+ span,
17261
+ values: event.values
17262
+ });
17263
+ });
17264
+ },
17265
+ onEmbedFinish(event) {
17266
+ runSafely("onEmbedFinish", () => {
17267
+ const state = embedSpans.get(event.embedCallId);
17268
+ if (!state) {
17269
+ return;
17270
+ }
17271
+ const result = {
17272
+ ...event,
17273
+ embeddings: event.embeddings
17274
+ };
17275
+ state.span.log({
17276
+ ...shouldRecordOutputs(event) ? {
17277
+ output: processAISDKEmbeddingOutput(
17278
+ result,
17279
+ DEFAULT_DENY_OUTPUT_PATHS
17280
+ )
17281
+ } : {},
17282
+ metrics: extractTokenMetrics(result)
17283
+ });
17284
+ state.span.end();
17285
+ embedSpans.delete(event.embedCallId);
17286
+ });
17287
+ },
17288
+ onRerankStart(event) {
17289
+ runSafely("onRerankStart", () => {
17290
+ const openSpan = rerankSpans.get(event.callId);
17291
+ if (openSpan) {
17292
+ openSpan.end();
17293
+ rerankSpans.delete(event.callId);
17294
+ }
17295
+ const span = startChildSpan(
17296
+ event.callId,
17297
+ "doRerank",
17298
+ "llm" /* LLM */,
17299
+ {
17300
+ ...shouldRecordInputs(event) ? {
17301
+ input: {
17302
+ documents: event.documents,
17303
+ query: event.query,
17304
+ topN: event.topN
17305
+ }
17306
+ } : {},
17307
+ metadata: metadataFromEvent(event)
17308
+ }
17309
+ );
17310
+ rerankSpans.set(event.callId, span);
17311
+ });
17312
+ },
17313
+ onRerankFinish(event) {
17314
+ runSafely("onRerankFinish", () => {
17315
+ const span = rerankSpans.get(event.callId);
17316
+ if (!span) {
17317
+ return;
17318
+ }
17319
+ const result = {
17320
+ ranking: event.ranking?.map((entry) => ({
17321
+ originalIndex: entry.index,
17322
+ score: entry.relevanceScore
17323
+ }))
17324
+ };
17325
+ span.log({
17326
+ ...shouldRecordOutputs(event) ? {
17327
+ output: processAISDKRerankOutput(
17328
+ result,
17329
+ DEFAULT_DENY_OUTPUT_PATHS
17330
+ )
17331
+ } : {}
17332
+ });
17333
+ span.end();
17334
+ rerankSpans.delete(event.callId);
17335
+ });
17336
+ },
17337
+ onToolExecutionStart(event) {
17338
+ runSafely("onToolExecutionStart", () => {
17339
+ const toolCallId = event.toolCall.toolCallId;
17340
+ if (!toolCallId) {
17341
+ return;
17342
+ }
17343
+ const span = startChildSpan(
17344
+ event.callId,
17345
+ event.toolCall.toolName || "tool",
17346
+ "tool" /* TOOL */,
17347
+ {
17348
+ ...shouldRecordInputs(event) ? {
17349
+ input: event.toolCall.input
17350
+ } : {},
17351
+ metadata: {
17352
+ ...createAISDKIntegrationMetadata(),
17353
+ toolCallId,
17354
+ ...typeof event.toolCall.toolName === "string" ? { toolName: event.toolCall.toolName } : {}
17355
+ }
17356
+ }
17357
+ );
17358
+ toolSpans.set(toolCallId, { callId: event.callId, span });
17359
+ });
17360
+ },
17361
+ onToolExecutionEnd(event) {
17362
+ runSafely("onToolExecutionEnd", () => {
17363
+ const toolCallId = event.toolCall.toolCallId;
17364
+ const state = toolCallId ? toolSpans.get(toolCallId) : void 0;
17365
+ if (!toolCallId || !state) {
17366
+ return;
17367
+ }
17368
+ const toolOutput = event.toolOutput;
17369
+ if (toolOutput?.type === "tool-error") {
17370
+ state.span.log({
17371
+ error: toolOutput.error instanceof Error ? toolOutput.error.message : String(toolOutput?.error),
17372
+ metrics: typeof event.durationMs === "number" ? { duration_ms: event.durationMs } : {}
17373
+ });
17374
+ } else {
17375
+ state.span.log({
17376
+ ...shouldRecordOutputs(event) ? { output: toolOutput?.output } : {},
17377
+ metrics: typeof event.durationMs === "number" ? { duration_ms: event.durationMs } : {}
17378
+ });
17379
+ }
17380
+ state.span.end();
17381
+ toolSpans.delete(toolCallId);
17382
+ });
17383
+ },
17384
+ onChunk(event) {
17385
+ runSafely("onChunk", () => {
17386
+ const callId = event.chunk?.callId;
17387
+ if (!callId) {
17388
+ return;
17389
+ }
17390
+ const state = operations.get(callId);
17391
+ if (!state || state.firstChunkTime !== void 0) {
17392
+ return;
17393
+ }
17394
+ state.firstChunkTime = getCurrentUnixTimestamp();
17395
+ });
17396
+ },
17397
+ onFinish(event) {
17398
+ runSafely("onFinish", () => {
17399
+ const state = operations.get(event.callId);
17400
+ if (!state) {
17401
+ return;
17402
+ }
17403
+ const result = finishResult(event, state.operationName);
17404
+ const metrics = state.hadModelChild ? {} : extractTokenMetrics(result);
17405
+ if (state.firstChunkTime !== void 0) {
17406
+ metrics.time_to_first_token = state.firstChunkTime - state.startTime;
17407
+ }
17408
+ state.span.log({
17409
+ ...shouldRecordOutputs(event) ? {
17410
+ output: finishOutput(result, state.operationName)
17411
+ } : {},
17412
+ metrics
17413
+ });
17414
+ state.span.end();
17415
+ operations.delete(event.callId);
17416
+ });
17417
+ },
17418
+ onError(event) {
17419
+ runSafely("onError", () => {
17420
+ const errorEvent = isObject(event) ? event : {};
17421
+ const callId = typeof errorEvent.callId === "string" ? errorEvent.callId : void 0;
17422
+ if (!callId) {
17423
+ return;
17424
+ }
17425
+ const state = operations.get(callId);
17426
+ if (!state) {
17427
+ return;
17428
+ }
17429
+ const error2 = errorEvent.error ?? event;
17430
+ const openModelSpans = modelSpans.get(callId);
17431
+ if (openModelSpans) {
17432
+ for (const span of openModelSpans) {
17433
+ logError(span, error2);
17434
+ span.end();
17435
+ }
17436
+ modelSpans.delete(callId);
17437
+ }
17438
+ const openObjectSpan = objectSpans.get(callId);
17439
+ if (openObjectSpan) {
17440
+ logError(openObjectSpan, error2);
17441
+ openObjectSpan.end();
17442
+ objectSpans.delete(callId);
17443
+ }
17444
+ for (const [embedCallId, embedState] of embedSpans) {
17445
+ if (embedState.callId === callId) {
17446
+ logError(embedState.span, error2);
17447
+ embedState.span.end();
17448
+ embedSpans.delete(embedCallId);
17449
+ }
17450
+ }
17451
+ const openRerankSpan = rerankSpans.get(callId);
17452
+ if (openRerankSpan) {
17453
+ logError(openRerankSpan, error2);
17454
+ openRerankSpan.end();
17455
+ rerankSpans.delete(callId);
17456
+ }
17457
+ for (const [toolCallId, toolState] of toolSpans) {
17458
+ if (toolState.callId === callId) {
17459
+ logError(toolState.span, error2);
17460
+ toolState.span.end();
17461
+ toolSpans.delete(toolCallId);
17462
+ }
17463
+ }
17464
+ logError(state.span, error2);
17465
+ state.span.end();
17466
+ operations.delete(callId);
17467
+ });
17468
+ },
17469
+ executeTool({ toolCallId, execute }) {
17470
+ const state = toolSpans.get(toolCallId);
17471
+ return state ? withCurrent(state.span, () => execute()) : execute();
17472
+ }
17473
+ };
17474
+ }
17475
+ function shouldRecordInputs(event) {
17476
+ return event.recordInputs !== false;
17477
+ }
17478
+ function shouldRecordOutputs(event) {
17479
+ return event.recordOutputs !== false;
17480
+ }
17481
+ function operationNameFromId(operationId) {
17482
+ return operationId?.startsWith("ai.") ? operationId.slice("ai.".length) : operationId || "ai-sdk";
17483
+ }
17484
+ function modelFromEvent(event) {
17485
+ return event.modelId ? {
17486
+ modelId: event.modelId,
17487
+ ...event.provider ? { provider: event.provider } : {}
17488
+ } : void 0;
17489
+ }
17490
+ function metadataFromEvent(event) {
17491
+ const metadata = createAISDKIntegrationMetadata();
17492
+ const { model, provider } = serializeModelWithProvider(modelFromEvent(event));
17493
+ if (model) {
17494
+ metadata.model = model;
17495
+ }
17496
+ if (provider) {
17497
+ metadata.provider = provider;
17498
+ }
17499
+ if (typeof event.functionId === "string") {
17500
+ metadata.functionId = event.functionId;
17501
+ }
17502
+ return metadata;
17503
+ }
17504
+ function operationInput(event, operationName) {
17505
+ if (operationName === "embed") {
17506
+ return {
17507
+ model: modelFromEvent(event),
17508
+ value: event.value
17509
+ };
17510
+ }
17511
+ if (operationName === "embedMany") {
17512
+ return {
17513
+ model: modelFromEvent(event),
17514
+ values: Array.isArray(event.value) ? event.value ?? [] : void 0
17515
+ };
17516
+ }
17517
+ if (operationName === "rerank") {
17518
+ return {
17519
+ model: modelFromEvent(event),
17520
+ documents: event.documents,
17521
+ query: event.query,
17522
+ topN: event.topN
17523
+ };
17524
+ }
17525
+ return {
17526
+ model: modelFromEvent(event),
17527
+ system: event.system,
17528
+ prompt: event.prompt,
17529
+ messages: event.messages,
17530
+ tools: event.tools,
17531
+ toolChoice: event.toolChoice,
17532
+ activeTools: event.activeTools,
17533
+ output: event.output,
17534
+ schema: event.schema,
17535
+ schemaName: event.schemaName,
17536
+ schemaDescription: event.schemaDescription,
17537
+ maxOutputTokens: event.maxOutputTokens,
17538
+ temperature: event.temperature,
17539
+ topP: event.topP,
17540
+ topK: event.topK,
17541
+ presencePenalty: event.presencePenalty,
17542
+ frequencyPenalty: event.frequencyPenalty,
17543
+ seed: event.seed,
17544
+ maxRetries: event.maxRetries,
17545
+ headers: event.headers,
17546
+ providerOptions: event.providerOptions
17547
+ };
17548
+ }
17549
+ function shiftModelSpan(modelSpans, callId) {
17550
+ const spans = modelSpans.get(callId);
17551
+ const span = spans?.shift();
17552
+ if (spans && spans.length === 0) {
17553
+ modelSpans.delete(callId);
17554
+ }
17555
+ return span;
17556
+ }
17557
+ function finishResult(event, operationName) {
17558
+ if (operationName === "embed") {
17559
+ return {
17560
+ ...event,
17561
+ embedding: event.embedding
17562
+ };
17563
+ }
17564
+ if (operationName === "embedMany") {
17565
+ return {
17566
+ ...event,
17567
+ embeddings: event.embedding
17568
+ };
17569
+ }
17570
+ if (operationName === "rerank") {
17571
+ return event;
17572
+ }
17573
+ return event;
17574
+ }
17575
+ function finishOutput(result, operationName) {
17576
+ if (operationName === "embed" || operationName === "embedMany") {
17577
+ return processAISDKEmbeddingOutput(
17578
+ result,
17579
+ DEFAULT_DENY_OUTPUT_PATHS
17580
+ );
17581
+ }
17582
+ if (operationName === "rerank") {
17583
+ return processAISDKRerankOutput(
17584
+ result,
17585
+ DEFAULT_DENY_OUTPUT_PATHS
17586
+ );
17587
+ }
17588
+ return processAISDKOutput(result, DEFAULT_DENY_OUTPUT_PATHS);
17589
+ }
17590
+
16944
17591
  // src/instrumentation/plugins/ai-sdk-channels.ts
16945
17592
  var aiSDKChannels = defineChannels("ai", {
16946
17593
  generateText: channel({
@@ -17000,6 +17647,10 @@ var aiSDKChannels = defineChannels("ai", {
17000
17647
  toolLoopAgentStream: channel({
17001
17648
  channelName: "ToolLoopAgent.stream",
17002
17649
  kind: "async"
17650
+ }),
17651
+ v7CreateTelemetryDispatcher: channel({
17652
+ channelName: "createTelemetryDispatcher",
17653
+ kind: "sync-stream"
17003
17654
  })
17004
17655
  });
17005
17656
 
@@ -17020,9 +17671,30 @@ var DEFAULT_DENY_OUTPUT_PATHS = [
17020
17671
  ];
17021
17672
  var AUTO_PATCHED_MODEL = /* @__PURE__ */ Symbol.for("braintrust.ai-sdk.auto-patched-model");
17022
17673
  var AUTO_PATCHED_TOOL = /* @__PURE__ */ Symbol.for("braintrust.ai-sdk.auto-patched-tool");
17674
+ var AUTO_PATCHED_V7_TELEMETRY_DISPATCHER = /* @__PURE__ */ Symbol.for(
17675
+ "braintrust.ai-sdk.v7.auto-patched-telemetry-dispatcher"
17676
+ );
17023
17677
  var RUNTIME_DENY_OUTPUT_PATHS = /* @__PURE__ */ Symbol.for(
17024
17678
  "braintrust.ai-sdk.deny-output-paths"
17025
17679
  );
17680
+ var AI_SDK_V7_TELEMETRY_CALLBACKS = [
17681
+ "onStart",
17682
+ "onStepStart",
17683
+ "onLanguageModelCallStart",
17684
+ "onLanguageModelCallEnd",
17685
+ "onToolExecutionStart",
17686
+ "onToolExecutionEnd",
17687
+ "onChunk",
17688
+ "onStepFinish",
17689
+ "onObjectStepStart",
17690
+ "onObjectStepFinish",
17691
+ "onEmbedStart",
17692
+ "onEmbedFinish",
17693
+ "onRerankStart",
17694
+ "onRerankFinish",
17695
+ "onFinish",
17696
+ "onError"
17697
+ ];
17026
17698
  var AISDKPlugin = class extends BasePlugin {
17027
17699
  config;
17028
17700
  constructor(config3 = {}) {
@@ -17037,6 +17709,7 @@ var AISDKPlugin = class extends BasePlugin {
17037
17709
  }
17038
17710
  subscribeToAISDK() {
17039
17711
  const denyOutputPaths = this.config.denyOutputPaths || DEFAULT_DENY_OUTPUT_PATHS;
17712
+ this.unsubscribers.push(subscribeToAISDKV7TelemetryDispatcher());
17040
17713
  this.unsubscribers.push(
17041
17714
  traceStreamingChannel(aiSDKChannels.generateText, {
17042
17715
  name: "generateText",
@@ -17261,9 +17934,60 @@ var AISDKPlugin = class extends BasePlugin {
17261
17934
  );
17262
17935
  }
17263
17936
  };
17264
- function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
17265
- if (Array.isArray(event?.denyOutputPaths)) {
17266
- return event.denyOutputPaths;
17937
+ function subscribeToAISDKV7TelemetryDispatcher() {
17938
+ const channel2 = aiSDKChannels.v7CreateTelemetryDispatcher.tracingChannel();
17939
+ const telemetry = braintrustAISDKTelemetry();
17940
+ const handlers = {
17941
+ end: (event) => {
17942
+ const telemetryOptions = event.arguments?.[0]?.telemetry;
17943
+ if (telemetryOptions?.isEnabled === false) {
17944
+ return;
17945
+ }
17946
+ patchAISDKV7TelemetryDispatcher(event.result, telemetry);
17947
+ }
17948
+ };
17949
+ channel2.subscribe(handlers);
17950
+ return () => {
17951
+ channel2.unsubscribe(handlers);
17952
+ };
17953
+ }
17954
+ function patchAISDKV7TelemetryDispatcher(dispatcher, telemetry) {
17955
+ if (!isObject(dispatcher)) {
17956
+ return;
17957
+ }
17958
+ const dispatcherRecord = dispatcher;
17959
+ if (dispatcherRecord[AUTO_PATCHED_V7_TELEMETRY_DISPATCHER]) {
17960
+ return;
17961
+ }
17962
+ dispatcherRecord[AUTO_PATCHED_V7_TELEMETRY_DISPATCHER] = true;
17963
+ for (const key of AI_SDK_V7_TELEMETRY_CALLBACKS) {
17964
+ const braintrustCallback = telemetry[key];
17965
+ if (typeof braintrustCallback !== "function") {
17966
+ continue;
17967
+ }
17968
+ const existingCallback = dispatcherRecord[key];
17969
+ dispatcherRecord[key] = (event) => {
17970
+ const existingResult = typeof existingCallback === "function" ? existingCallback.call(dispatcher, event) : void 0;
17971
+ const braintrustResult = braintrustCallback.call(telemetry, event);
17972
+ const pending = [existingResult, braintrustResult].filter(isPromiseLike);
17973
+ if (pending.length > 0) {
17974
+ return Promise.allSettled(pending).then(() => void 0);
17975
+ }
17976
+ };
17977
+ }
17978
+ const braintrustExecuteTool = telemetry.executeTool;
17979
+ if (typeof braintrustExecuteTool !== "function") {
17980
+ return;
17981
+ }
17982
+ const existingExecuteTool = dispatcherRecord.executeTool;
17983
+ dispatcherRecord.executeTool = (args) => braintrustExecuteTool.call(telemetry, {
17984
+ ...args,
17985
+ execute: () => typeof existingExecuteTool === "function" ? existingExecuteTool.call(dispatcher, args) : args.execute()
17986
+ });
17987
+ }
17988
+ function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
17989
+ if (Array.isArray(event?.denyOutputPaths)) {
17990
+ return event.denyOutputPaths;
17267
17991
  }
17268
17992
  const firstArgument2 = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
17269
17993
  if (!firstArgument2 || typeof firstArgument2 !== "object") {
@@ -17712,11 +18436,11 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
17712
18436
  metadata: baseMetadata
17713
18437
  }
17714
18438
  });
18439
+ const streamStartTime = getCurrentUnixTimestamp();
17715
18440
  const result = await withCurrent(
17716
18441
  span,
17717
18442
  () => Reflect.apply(originalDoStream, resolvedModel, [options])
17718
18443
  );
17719
- const streamStartTime = getCurrentUnixTimestamp();
17720
18444
  let firstChunkTime;
17721
18445
  const output = {};
17722
18446
  let text = "";
@@ -17725,7 +18449,7 @@ function prepareAISDKChildTracing(params, self, parentSpan, denyOutputPaths, aiS
17725
18449
  let object = void 0;
17726
18450
  const transformStream = new TransformStream({
17727
18451
  transform(chunk, controller) {
17728
- if (firstChunkTime === void 0) {
18452
+ if (firstChunkTime === void 0 && isAISDKContentStreamChunk(chunk)) {
17729
18453
  firstChunkTime = getCurrentUnixTimestamp();
17730
18454
  }
17731
18455
  switch (chunk.type) {
@@ -17917,6 +18641,78 @@ function finalizeAISDKChildTracing(event) {
17917
18641
  delete event.__braintrust_ai_sdk_cleanup;
17918
18642
  }
17919
18643
  }
18644
+ function extractAISDKStreamPart(chunk) {
18645
+ if (!isObject(chunk) || !isObject(chunk.part)) {
18646
+ return chunk;
18647
+ }
18648
+ return chunk.part;
18649
+ }
18650
+ function stringContent(value) {
18651
+ return typeof value === "string" && value.length > 0 ? value : void 0;
18652
+ }
18653
+ function rawValueHasAISDKContent(value) {
18654
+ if (value === void 0 || value === null) {
18655
+ return false;
18656
+ }
18657
+ if (typeof value === "string") {
18658
+ return value.length > 0;
18659
+ }
18660
+ if (!isObject(value)) {
18661
+ return true;
18662
+ }
18663
+ const delta = value.delta;
18664
+ 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) => {
18665
+ if (!isObject(choice) || !isObject(choice.delta)) {
18666
+ return false;
18667
+ }
18668
+ const choiceDelta = choice.delta;
18669
+ if (stringContent(choiceDelta.content) || stringContent(choiceDelta.text)) {
18670
+ return true;
18671
+ }
18672
+ if (isObject(choiceDelta.function_call) && stringContent(choiceDelta.function_call.arguments)) {
18673
+ return true;
18674
+ }
18675
+ return Array.isArray(choiceDelta.tool_calls) && choiceDelta.tool_calls.some(
18676
+ (toolCall) => isObject(toolCall) && isObject(toolCall.function) && stringContent(toolCall.function.arguments)
18677
+ );
18678
+ })) {
18679
+ return true;
18680
+ }
18681
+ return false;
18682
+ }
18683
+ function isAISDKContentStreamChunk(chunk) {
18684
+ const part = extractAISDKStreamPart(chunk);
18685
+ if (typeof part === "string") {
18686
+ return part.length > 0;
18687
+ }
18688
+ if (!isObject(part) || typeof part.type !== "string") {
18689
+ return false;
18690
+ }
18691
+ switch (part.type) {
18692
+ case "text-delta":
18693
+ return stringContent(part.textDelta) !== void 0 || stringContent(part.delta) !== void 0 || stringContent(part.text) !== void 0 || stringContent(part.content) !== void 0;
18694
+ case "reasoning-delta":
18695
+ return stringContent(part.delta) !== void 0 || stringContent(part.text) !== void 0 || stringContent(part.content) !== void 0;
18696
+ case "tool-call":
18697
+ case "object":
18698
+ case "file":
18699
+ return true;
18700
+ case "tool-input-delta":
18701
+ case "tool-call-delta":
18702
+ 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;
18703
+ case "raw":
18704
+ return rawValueHasAISDKContent(part.rawValue);
18705
+ default:
18706
+ return false;
18707
+ }
18708
+ }
18709
+ function isAISDKContentAsyncIterableChunk(chunk) {
18710
+ if (isAISDKContentStreamChunk(chunk)) {
18711
+ return true;
18712
+ }
18713
+ const part = extractAISDKStreamPart(chunk);
18714
+ return isObject(part) && typeof part.type !== "string";
18715
+ }
17920
18716
  function patchAISDKStreamingResult(args) {
17921
18717
  const { defaultDenyOutputPaths, endEvent, result, span, startTime } = args;
17922
18718
  if (!result || typeof result !== "object") {
@@ -17929,7 +18725,7 @@ function patchAISDKStreamingResult(args) {
17929
18725
  const wrappedBaseStream = resultRecord.baseStream.pipeThrough(
17930
18726
  new TransformStream({
17931
18727
  transform(chunk, controller) {
17932
- if (firstChunkTime2 === void 0) {
18728
+ if (firstChunkTime2 === void 0 && isAISDKContentStreamChunk(chunk)) {
17933
18729
  firstChunkTime2 = getCurrentUnixTimestamp();
17934
18730
  }
17935
18731
  controller.enqueue(chunk);
@@ -17973,8 +18769,8 @@ function patchAISDKStreamingResult(args) {
17973
18769
  }
17974
18770
  let firstChunkTime;
17975
18771
  const wrappedStream = createPatchedAsyncIterable(streamField.stream, {
17976
- onChunk: () => {
17977
- if (firstChunkTime === void 0) {
18772
+ onChunk: (chunk) => {
18773
+ if (firstChunkTime === void 0 && isAISDKContentAsyncIterableChunk(chunk)) {
17978
18774
  firstChunkTime = getCurrentUnixTimestamp();
17979
18775
  }
17980
18776
  },
@@ -18946,15 +19742,17 @@ function collectLocalMcpServerToolHookNames(serverName, serverConfig) {
18946
19742
 
18947
19743
  // src/instrumentation/plugins/claude-agent-sdk-plugin.ts
18948
19744
  var ROOT_LLM_PARENT_KEY = "__root__";
19745
+ var SUB_AGENT_PROMPT_SOURCE_PRIORITY = {
19746
+ delegation: 0,
19747
+ lifecycle: 1,
19748
+ sidechain: 2
19749
+ };
18949
19750
  function llmParentKey(parentToolUseId) {
18950
19751
  return parentToolUseId ?? ROOT_LLM_PARENT_KEY;
18951
19752
  }
18952
19753
  function isSubAgentDelegationToolName(toolName) {
18953
19754
  return toolName === "Agent" || toolName === "Task";
18954
19755
  }
18955
- function shouldParentToolAsTaskSibling(toolName) {
18956
- return toolName === "Agent" || toolName === "Task" || toolName === "Bash";
18957
- }
18958
19756
  function filterSerializableOptions(options) {
18959
19757
  const allowedKeys = [
18960
19758
  "model",
@@ -19091,26 +19889,39 @@ function extractUsageFromMessage(message) {
19091
19889
  }
19092
19890
  return metrics;
19093
19891
  }
19094
- function buildLLMInput(prompt, conversationHistory, capturedPromptMessages) {
19095
- const promptMessages = [];
19096
- if (typeof prompt === "string") {
19097
- promptMessages.push({ content: prompt, role: "user" });
19098
- } else if (capturedPromptMessages && capturedPromptMessages.length > 0) {
19099
- for (const msg of capturedPromptMessages) {
19100
- const role = msg.message?.role;
19101
- const content = msg.message?.content;
19102
- if (role && content !== void 0) {
19103
- promptMessages.push({ content, role });
19104
- }
19105
- }
19106
- }
19892
+ function buildLLMInput(promptMessages, conversationHistory) {
19107
19893
  const inputParts = [...promptMessages, ...conversationHistory];
19108
19894
  return inputParts.length > 0 ? inputParts : void 0;
19109
19895
  }
19896
+ function conversationMessageFromSDKMessage(message) {
19897
+ const role = message.message?.role;
19898
+ const content = message.message?.content;
19899
+ if (role && content !== void 0) {
19900
+ return { content, role };
19901
+ }
19902
+ return void 0;
19903
+ }
19904
+ function messageContentHasBlockType(message, blockType) {
19905
+ const content = message.message?.content;
19906
+ return Array.isArray(content) && content.some(
19907
+ (block) => typeof block === "object" && block !== null && "type" in block && block.type === blockType
19908
+ );
19909
+ }
19910
+ function buildRootPromptMessages(prompt, capturedPromptMessages) {
19911
+ if (typeof prompt === "string") {
19912
+ return [{ content: prompt, role: "user" }];
19913
+ }
19914
+ if (!capturedPromptMessages || capturedPromptMessages.length === 0) {
19915
+ return [];
19916
+ }
19917
+ return capturedPromptMessages.map(conversationMessageFromSDKMessage).filter(
19918
+ (message) => message !== void 0
19919
+ );
19920
+ }
19110
19921
  function formatCapturedMessages(messages) {
19111
19922
  return messages.length > 0 ? messages : [];
19112
19923
  }
19113
- async function createLLMSpanForMessages(messages, prompt, conversationHistory, options, startTime, capturedPromptMessages, parentSpan, existingSpan) {
19924
+ async function createLLMSpanForMessages(messages, promptMessages, conversationHistory, options, startTime, parentSpan, existingSpan) {
19114
19925
  if (messages.length === 0) {
19115
19926
  return void 0;
19116
19927
  }
@@ -19120,11 +19931,7 @@ async function createLLMSpanForMessages(messages, prompt, conversationHistory, o
19120
19931
  }
19121
19932
  const model = lastMessage.message.model || options.model;
19122
19933
  const usage = extractUsageFromMessage(lastMessage);
19123
- const input = buildLLMInput(
19124
- prompt,
19125
- conversationHistory,
19126
- capturedPromptMessages
19127
- );
19934
+ const input = buildLLMInput(promptMessages, conversationHistory);
19128
19935
  const outputs = messages.map(
19129
19936
  (m) => m.message?.content && m.message?.role ? { content: m.message.content, role: m.message.role } : void 0
19130
19937
  ).filter(
@@ -19255,8 +20062,7 @@ function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers,
19255
20062
  },
19256
20063
  name: parsed.displayName,
19257
20064
  parent: await resolveParentSpan(toolUseID, {
19258
- agentId: input.agent_id,
19259
- preferTaskSiblingParent: shouldParentToolAsTaskSibling(parsed.toolName)
20065
+ agentId: input.agent_id
19260
20066
  }),
19261
20067
  spanAttributes: { type: "tool" /* TOOL */ }
19262
20068
  });
@@ -19502,12 +20308,37 @@ function injectTracingHooks(options, resolveParentSpan, activeToolSpans, localTo
19502
20308
  }
19503
20309
  };
19504
20310
  }
20311
+ function setSubAgentPromptMessages(state, parentToolUseId, promptMessages, source) {
20312
+ if (promptMessages.length === 0) {
20313
+ return;
20314
+ }
20315
+ const parentKey = llmParentKey(parentToolUseId);
20316
+ const sourcePriority = SUB_AGENT_PROMPT_SOURCE_PRIORITY[source];
20317
+ const currentPriority = state.promptSourcePriorityByParentKey.get(parentKey) ?? -1;
20318
+ if (sourcePriority > currentPriority) {
20319
+ state.promptMessagesByParentKey.set(parentKey, promptMessages);
20320
+ state.promptSourcePriorityByParentKey.set(parentKey, sourcePriority);
20321
+ }
20322
+ }
20323
+ function getConversationHistory(state, parentKey) {
20324
+ let conversationHistory = state.conversationHistoryByParentKey.get(parentKey);
20325
+ if (!conversationHistory) {
20326
+ conversationHistory = [];
20327
+ state.conversationHistoryByParentKey.set(parentKey, conversationHistory);
20328
+ }
20329
+ return conversationHistory;
20330
+ }
19505
20331
  async function finalizeCurrentMessageGroup(state) {
19506
20332
  if (state.currentMessages.length === 0) {
19507
20333
  return;
19508
20334
  }
19509
20335
  const parentToolUseId = state.currentMessages[0]?.parent_tool_use_id ?? null;
19510
20336
  const parentKey = llmParentKey(parentToolUseId);
20337
+ const conversationHistory = getConversationHistory(state, parentKey);
20338
+ const promptMessages = parentToolUseId ? state.promptMessagesByParentKey.get(parentKey) ?? [] : buildRootPromptMessages(
20339
+ state.originalPrompt,
20340
+ state.capturedPromptMessages
20341
+ );
19511
20342
  let parentSpan = await state.span.export();
19512
20343
  if (parentToolUseId) {
19513
20344
  const subAgentSpan = state.subAgentSpans.get(parentToolUseId);
@@ -19518,11 +20349,10 @@ async function finalizeCurrentMessageGroup(state) {
19518
20349
  const existingLlmSpan = state.activeLlmSpansByParentToolUse.get(parentKey);
19519
20350
  const llmSpanResult = await createLLMSpanForMessages(
19520
20351
  state.currentMessages,
19521
- state.originalPrompt,
19522
- state.finalResults,
20352
+ promptMessages,
20353
+ conversationHistory,
19523
20354
  state.options,
19524
20355
  state.currentMessageStartTime,
19525
- state.capturedPromptMessages,
19526
20356
  parentSpan,
19527
20357
  existingLlmSpan
19528
20358
  );
@@ -19536,6 +20366,7 @@ async function finalizeCurrentMessageGroup(state) {
19536
20366
  state.latestRootLlmParentRef.value = llmSpanResult.spanExport;
19537
20367
  }
19538
20368
  if (llmSpanResult.finalMessage) {
20369
+ conversationHistory.push(llmSpanResult.finalMessage);
19539
20370
  state.finalResults.push(llmSpanResult.finalMessage);
19540
20371
  }
19541
20372
  }
@@ -19563,6 +20394,15 @@ function maybeTrackToolUseContext(state, message) {
19563
20394
  description: getStringProperty(block.input, "description"),
19564
20395
  toolUseId: block.id
19565
20396
  });
20397
+ const prompt = getStringProperty(block.input, "prompt");
20398
+ if (prompt) {
20399
+ setSubAgentPromptMessages(
20400
+ state,
20401
+ block.id,
20402
+ [{ content: prompt, role: "user" }],
20403
+ "delegation"
20404
+ );
20405
+ }
19566
20406
  }
19567
20407
  }
19568
20408
  }
@@ -19681,6 +20521,14 @@ async function maybeHandleTaskLifecycleMessage(state, message) {
19681
20521
  };
19682
20522
  if (message.subtype === "task_started") {
19683
20523
  const prompt = getStringProperty(message, "prompt");
20524
+ if (prompt) {
20525
+ setSubAgentPromptMessages(
20526
+ state,
20527
+ toolUseId,
20528
+ [{ content: prompt, role: "user" }],
20529
+ "lifecycle"
20530
+ );
20531
+ }
19684
20532
  subAgentSpan.log({
19685
20533
  input: prompt,
19686
20534
  metadata
@@ -19731,6 +20579,28 @@ async function handleStreamMessage(state, message) {
19731
20579
  return;
19732
20580
  }
19733
20581
  await maybeStartSubAgentSpan(state, message);
20582
+ const messageParentToolUseId = message.parent_tool_use_id;
20583
+ if (messageParentToolUseId && message.type === "user") {
20584
+ const conversationMessage = conversationMessageFromSDKMessage(message);
20585
+ if (conversationMessage?.role === "user") {
20586
+ await finalizeCurrentMessageGroup(state);
20587
+ const parentKey = llmParentKey(messageParentToolUseId);
20588
+ const conversationHistory = getConversationHistory(state, parentKey);
20589
+ const currentPromptSourcePriority = state.promptSourcePriorityByParentKey.get(parentKey) ?? -1;
20590
+ const canUseAsInitialSidechainPrompt = conversationHistory.length === 0 && currentPromptSourcePriority < SUB_AGENT_PROMPT_SOURCE_PRIORITY.sidechain && !messageContentHasBlockType(message, "tool_result");
20591
+ if (!canUseAsInitialSidechainPrompt) {
20592
+ conversationHistory.push(conversationMessage);
20593
+ return;
20594
+ }
20595
+ setSubAgentPromptMessages(
20596
+ state,
20597
+ messageParentToolUseId,
20598
+ [conversationMessage],
20599
+ "sidechain"
20600
+ );
20601
+ return;
20602
+ }
20603
+ }
19734
20604
  const messageId = message.message?.id;
19735
20605
  if (messageId && messageId !== state.currentMessageId) {
19736
20606
  await finalizeCurrentMessageGroup(state);
@@ -19882,6 +20752,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
19882
20752
  }
19883
20753
  const activeToolSpans = /* @__PURE__ */ new Map();
19884
20754
  const activeLlmSpansByParentToolUse = /* @__PURE__ */ new Map();
20755
+ const conversationHistoryByParentKey = /* @__PURE__ */ new Map();
19885
20756
  const subAgentSpans = /* @__PURE__ */ new Map();
19886
20757
  const endedSubAgentSpans = /* @__PURE__ */ new Set();
19887
20758
  const toolUseToParent = /* @__PURE__ */ new Map();
@@ -19891,6 +20762,8 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
19891
20762
  };
19892
20763
  const subAgentDetailsByToolUseId = /* @__PURE__ */ new Map();
19893
20764
  const taskIdToToolUseId = /* @__PURE__ */ new Map();
20765
+ const promptMessagesByParentKey = /* @__PURE__ */ new Map();
20766
+ const promptSourcePriorityByParentKey = /* @__PURE__ */ new Map();
19894
20767
  const localToolContext = createClaudeLocalToolContext();
19895
20768
  const { hasLocalToolHandlers, localToolHookNames } = prepareLocalToolHandlersInMcpServers(options.mcpServers);
19896
20769
  const skipLocalToolHooks = options[CLAUDE_AGENT_SDK_SKIP_LOCAL_TOOL_HOOKS_OPTION] === true || hasLocalToolHandlers;
@@ -19899,38 +20772,19 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
19899
20772
  const parentToolUseId = trackedParentToolUseId ?? (context2?.agentId ? taskIdToToolUseId.get(context2.agentId) ?? null : null);
19900
20773
  const parentKey = llmParentKey(parentToolUseId);
19901
20774
  const activeLlmSpan = activeLlmSpansByParentToolUse.get(parentKey);
19902
- if (context2?.preferTaskSiblingParent) {
19903
- if (!activeLlmSpan) {
19904
- await ensureActiveLlmSpanForParentToolUse(
19905
- span,
19906
- activeLlmSpansByParentToolUse,
19907
- subAgentDetailsByToolUseId,
19908
- activeToolSpans,
19909
- subAgentSpans,
19910
- parentToolUseId,
19911
- getCurrentUnixTimestamp()
19912
- );
19913
- }
19914
- if (parentToolUseId) {
19915
- const subAgentSpan = await ensureSubAgentSpan(
19916
- subAgentDetailsByToolUseId,
19917
- span,
19918
- activeToolSpans,
19919
- subAgentSpans,
19920
- parentToolUseId
19921
- );
19922
- return subAgentSpan.export();
19923
- }
19924
- return span.export();
19925
- }
19926
- if (activeLlmSpan) {
19927
- return activeLlmSpan.export();
20775
+ const latestLlmParent = parentToolUseId ? latestLlmParentBySubAgentToolUse.get(parentToolUseId) : latestRootLlmParentRef.value;
20776
+ if (!activeLlmSpan && !latestLlmParent) {
20777
+ await ensureActiveLlmSpanForParentToolUse(
20778
+ span,
20779
+ activeLlmSpansByParentToolUse,
20780
+ subAgentDetailsByToolUseId,
20781
+ activeToolSpans,
20782
+ subAgentSpans,
20783
+ parentToolUseId,
20784
+ getCurrentUnixTimestamp()
20785
+ );
19928
20786
  }
19929
20787
  if (parentToolUseId) {
19930
- const parentLlm = latestLlmParentBySubAgentToolUse.get(parentToolUseId);
19931
- if (parentLlm) {
19932
- return parentLlm;
19933
- }
19934
20788
  const subAgentSpan = await ensureSubAgentSpan(
19935
20789
  subAgentDetailsByToolUseId,
19936
20790
  span,
@@ -19940,9 +20794,6 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
19940
20794
  );
19941
20795
  return subAgentSpan.export();
19942
20796
  }
19943
- if (latestRootLlmParentRef.value) {
19944
- return latestRootLlmParentRef.value;
19945
- }
19946
20797
  return span.export();
19947
20798
  };
19948
20799
  localToolContext.resolveLocalToolParent = resolveToolUseParentSpan;
@@ -19963,6 +20814,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
19963
20814
  accumulatedOutputTokens: 0,
19964
20815
  activeLlmSpansByParentToolUse,
19965
20816
  activeToolSpans,
20817
+ conversationHistoryByParentKey,
19966
20818
  capturedPromptMessages,
19967
20819
  currentMessageId: void 0,
19968
20820
  currentMessageStartTime: startTime,
@@ -19973,7 +20825,9 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
19973
20825
  originalPrompt,
19974
20826
  processing: Promise.resolve(),
19975
20827
  promptDone,
20828
+ promptMessagesByParentKey,
19976
20829
  promptStarted: () => promptStarted,
20830
+ promptSourcePriorityByParentKey,
19977
20831
  span,
19978
20832
  subAgentDetailsByToolUseId,
19979
20833
  subAgentSpans,
@@ -21402,6 +22256,10 @@ var googleGenAIChannels = defineChannels("@google/genai", {
21402
22256
  embedContent: channel({
21403
22257
  channelName: "models.embedContent",
21404
22258
  kind: "async"
22259
+ }),
22260
+ interactionsCreate: channel({
22261
+ channelName: "interactions.create",
22262
+ kind: "async"
21405
22263
  })
21406
22264
  });
21407
22265
 
@@ -21429,6 +22287,7 @@ var GoogleGenAIPlugin = class extends BasePlugin {
21429
22287
  this.subscribeToGenerateContentChannel();
21430
22288
  this.subscribeToGenerateContentStreamChannel();
21431
22289
  this.subscribeToEmbedContentChannel();
22290
+ this.subscribeToInteractionsCreateChannel();
21432
22291
  }
21433
22292
  subscribeToGenerateContentChannel() {
21434
22293
  const tracingChannel2 = googleGenAIChannels.generateContent.tracingChannel();
@@ -21601,7 +22460,30 @@ var GoogleGenAIPlugin = class extends BasePlugin {
21601
22460
  tracingChannel2.unsubscribe(handlers);
21602
22461
  });
21603
22462
  }
22463
+ subscribeToInteractionsCreateChannel() {
22464
+ this.unsubscribers.push(
22465
+ traceStreamingChannel(
22466
+ googleGenAIChannels.interactionsCreate,
22467
+ {
22468
+ name: "create_interaction",
22469
+ shouldTrace: ([params]) => !isBackgroundInteractionCreate(params),
22470
+ type: "llm" /* LLM */,
22471
+ extractInput: ([params]) => ({
22472
+ input: serializeInteractionInput(params),
22473
+ metadata: extractInteractionMetadata(params)
22474
+ }),
22475
+ extractOutput: (result) => serializeInteractionValue(result),
22476
+ extractMetadata: (result) => extractInteractionResponseMetadata(result),
22477
+ extractMetrics: (result, startTime) => cleanMetrics3(extractInteractionMetrics(result, startTime)),
22478
+ aggregateChunks: (chunks, _result, _event, startTime) => aggregateInteractionEvents(chunks, startTime)
22479
+ }
22480
+ )
22481
+ );
22482
+ }
21604
22483
  };
22484
+ function isBackgroundInteractionCreate(params) {
22485
+ return tryToDict(params)?.background === true;
22486
+ }
21605
22487
  function ensureSpanState(states, event, create) {
21606
22488
  const existing = states.get(event);
21607
22489
  if (existing) {
@@ -21809,6 +22691,60 @@ function serializeEmbedContentInput(params) {
21809
22691
  }
21810
22692
  return input;
21811
22693
  }
22694
+ function serializeInteractionInput(params) {
22695
+ const input = {
22696
+ input: serializeInteractionValue(params.input)
22697
+ };
22698
+ for (const key of [
22699
+ "model",
22700
+ "agent",
22701
+ "agent_config",
22702
+ "api_version",
22703
+ "background",
22704
+ "environment",
22705
+ "generation_config",
22706
+ "previous_interaction_id",
22707
+ "response_format",
22708
+ "response_mime_type",
22709
+ "response_modalities",
22710
+ "service_tier",
22711
+ "store",
22712
+ "stream",
22713
+ "system_instruction",
22714
+ "webhook_config"
22715
+ ]) {
22716
+ const value = params[key];
22717
+ if (value !== void 0) {
22718
+ input[key] = serializeInteractionValue(value);
22719
+ }
22720
+ }
22721
+ return input;
22722
+ }
22723
+ function extractInteractionMetadata(params) {
22724
+ const metadata = {};
22725
+ for (const key of [
22726
+ "model",
22727
+ "agent",
22728
+ "agent_config",
22729
+ "generation_config",
22730
+ "system_instruction",
22731
+ "response_format",
22732
+ "response_mime_type",
22733
+ "response_modalities",
22734
+ "service_tier"
22735
+ ]) {
22736
+ const value = params[key];
22737
+ if (value !== void 0) {
22738
+ metadata[key] = serializeInteractionValue(value);
22739
+ }
22740
+ }
22741
+ if (Array.isArray(params.tools)) {
22742
+ metadata.tools = params.tools.map(
22743
+ (tool) => serializeInteractionValue(tool)
22744
+ );
22745
+ }
22746
+ return metadata;
22747
+ }
21812
22748
  function serializeContentCollection(contents) {
21813
22749
  if (contents === null || contents === void 0) {
21814
22750
  return null;
@@ -21839,21 +22775,8 @@ function serializePart(part) {
21839
22775
  }
21840
22776
  if (part.inlineData && part.inlineData.data) {
21841
22777
  const { data, mimeType } = part.inlineData;
21842
- if (data instanceof Uint8Array || typeof Buffer !== "undefined" && Buffer.isBuffer(data) || typeof data === "string") {
21843
- const extension = mimeType ? mimeType.split("/")[1] : "bin";
21844
- const filename = `file.${extension}`;
21845
- const buffer = typeof data === "string" ? typeof Buffer !== "undefined" ? Buffer.from(data, "base64") : new Uint8Array(
21846
- atob(data).split("").map((c) => c.charCodeAt(0))
21847
- ) : typeof Buffer !== "undefined" ? Buffer.from(data) : new Uint8Array(data);
21848
- const arrayBuffer = buffer instanceof Uint8Array ? buffer.buffer.slice(
21849
- buffer.byteOffset,
21850
- buffer.byteOffset + buffer.byteLength
21851
- ) : buffer;
21852
- const attachment = new Attachment({
21853
- data: arrayBuffer,
21854
- filename,
21855
- contentType: mimeType || "application/octet-stream"
21856
- });
22778
+ const attachment = createAttachmentFromInlineData(data, mimeType);
22779
+ if (attachment) {
21857
22780
  return {
21858
22781
  image_url: { url: attachment }
21859
22782
  };
@@ -21861,6 +22784,59 @@ function serializePart(part) {
21861
22784
  }
21862
22785
  return part;
21863
22786
  }
22787
+ function serializeInteractionValue(value, seen = /* @__PURE__ */ new WeakSet()) {
22788
+ if (value === null || value === void 0 || typeof value !== "object") {
22789
+ return value;
22790
+ }
22791
+ if (Array.isArray(value)) {
22792
+ return value.map((item) => serializeInteractionValue(item, seen));
22793
+ }
22794
+ const dict = tryToDict(value);
22795
+ if (dict === null || dict === void 0 || typeof dict !== "object") {
22796
+ return dict;
22797
+ }
22798
+ if (Array.isArray(dict)) {
22799
+ return dict.map((item) => serializeInteractionValue(item, seen));
22800
+ }
22801
+ if (seen.has(dict)) {
22802
+ return "[Circular]";
22803
+ }
22804
+ seen.add(dict);
22805
+ try {
22806
+ const serialized = {};
22807
+ 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;
22808
+ const attachment = mimeType && "data" in dict && dict.data !== void 0 ? createAttachmentFromInlineData(dict.data, mimeType) : null;
22809
+ for (const [key, entry] of Object.entries(dict)) {
22810
+ if (key === "data" && attachment) {
22811
+ serialized[key] = attachment;
22812
+ } else {
22813
+ serialized[key] = serializeInteractionValue(entry, seen);
22814
+ }
22815
+ }
22816
+ return serialized;
22817
+ } finally {
22818
+ seen.delete(dict);
22819
+ }
22820
+ }
22821
+ function createAttachmentFromInlineData(data, mimeType) {
22822
+ if (!(data instanceof Uint8Array || typeof Buffer !== "undefined" && Buffer.isBuffer(data) || typeof data === "string")) {
22823
+ return null;
22824
+ }
22825
+ const extension = mimeType ? mimeType.split("/")[1] : "bin";
22826
+ const filename = `file.${extension}`;
22827
+ const buffer = typeof data === "string" ? typeof Buffer !== "undefined" ? Buffer.from(data, "base64") : new Uint8Array(
22828
+ atob(data).split("").map((c) => c.charCodeAt(0))
22829
+ ) : typeof Buffer !== "undefined" ? Buffer.from(data) : new Uint8Array(data);
22830
+ const arrayBuffer = buffer instanceof Uint8Array ? buffer.buffer.slice(
22831
+ buffer.byteOffset,
22832
+ buffer.byteOffset + buffer.byteLength
22833
+ ) : buffer;
22834
+ return new Attachment({
22835
+ data: arrayBuffer,
22836
+ filename,
22837
+ contentType: mimeType || "application/octet-stream"
22838
+ });
22839
+ }
21864
22840
  function serializeGenerateContentTools(params) {
21865
22841
  const config3 = params.config ? tryToDict(params.config) : null;
21866
22842
  const tools = config3?.tools;
@@ -21945,6 +22921,33 @@ function extractEmbedContentMetrics(response, startTime) {
21945
22921
  }
21946
22922
  return metrics;
21947
22923
  }
22924
+ function extractInteractionMetrics(response, startTime) {
22925
+ const metrics = {};
22926
+ if (startTime !== void 0) {
22927
+ const end = getCurrentUnixTimestamp();
22928
+ metrics.start = startTime;
22929
+ metrics.end = end;
22930
+ metrics.duration = end - startTime;
22931
+ }
22932
+ if (response?.usage) {
22933
+ populateInteractionUsageMetrics(metrics, response.usage);
22934
+ }
22935
+ return metrics;
22936
+ }
22937
+ function extractInteractionResponseMetadata(response) {
22938
+ const responseDict = tryToDict(response);
22939
+ if (!responseDict) {
22940
+ return void 0;
22941
+ }
22942
+ const metadata = {};
22943
+ if (typeof responseDict.id === "string") {
22944
+ metadata.interaction_id = responseDict.id;
22945
+ }
22946
+ if (typeof responseDict.status === "string") {
22947
+ metadata.status = responseDict.status;
22948
+ }
22949
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
22950
+ }
21948
22951
  function extractEmbedPromptTokenCount(response) {
21949
22952
  if (!response) {
21950
22953
  return void 0;
@@ -22007,6 +23010,23 @@ function populateUsageMetrics(metrics, usage) {
22007
23010
  metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
22008
23011
  }
22009
23012
  }
23013
+ function populateInteractionUsageMetrics(metrics, usage) {
23014
+ if (typeof usage.total_input_tokens === "number") {
23015
+ metrics.prompt_tokens = usage.total_input_tokens;
23016
+ }
23017
+ if (typeof usage.total_output_tokens === "number") {
23018
+ metrics.completion_tokens = usage.total_output_tokens;
23019
+ }
23020
+ if (typeof usage.total_tokens === "number") {
23021
+ metrics.tokens = usage.total_tokens;
23022
+ }
23023
+ if (typeof usage.total_cached_tokens === "number") {
23024
+ metrics.prompt_cached_tokens = usage.total_cached_tokens;
23025
+ }
23026
+ if (typeof usage.total_thought_tokens === "number") {
23027
+ metrics.completion_reasoning_tokens = usage.total_thought_tokens;
23028
+ }
23029
+ }
22010
23030
  function aggregateGenerateContentChunks(chunks, startTime, firstTokenTime) {
22011
23031
  const end = getCurrentUnixTimestamp();
22012
23032
  const metrics = {
@@ -22104,6 +23124,146 @@ function aggregateGenerateContentChunks(chunks, startTime, firstTokenTime) {
22104
23124
  }
22105
23125
  return { aggregated, metrics };
22106
23126
  }
23127
+ function aggregateInteractionEvents(chunks, startTime) {
23128
+ const end = getCurrentUnixTimestamp();
23129
+ const metrics = {};
23130
+ if (startTime !== void 0) {
23131
+ metrics.start = startTime;
23132
+ metrics.end = end;
23133
+ metrics.duration = end - startTime;
23134
+ }
23135
+ let latestInteraction;
23136
+ let latestUsage;
23137
+ let status;
23138
+ let outputText = "";
23139
+ const steps = /* @__PURE__ */ new Map();
23140
+ for (const chunk of chunks) {
23141
+ const event = tryToDict(chunk);
23142
+ if (!event) {
23143
+ continue;
23144
+ }
23145
+ const usage = extractInteractionUsageFromEvent(event);
23146
+ if (usage) {
23147
+ latestUsage = usage;
23148
+ }
23149
+ const interaction = tryToDict(event.interaction);
23150
+ if (interaction) {
23151
+ latestInteraction = serializeInteractionValue(interaction);
23152
+ if (typeof interaction.status === "string") {
23153
+ status = interaction.status;
23154
+ }
23155
+ }
23156
+ if (typeof event.status === "string") {
23157
+ status = event.status;
23158
+ }
23159
+ const index = typeof event.index === "number" ? event.index : void 0;
23160
+ if (index === void 0) {
23161
+ continue;
23162
+ }
23163
+ if (event.event_type === "step.start") {
23164
+ const compact = compactInteractionStep(event.step);
23165
+ compact.index = index;
23166
+ steps.set(index, compact);
23167
+ continue;
23168
+ }
23169
+ if (event.event_type === "step.delta") {
23170
+ const step = steps.get(index) ?? { index };
23171
+ const textDelta = applyInteractionDelta(step, event.delta);
23172
+ if (textDelta) {
23173
+ outputText += textDelta;
23174
+ }
23175
+ steps.set(index, step);
23176
+ }
23177
+ }
23178
+ if (latestUsage) {
23179
+ populateInteractionUsageMetrics(metrics, latestUsage);
23180
+ }
23181
+ const output = latestInteraction ? { ...latestInteraction } : {};
23182
+ if (status) {
23183
+ output.status = status;
23184
+ }
23185
+ if (outputText) {
23186
+ output.output_text = outputText;
23187
+ }
23188
+ if (latestUsage) {
23189
+ output.usage = serializeInteractionValue(latestUsage);
23190
+ }
23191
+ const compactSteps = Array.from(steps.values()).sort(
23192
+ (left, right) => Number(left.index ?? 0) - Number(right.index ?? 0)
23193
+ );
23194
+ if (compactSteps.length > 0) {
23195
+ output.steps = compactSteps;
23196
+ }
23197
+ const metadata = {};
23198
+ if (typeof output.id === "string") {
23199
+ metadata.interaction_id = output.id;
23200
+ }
23201
+ if (typeof output.status === "string") {
23202
+ metadata.status = output.status;
23203
+ }
23204
+ return {
23205
+ output,
23206
+ metrics: cleanMetrics3(metrics),
23207
+ ...Object.keys(metadata).length > 0 ? { metadata } : {}
23208
+ };
23209
+ }
23210
+ function extractInteractionUsageFromEvent(event) {
23211
+ const metadata = tryToDict(event.metadata);
23212
+ const metadataUsage = tryToDict(metadata?.usage);
23213
+ if (metadataUsage) {
23214
+ return metadataUsage;
23215
+ }
23216
+ const metadataTotalUsage = tryToDict(metadata?.total_usage);
23217
+ if (metadataTotalUsage) {
23218
+ return metadataTotalUsage;
23219
+ }
23220
+ const interaction = tryToDict(event.interaction);
23221
+ const interactionUsage = tryToDict(interaction?.usage);
23222
+ return interactionUsage ? interactionUsage : void 0;
23223
+ }
23224
+ function compactInteractionStep(step) {
23225
+ const stepDict = tryToDict(step);
23226
+ if (!stepDict) {
23227
+ return {};
23228
+ }
23229
+ const compact = {};
23230
+ for (const key of [
23231
+ "type",
23232
+ "content",
23233
+ "name",
23234
+ "server_name",
23235
+ "arguments",
23236
+ "result",
23237
+ "is_error"
23238
+ ]) {
23239
+ if (stepDict[key] !== void 0) {
23240
+ compact[key] = serializeInteractionValue(stepDict[key]);
23241
+ }
23242
+ }
23243
+ return Object.keys(compact).length > 0 ? compact : serializeInteractionValue(stepDict);
23244
+ }
23245
+ function applyInteractionDelta(step, delta) {
23246
+ const deltaDict = tryToDict(delta);
23247
+ if (!deltaDict) {
23248
+ return void 0;
23249
+ }
23250
+ const deltaType = deltaDict.type;
23251
+ if (typeof deltaType === "string" && typeof step.type !== "string") {
23252
+ step.type = deltaType === "text" ? "model_output" : deltaType;
23253
+ }
23254
+ if (deltaType === "text" && typeof deltaDict.text === "string") {
23255
+ step.text = `${typeof step.text === "string" ? step.text : ""}${deltaDict.text}`;
23256
+ return deltaDict.text;
23257
+ }
23258
+ if (deltaType === "arguments_delta" && typeof deltaDict.arguments === "string") {
23259
+ step.arguments = `${typeof step.arguments === "string" ? step.arguments : ""}${deltaDict.arguments}`;
23260
+ return void 0;
23261
+ }
23262
+ const deltas = Array.isArray(step.deltas) ? step.deltas : [];
23263
+ deltas.push(serializeInteractionValue(deltaDict));
23264
+ step.deltas = deltas;
23265
+ return void 0;
23266
+ }
22107
23267
  function cleanMetrics3(metrics) {
22108
23268
  const cleaned = {};
22109
23269
  for (const [key, value] of Object.entries(metrics)) {
@@ -27930,7 +29090,8 @@ var FlueObserveBridge = class {
27930
29090
  metadata
27931
29091
  }
27932
29092
  });
27933
- this.runsById.set(event.runId, { metadata, span });
29093
+ const activeContext = enterCurrentFlueSpan(span);
29094
+ this.runsById.set(event.runId, { activeContext, metadata, span });
27934
29095
  }
27935
29096
  handleRunEnd(event) {
27936
29097
  const state = this.runsById.get(event.runId);
@@ -27948,6 +29109,7 @@ var FlueObserveBridge = class {
27948
29109
  });
27949
29110
  safeEnd(state.span, eventTime(event.timestamp));
27950
29111
  this.runsById.delete(event.runId);
29112
+ restoreCurrentFlueSpan(state.activeContext);
27951
29113
  }
27952
29114
  void flush().catch((error2) => {
27953
29115
  logInstrumentationError3("Flue flush", error2);
@@ -28074,7 +29236,8 @@ var FlueObserveBridge = class {
28074
29236
  metadata
28075
29237
  }
28076
29238
  });
28077
- this.toolsByKey.set(toolKey(event), { metadata, span });
29239
+ const activeContext = enterCurrentFlueSpan(span);
29240
+ this.toolsByKey.set(toolKey(event), { activeContext, metadata, span });
28078
29241
  }
28079
29242
  handleToolCall(event) {
28080
29243
  if (!event.toolCallId) {
@@ -28097,6 +29260,7 @@ var FlueObserveBridge = class {
28097
29260
  });
28098
29261
  safeEnd(state.span, eventTime(event.timestamp));
28099
29262
  this.toolsByKey.delete(key);
29263
+ restoreCurrentFlueSpan(state.activeContext);
28100
29264
  }
28101
29265
  handleTaskStart(event) {
28102
29266
  if (!event.taskId) {
@@ -28501,11 +29665,39 @@ function stateMatchesRun(state, runId) {
28501
29665
  function startFlueSpan(parent, args) {
28502
29666
  return parent ? withCurrent(parent, () => startSpan(args)) : startSpan(args);
28503
29667
  }
28504
- function safeLog3(span, event) {
29668
+ function enterCurrentFlueSpan(span) {
29669
+ const contextManager = _internalGetGlobalState()?.contextManager;
29670
+ const store = contextManager ? Reflect.get(contextManager, BRAINTRUST_CURRENT_SPAN_STORE) : void 0;
29671
+ if (!contextManager || !isCurrentSpanStore(store)) {
29672
+ return void 0;
29673
+ }
29674
+ const previous = store.getStore();
28505
29675
  try {
28506
- span.log(event);
29676
+ store.enterWith(contextManager.wrapSpanForStore(span));
29677
+ return { previous, store };
28507
29678
  } catch (error2) {
28508
- logInstrumentationError3("Flue span log", error2);
29679
+ logInstrumentationError3("Flue context propagation", error2);
29680
+ return void 0;
29681
+ }
29682
+ }
29683
+ function isCurrentSpanStore(value) {
29684
+ return isObjectLike(value) && typeof Reflect.get(value, "enterWith") === "function" && typeof Reflect.get(value, "getStore") === "function";
29685
+ }
29686
+ function restoreCurrentFlueSpan(activeContext) {
29687
+ if (!activeContext) {
29688
+ return;
29689
+ }
29690
+ try {
29691
+ activeContext.store.enterWith(activeContext.previous);
29692
+ } catch (error2) {
29693
+ logInstrumentationError3("Flue context restoration", error2);
29694
+ }
29695
+ }
29696
+ function safeLog3(span, event) {
29697
+ try {
29698
+ span.log(event);
29699
+ } catch (error2) {
29700
+ logInstrumentationError3("Flue span log", error2);
28509
29701
  }
28510
29702
  }
28511
29703
  function safeEnd(span, endTime) {
@@ -28978,6 +30170,890 @@ function isBraintrustHandler(handler) {
28978
30170
  return Reflect.get(handler, "name") === BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME;
28979
30171
  }
28980
30172
 
30173
+ // src/instrumentation/plugins/pi-coding-agent-channels.ts
30174
+ var piCodingAgentChannels = defineChannels(
30175
+ "@earendil-works/pi-coding-agent",
30176
+ {
30177
+ prompt: channel({
30178
+ channelName: "AgentSession.prompt",
30179
+ kind: "async"
30180
+ })
30181
+ }
30182
+ );
30183
+
30184
+ // src/instrumentation/plugins/pi-coding-agent-plugin.ts
30185
+ var piStreamPatchStates = /* @__PURE__ */ new WeakMap();
30186
+ var piPromptContextStore;
30187
+ var PiCodingAgentPlugin = class extends BasePlugin {
30188
+ activePromptStates = /* @__PURE__ */ new Set();
30189
+ onEnable() {
30190
+ this.subscribeToPrompt();
30191
+ }
30192
+ onDisable() {
30193
+ for (const unsubscribe of this.unsubscribers) {
30194
+ unsubscribe();
30195
+ }
30196
+ this.unsubscribers = [];
30197
+ for (const state of [...this.activePromptStates]) {
30198
+ void finalizePiPromptRun(state).catch((error2) => {
30199
+ logInstrumentationError4("Pi Coding Agent disable cleanup", error2);
30200
+ });
30201
+ }
30202
+ }
30203
+ subscribeToPrompt() {
30204
+ const channel2 = piCodingAgentChannels.prompt.tracingChannel();
30205
+ const states = /* @__PURE__ */ new WeakMap();
30206
+ const unbindAutoInstrumentationSuppression = bindAutoInstrumentationSuppressionToStart(channel2);
30207
+ const handlers = {
30208
+ start: (event) => {
30209
+ const state = startPiPromptRun(event, (state2) => {
30210
+ this.activePromptStates.delete(state2);
30211
+ });
30212
+ if (state) {
30213
+ this.activePromptStates.add(state);
30214
+ states.set(event, state);
30215
+ }
30216
+ },
30217
+ asyncEnd: async (event) => {
30218
+ const state = states.get(event);
30219
+ if (!state) {
30220
+ return;
30221
+ }
30222
+ states.delete(event);
30223
+ state.promptCallEnded = true;
30224
+ if (!state.finalized && state.deferCompletionUntilTurnEnd && !state.turnEnded) {
30225
+ if (!state.sawStreamFn) {
30226
+ state.queued = true;
30227
+ if (!state.streamPatchState.queuedPromptStates.includes(state)) {
30228
+ state.streamPatchState.queuedPromptStates.push(state);
30229
+ }
30230
+ }
30231
+ return;
30232
+ }
30233
+ await finalizePiPromptRun(state);
30234
+ },
30235
+ error: async (event) => {
30236
+ const state = states.get(event);
30237
+ if (!state) {
30238
+ return;
30239
+ }
30240
+ states.delete(event);
30241
+ await finalizePiPromptRun(state, event.error);
30242
+ }
30243
+ };
30244
+ channel2.subscribe(handlers);
30245
+ this.unsubscribers.push(() => {
30246
+ unbindAutoInstrumentationSuppression?.();
30247
+ channel2.unsubscribe(handlers);
30248
+ });
30249
+ }
30250
+ };
30251
+ function startPiPromptRun(event, onFinalize) {
30252
+ const session = extractSession(event);
30253
+ const agent = session?.agent;
30254
+ if (!session || !isPiAgent(agent)) {
30255
+ return void 0;
30256
+ }
30257
+ const metadata = {
30258
+ ...extractSessionMetadata(session),
30259
+ ...extractPromptOptionsMetadata(event.arguments[1]),
30260
+ "pi_coding_agent.operation": "AgentSession.prompt",
30261
+ provider: session.model?.provider ?? agent.state?.model?.provider ?? "pi",
30262
+ ...session.model?.id || agent.state?.model?.id ? { model: session.model?.id ?? agent.state?.model?.id } : {},
30263
+ ...event.moduleVersion ? { "pi_coding_agent.version": event.moduleVersion } : {}
30264
+ };
30265
+ const span = startSpan({
30266
+ event: {
30267
+ input: extractPromptInput(event.arguments[0], event.arguments[1]),
30268
+ metadata
30269
+ },
30270
+ name: "AgentSession.prompt",
30271
+ spanAttributes: { type: "task" /* TASK */ }
30272
+ });
30273
+ const streamPatchState = installPiStreamPatch(agent);
30274
+ const options = event.arguments[1];
30275
+ const promptText = event.arguments[0];
30276
+ const state = {
30277
+ activeLlmSpans: /* @__PURE__ */ new Set(),
30278
+ activeToolSpans: /* @__PURE__ */ new Map(),
30279
+ agent,
30280
+ collectedLlmUsageMetrics: false,
30281
+ deferCompletionUntilTurnEnd: options?.streamingBehavior === "followUp" || options?.streamingBehavior === "steer",
30282
+ finalized: false,
30283
+ metadata,
30284
+ metrics: {},
30285
+ onFinalize,
30286
+ promptCallEnded: false,
30287
+ ...typeof promptText === "string" ? { promptText } : {},
30288
+ queued: false,
30289
+ span,
30290
+ sawStreamFn: false,
30291
+ startTime: getCurrentUnixTimestamp(),
30292
+ streamPatchState,
30293
+ turnEnded: false
30294
+ };
30295
+ state.restorePromptContext = enterPiPromptContext(state);
30296
+ streamPatchState.activePromptStates.add(state);
30297
+ try {
30298
+ state.unsubscribeAgent = agent.subscribe(async (agentEvent) => {
30299
+ try {
30300
+ await runWithAutoInstrumentationSuppressed(
30301
+ () => handlePiAgentEvent(state, agentEvent)
30302
+ );
30303
+ } catch (error2) {
30304
+ logInstrumentationError4("Pi Coding Agent event", error2);
30305
+ }
30306
+ });
30307
+ } catch (error2) {
30308
+ logInstrumentationError4("Pi Coding Agent event subscription", error2);
30309
+ }
30310
+ return state;
30311
+ }
30312
+ function extractSession(event) {
30313
+ const candidate = event.session ?? event.self;
30314
+ return isObject(candidate) && typeof candidate.prompt === "function" ? candidate : void 0;
30315
+ }
30316
+ function isPiAgent(value) {
30317
+ return isObject(value) && typeof value.streamFn === "function" && typeof value.subscribe === "function";
30318
+ }
30319
+ function promptContextStore() {
30320
+ piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
30321
+ return piPromptContextStore;
30322
+ }
30323
+ function currentPromptContextFrames() {
30324
+ return promptContextStore().getStore()?.frames ?? [];
30325
+ }
30326
+ function currentPiPromptState() {
30327
+ const frames = currentPromptContextFrames();
30328
+ return frames[frames.length - 1]?.state;
30329
+ }
30330
+ function enterPiPromptContext(state) {
30331
+ const frame = {
30332
+ id: /* @__PURE__ */ Symbol("braintrust.pi-coding-agent.prompt"),
30333
+ state
30334
+ };
30335
+ promptContextStore().enterWith({
30336
+ frames: [...currentPromptContextFrames(), frame]
30337
+ });
30338
+ return () => {
30339
+ const frames = currentPromptContextFrames().filter(
30340
+ (candidate) => candidate.id !== frame.id
30341
+ );
30342
+ promptContextStore().enterWith(frames.length > 0 ? { frames } : void 0);
30343
+ };
30344
+ }
30345
+ function installPiStreamPatch(agent) {
30346
+ const existing = piStreamPatchStates.get(agent);
30347
+ if (existing) {
30348
+ if (agent.streamFn !== existing.wrappedStreamFn) {
30349
+ debugLogger.debug(
30350
+ "Pi Coding Agent streamFn changed while Braintrust instrumentation was active; preserving existing patch state."
30351
+ );
30352
+ }
30353
+ return existing;
30354
+ }
30355
+ const patchState = {
30356
+ activePromptStates: /* @__PURE__ */ new Set(),
30357
+ agent,
30358
+ originalStreamFn: agent.streamFn,
30359
+ queuedPromptStates: [],
30360
+ wrappedStreamFn: agent.streamFn
30361
+ };
30362
+ patchState.wrappedStreamFn = makeSharedInstrumentedStreamFn(patchState);
30363
+ agent.streamFn = patchState.wrappedStreamFn;
30364
+ piStreamPatchStates.set(agent, patchState);
30365
+ return patchState;
30366
+ }
30367
+ function resolveStreamPromptState(patchState, context2) {
30368
+ let lastUserText;
30369
+ if (Array.isArray(context2.messages)) {
30370
+ for (let i = context2.messages.length - 1; i >= 0; i--) {
30371
+ const message = context2.messages[i];
30372
+ if (isPiUserMessage(message)) {
30373
+ if (typeof message.content === "string") {
30374
+ lastUserText = message.content;
30375
+ } else {
30376
+ lastUserText = message.content.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
30377
+ }
30378
+ break;
30379
+ }
30380
+ }
30381
+ }
30382
+ if (lastUserText !== void 0) {
30383
+ const queuedMatch = patchState.queuedPromptStates.find(
30384
+ (state) => state.promptText === lastUserText
30385
+ );
30386
+ if (queuedMatch) {
30387
+ return queuedMatch;
30388
+ }
30389
+ const matches = [...patchState.activePromptStates].filter(
30390
+ (state) => state.promptText === lastUserText
30391
+ );
30392
+ if (matches.length === 1) {
30393
+ return matches[0];
30394
+ }
30395
+ }
30396
+ const contextState = currentPiPromptState();
30397
+ if (contextState && patchState.activePromptStates.has(contextState) && (!contextState.queued || lastUserText !== void 0 && contextState.promptText === lastUserText)) {
30398
+ return contextState;
30399
+ }
30400
+ if (patchState.activePromptStates.size === 1) {
30401
+ return [...patchState.activePromptStates][0];
30402
+ }
30403
+ return void 0;
30404
+ }
30405
+ function makeSharedInstrumentedStreamFn(patchState) {
30406
+ return async function instrumentedPiStreamFn(model, context2, options) {
30407
+ const state = resolveStreamPromptState(patchState, context2);
30408
+ if (!state) {
30409
+ const invokeOriginal = () => Reflect.apply(patchState.originalStreamFn, this, [
30410
+ model,
30411
+ context2,
30412
+ options
30413
+ ]);
30414
+ return patchState.activePromptStates.size > 0 ? runWithAutoInstrumentationSuppressed(invokeOriginal) : invokeOriginal();
30415
+ }
30416
+ state.sawStreamFn = true;
30417
+ removeQueuedPromptState(state);
30418
+ state.streamPatchState.eventPromptState = state;
30419
+ const llmState = await startPiLlmSpan(state, model, context2, options);
30420
+ try {
30421
+ const stream = await runWithAutoInstrumentationSuppressed(
30422
+ () => Reflect.apply(patchState.originalStreamFn, this, [
30423
+ model,
30424
+ context2,
30425
+ options
30426
+ ])
30427
+ );
30428
+ return patchAssistantMessageStream(stream, state, llmState);
30429
+ } catch (error2) {
30430
+ finishPiLlmSpan(state, llmState, void 0, error2);
30431
+ throw error2;
30432
+ }
30433
+ };
30434
+ }
30435
+ async function startPiLlmSpan(state, model, context2, options) {
30436
+ const metadata = {
30437
+ ...extractModelMetadata2(model),
30438
+ ...extractStreamOptionsMetadata(options),
30439
+ ...extractToolMetadata(context2.tools),
30440
+ "pi_coding_agent.operation": "agent.streamFn"
30441
+ };
30442
+ const span = startSpan({
30443
+ event: {
30444
+ input: processInputAttachments(normalizePiContextInput(context2)),
30445
+ metadata
30446
+ },
30447
+ name: getLlmSpanName(model),
30448
+ parent: await state.span.export(),
30449
+ spanAttributes: { type: "llm" /* LLM */ }
30450
+ });
30451
+ const llmState = {
30452
+ finalized: false,
30453
+ metadata,
30454
+ metrics: {},
30455
+ span,
30456
+ startTime: getCurrentUnixTimestamp()
30457
+ };
30458
+ state.activeLlmSpans.add(llmState);
30459
+ return llmState;
30460
+ }
30461
+ function patchAssistantMessageStream(stream, promptState, llmState) {
30462
+ if (!isObject(stream)) {
30463
+ return stream;
30464
+ }
30465
+ const streamRecord = stream;
30466
+ const originalResult = stream.result;
30467
+ if (typeof originalResult === "function") {
30468
+ streamRecord.result = function patchedPiResult() {
30469
+ return Promise.resolve(Reflect.apply(originalResult, this, [])).then(
30470
+ (message) => {
30471
+ finishPiLlmSpan(promptState, llmState, message);
30472
+ return message;
30473
+ },
30474
+ (error2) => {
30475
+ finishPiLlmSpan(promptState, llmState, void 0, error2);
30476
+ throw error2;
30477
+ }
30478
+ );
30479
+ };
30480
+ }
30481
+ const originalIterator = stream[Symbol.asyncIterator];
30482
+ if (typeof originalIterator === "function") {
30483
+ streamRecord[Symbol.asyncIterator] = function patchedPiIterator() {
30484
+ const iterator = Reflect.apply(
30485
+ originalIterator,
30486
+ this,
30487
+ []
30488
+ );
30489
+ return {
30490
+ async next() {
30491
+ try {
30492
+ const result = await iterator.next();
30493
+ if (result.done) {
30494
+ finishPiLlmSpan(promptState, llmState);
30495
+ return result;
30496
+ }
30497
+ recordPiAssistantMessageEvent(promptState, llmState, result.value);
30498
+ return result;
30499
+ } catch (error2) {
30500
+ finishPiLlmSpan(promptState, llmState, void 0, error2);
30501
+ if (typeof iterator.return === "function") {
30502
+ try {
30503
+ await iterator.return();
30504
+ } catch (cleanupError) {
30505
+ logInstrumentationError4(
30506
+ "Pi Coding Agent stream cleanup",
30507
+ cleanupError
30508
+ );
30509
+ }
30510
+ }
30511
+ throw error2;
30512
+ }
30513
+ },
30514
+ async return(value) {
30515
+ try {
30516
+ if (typeof iterator.return === "function") {
30517
+ return await iterator.return(value);
30518
+ }
30519
+ return {
30520
+ done: true,
30521
+ value
30522
+ };
30523
+ } catch (error2) {
30524
+ finishPiLlmSpan(promptState, llmState, void 0, error2);
30525
+ throw error2;
30526
+ } finally {
30527
+ finishPiLlmSpan(promptState, llmState);
30528
+ }
30529
+ },
30530
+ async throw(error2) {
30531
+ try {
30532
+ if (typeof iterator.throw === "function") {
30533
+ return await iterator.throw(error2);
30534
+ }
30535
+ throw error2;
30536
+ } catch (thrownError) {
30537
+ finishPiLlmSpan(promptState, llmState, void 0, thrownError);
30538
+ throw thrownError;
30539
+ }
30540
+ },
30541
+ [Symbol.asyncIterator]() {
30542
+ return this;
30543
+ }
30544
+ };
30545
+ };
30546
+ }
30547
+ return stream;
30548
+ }
30549
+ function recordPiAssistantMessageEvent(promptState, llmState, event) {
30550
+ recordFirstTokenMetric(llmState, event);
30551
+ const message = "message" in event ? event.message : void 0;
30552
+ const errorMessage2 = "error" in event ? event.error : void 0;
30553
+ if (event.type === "done" && isPiAssistantMessage(message)) {
30554
+ finishPiLlmSpan(promptState, llmState, message);
30555
+ } else if (event.type === "error" && isPiAssistantMessage(errorMessage2)) {
30556
+ finishPiLlmSpan(promptState, llmState, errorMessage2);
30557
+ }
30558
+ }
30559
+ function recordFirstTokenMetric(state, event) {
30560
+ if (state.metrics.time_to_first_token !== void 0 || event.type === "start") {
30561
+ return;
30562
+ }
30563
+ state.metrics.time_to_first_token = getCurrentUnixTimestamp() - state.startTime;
30564
+ }
30565
+ async function handlePiAgentEvent(state, event) {
30566
+ if (state.finalized) {
30567
+ return;
30568
+ }
30569
+ const eventPromptState = state.streamPatchState.eventPromptState;
30570
+ if (eventPromptState && eventPromptState !== state) {
30571
+ return;
30572
+ }
30573
+ if (!eventPromptState && (state.queued || state.streamPatchState.activePromptStates.size > 1 && currentPiPromptState() !== state)) {
30574
+ return;
30575
+ }
30576
+ switch (event.type) {
30577
+ case "message_end":
30578
+ if (isPiAssistantMessage(event.message)) {
30579
+ state.output = extractAssistantOutput(event.message);
30580
+ }
30581
+ return;
30582
+ case "turn_end":
30583
+ state.turnEnded = true;
30584
+ if (isPiAssistantMessage(event.message)) {
30585
+ state.output = extractAssistantOutput(event.message);
30586
+ if (!state.collectedLlmUsageMetrics) {
30587
+ addMetrics(state.metrics, extractUsageMetrics2(event.message.usage));
30588
+ }
30589
+ }
30590
+ if (state.streamPatchState.eventPromptState === state) {
30591
+ state.streamPatchState.eventPromptState = void 0;
30592
+ }
30593
+ if (state.promptCallEnded && state.deferCompletionUntilTurnEnd) {
30594
+ await finalizePiPromptRun(state);
30595
+ }
30596
+ return;
30597
+ case "tool_execution_start":
30598
+ await startPiToolSpan(state, event);
30599
+ return;
30600
+ case "tool_execution_end":
30601
+ finishPiToolSpan(state, event);
30602
+ return;
30603
+ default:
30604
+ return;
30605
+ }
30606
+ }
30607
+ async function startPiToolSpan(state, event) {
30608
+ if (!event.toolCallId || state.activeToolSpans.has(event.toolCallId)) {
30609
+ return;
30610
+ }
30611
+ const restoreAutoInstrumentation = enterAutoInstrumentationAllowed();
30612
+ const metadata = {
30613
+ "gen_ai.tool.call.id": event.toolCallId,
30614
+ "gen_ai.tool.name": event.toolName,
30615
+ "pi_coding_agent.tool.name": event.toolName
30616
+ };
30617
+ try {
30618
+ const span = startSpan({
30619
+ event: {
30620
+ input: event.args,
30621
+ metadata
30622
+ },
30623
+ name: event.toolName || "tool",
30624
+ parent: await state.span.export(),
30625
+ spanAttributes: { type: "tool" /* TOOL */ }
30626
+ });
30627
+ state.activeToolSpans.set(event.toolCallId, {
30628
+ restoreAutoInstrumentation,
30629
+ span
30630
+ });
30631
+ } catch (error2) {
30632
+ restoreAutoInstrumentation();
30633
+ throw error2;
30634
+ }
30635
+ }
30636
+ function finishPiToolSpan(state, event) {
30637
+ const toolState = state.activeToolSpans.get(event.toolCallId);
30638
+ if (!toolState) {
30639
+ return;
30640
+ }
30641
+ state.activeToolSpans.delete(event.toolCallId);
30642
+ const metadata = {
30643
+ "gen_ai.tool.call.id": event.toolCallId,
30644
+ "gen_ai.tool.name": event.toolName,
30645
+ "pi_coding_agent.tool.name": event.toolName,
30646
+ "pi_coding_agent.tool.is_error": event.isError
30647
+ };
30648
+ try {
30649
+ safeLog4(toolState.span, {
30650
+ ...event.isError ? { error: stringifyUnknown2(event.result) } : {},
30651
+ metadata,
30652
+ output: event.result
30653
+ });
30654
+ } finally {
30655
+ try {
30656
+ toolState.span.end();
30657
+ } finally {
30658
+ toolState.restoreAutoInstrumentation?.();
30659
+ }
30660
+ }
30661
+ }
30662
+ async function finalizePiPromptRun(state, error2) {
30663
+ if (state.finalized) {
30664
+ return;
30665
+ }
30666
+ state.finalized = true;
30667
+ state.onFinalize?.(state);
30668
+ restorePiStreamFn(state);
30669
+ try {
30670
+ state.unsubscribeAgent?.();
30671
+ } catch (unsubscribeError) {
30672
+ logInstrumentationError4("Pi Coding Agent unsubscribe", unsubscribeError);
30673
+ }
30674
+ await finishOpenLlmSpans(state, error2);
30675
+ finishOpenToolSpans(state, error2);
30676
+ const metadata = {
30677
+ ...state.metadata,
30678
+ ...extractModelMetadata2(state.agent.state?.model)
30679
+ };
30680
+ try {
30681
+ safeLog4(state.span, {
30682
+ ...error2 ? { error: stringifyUnknown2(error2) } : {},
30683
+ metadata,
30684
+ metrics: {
30685
+ ...cleanMetrics5(state.metrics),
30686
+ ...buildDurationMetrics3(state.startTime)
30687
+ },
30688
+ output: state.output
30689
+ });
30690
+ } finally {
30691
+ state.span.end();
30692
+ }
30693
+ }
30694
+ function restorePiStreamFn(state) {
30695
+ const patchState = state.streamPatchState;
30696
+ patchState.activePromptStates.delete(state);
30697
+ removeQueuedPromptState(state);
30698
+ if (patchState.eventPromptState === state) {
30699
+ patchState.eventPromptState = void 0;
30700
+ }
30701
+ state.restorePromptContext?.();
30702
+ if (patchState.activePromptStates.size > 0) {
30703
+ return;
30704
+ }
30705
+ if (patchState.agent.streamFn === patchState.wrappedStreamFn) {
30706
+ patchState.agent.streamFn = patchState.originalStreamFn;
30707
+ }
30708
+ piStreamPatchStates.delete(patchState.agent);
30709
+ }
30710
+ function removeQueuedPromptState(state) {
30711
+ state.queued = false;
30712
+ const queuedPromptStates = state.streamPatchState.queuedPromptStates;
30713
+ const index = queuedPromptStates.indexOf(state);
30714
+ if (index >= 0) {
30715
+ queuedPromptStates.splice(index, 1);
30716
+ }
30717
+ }
30718
+ async function finishOpenLlmSpans(state, error2) {
30719
+ for (const llmState of [...state.activeLlmSpans]) {
30720
+ finishPiLlmSpan(state, llmState, void 0, error2);
30721
+ }
30722
+ }
30723
+ function finishPiLlmSpan(promptState, llmState, message, error2) {
30724
+ if (llmState.finalized) {
30725
+ return;
30726
+ }
30727
+ llmState.finalized = true;
30728
+ promptState.activeLlmSpans.delete(llmState);
30729
+ const messageError = message?.stopReason === "error" && message.errorMessage;
30730
+ const metrics = {
30731
+ ...extractUsageMetrics2(message?.usage),
30732
+ ...cleanMetrics5(llmState.metrics),
30733
+ ...buildDurationMetrics3(llmState.startTime)
30734
+ };
30735
+ const usageMetrics = extractUsageMetrics2(message?.usage);
30736
+ if (Object.keys(usageMetrics).length > 0) {
30737
+ promptState.collectedLlmUsageMetrics = true;
30738
+ addMetrics(promptState.metrics, usageMetrics);
30739
+ }
30740
+ try {
30741
+ safeLog4(llmState.span, {
30742
+ ...error2 || messageError ? { error: stringifyUnknown2(error2 ?? messageError) } : {},
30743
+ metadata: {
30744
+ ...llmState.metadata,
30745
+ ...message ? extractAssistantMetadata(message) : {}
30746
+ },
30747
+ metrics,
30748
+ ...message ? { output: extractAssistantOutput(message) } : {}
30749
+ });
30750
+ } finally {
30751
+ llmState.span.end();
30752
+ }
30753
+ }
30754
+ function finishOpenToolSpans(state, error2) {
30755
+ for (const [, toolState] of state.activeToolSpans) {
30756
+ try {
30757
+ safeLog4(toolState.span, {
30758
+ error: error2 ? stringifyUnknown2(error2) : "Pi tool did not complete"
30759
+ });
30760
+ toolState.span.end();
30761
+ } finally {
30762
+ toolState.restoreAutoInstrumentation?.();
30763
+ }
30764
+ }
30765
+ state.activeToolSpans.clear();
30766
+ }
30767
+ function normalizePiContextInput(context2) {
30768
+ const messages = context2.messages.flatMap(
30769
+ (message) => normalizePiMessage(message)
30770
+ );
30771
+ if (context2.systemPrompt) {
30772
+ return [{ role: "system", content: context2.systemPrompt }, ...messages];
30773
+ }
30774
+ return messages;
30775
+ }
30776
+ function normalizePiMessage(message) {
30777
+ if (isPiUserMessage(message)) {
30778
+ return [
30779
+ {
30780
+ role: "user",
30781
+ content: normalizeUserContent(message.content)
30782
+ }
30783
+ ];
30784
+ }
30785
+ if (isPiAssistantMessage(message)) {
30786
+ return [normalizeAssistantMessage(message)];
30787
+ }
30788
+ if (isPiToolResultMessage(message)) {
30789
+ return [normalizeToolResultMessage(message)];
30790
+ }
30791
+ return [];
30792
+ }
30793
+ function normalizeAssistantMessage(message) {
30794
+ const text = message.content.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
30795
+ const thinking = message.content.flatMap(
30796
+ (part) => part.type === "thinking" && !part.redacted ? [part.thinking] : []
30797
+ ).join("");
30798
+ const toolCalls = message.content.flatMap(
30799
+ (part) => part.type === "toolCall" ? [normalizeToolCall(part)] : []
30800
+ );
30801
+ return {
30802
+ role: "assistant",
30803
+ content: text || (toolCalls.length > 0 ? null : ""),
30804
+ ...thinking ? { reasoning: thinking } : {},
30805
+ ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
30806
+ };
30807
+ }
30808
+ function normalizeToolResultMessage(message) {
30809
+ return {
30810
+ role: "tool",
30811
+ tool_call_id: message.toolCallId,
30812
+ content: normalizeUserContent(message.content)
30813
+ };
30814
+ }
30815
+ function normalizeUserContent(content) {
30816
+ if (typeof content === "string") {
30817
+ return content;
30818
+ }
30819
+ return content.map((part) => {
30820
+ if (part.type === "text") {
30821
+ return { type: "text", text: part.text };
30822
+ }
30823
+ if (part.type === "image") {
30824
+ return {
30825
+ type: "image_url",
30826
+ image_url: {
30827
+ url: `data:${part.mimeType};base64,${part.data}`
30828
+ }
30829
+ };
30830
+ }
30831
+ return part;
30832
+ });
30833
+ }
30834
+ function normalizeToolCall(toolCall) {
30835
+ return {
30836
+ id: toolCall.id,
30837
+ type: "function",
30838
+ function: {
30839
+ name: toolCall.name,
30840
+ arguments: stringifyArguments(toolCall.arguments)
30841
+ }
30842
+ };
30843
+ }
30844
+ function extractAssistantOutput(message) {
30845
+ return processInputAttachments([
30846
+ {
30847
+ finish_reason: normalizeStopReason(message.stopReason),
30848
+ index: 0,
30849
+ message: normalizeAssistantMessage(message)
30850
+ }
30851
+ ]);
30852
+ }
30853
+ function isPiUserMessage(message) {
30854
+ return message.role === "user" && "content" in message;
30855
+ }
30856
+ function isPiAssistantMessage(message) {
30857
+ return isObject(message) && message.role === "assistant" && Array.isArray(message.content);
30858
+ }
30859
+ function isPiToolResultMessage(message) {
30860
+ return message.role === "toolResult" && Array.isArray(message.content);
30861
+ }
30862
+ function normalizeStopReason(reason) {
30863
+ switch (reason) {
30864
+ case "toolUse":
30865
+ return "tool_calls";
30866
+ case "length":
30867
+ case "stop":
30868
+ return reason;
30869
+ default:
30870
+ return reason ?? "stop";
30871
+ }
30872
+ }
30873
+ function extractPromptInput(text, options) {
30874
+ const images = options?.images;
30875
+ if (!images || images.length === 0) {
30876
+ return text;
30877
+ }
30878
+ return processInputAttachments([
30879
+ {
30880
+ role: "user",
30881
+ content: [
30882
+ { type: "text", text: text ?? "" },
30883
+ ...images.map((image) => ({
30884
+ type: "image_url",
30885
+ image_url: {
30886
+ url: `data:${image.mimeType};base64,${image.data}`
30887
+ }
30888
+ }))
30889
+ ]
30890
+ }
30891
+ ]);
30892
+ }
30893
+ function extractToolMetadata(tools) {
30894
+ if (!tools || tools.length === 0) {
30895
+ return {};
30896
+ }
30897
+ return {
30898
+ tools: tools.map((tool) => ({
30899
+ type: "function",
30900
+ function: {
30901
+ name: tool.name,
30902
+ ...tool.description ? { description: tool.description } : {},
30903
+ ...tool.parameters ? { parameters: tool.parameters } : {}
30904
+ }
30905
+ }))
30906
+ };
30907
+ }
30908
+ function extractModelMetadata2(model) {
30909
+ if (!model) {
30910
+ return {};
30911
+ }
30912
+ return {
30913
+ ...model.provider ? { provider: model.provider } : {},
30914
+ ...model.id ? { model: model.id, "pi_coding_agent.model": model.id } : {},
30915
+ ...model.api ? { "pi_coding_agent.api": model.api } : {},
30916
+ ...model.name ? { "pi_coding_agent.model_name": model.name } : {}
30917
+ };
30918
+ }
30919
+ function extractAssistantMetadata(message) {
30920
+ return {
30921
+ ...message.provider ? { provider: message.provider } : {},
30922
+ ...message.responseModel || message.model ? { model: message.responseModel ?? message.model } : {},
30923
+ ...message.api ? { "pi_coding_agent.api": message.api } : {},
30924
+ ...message.model ? { "pi_coding_agent.model": message.model } : {},
30925
+ ...message.responseModel ? { "pi_coding_agent.response_model": message.responseModel } : {},
30926
+ ...message.responseId ? { "pi_coding_agent.response_id": message.responseId } : {},
30927
+ ...message.stopReason ? { "pi_coding_agent.stop_reason": message.stopReason } : {}
30928
+ };
30929
+ }
30930
+ function extractSessionMetadata(session) {
30931
+ return {
30932
+ ...extractModelMetadata2(session.model),
30933
+ ...session.sessionId ? { "pi_coding_agent.session_id": session.sessionId } : {},
30934
+ ...session.sessionName ? { "pi_coding_agent.session_name": session.sessionName } : {},
30935
+ ...session.thinkingLevel ? { "pi_coding_agent.thinking_level": session.thinkingLevel } : {},
30936
+ ...typeof session.getActiveToolNames === "function" ? { "pi_coding_agent.active_tools": session.getActiveToolNames() } : {}
30937
+ };
30938
+ }
30939
+ function extractPromptOptionsMetadata(options) {
30940
+ if (!options) {
30941
+ return {};
30942
+ }
30943
+ return {
30944
+ ...options.source ? { "pi_coding_agent.source": options.source } : {},
30945
+ ...options.streamingBehavior ? { "pi_coding_agent.streaming_behavior": options.streamingBehavior } : {},
30946
+ ...options.expandPromptTemplates !== void 0 ? {
30947
+ "pi_coding_agent.expand_prompt_templates": options.expandPromptTemplates
30948
+ } : {}
30949
+ };
30950
+ }
30951
+ function extractStreamOptionsMetadata(options) {
30952
+ if (!options) {
30953
+ return {};
30954
+ }
30955
+ return {
30956
+ ...options.temperature !== void 0 ? { temperature: options.temperature } : {},
30957
+ ...options.maxTokens !== void 0 ? { max_tokens: options.maxTokens } : {},
30958
+ ...options.reasoning ? { "pi_coding_agent.reasoning": options.reasoning } : {},
30959
+ ...options.transport ? { "pi_coding_agent.transport": options.transport } : {},
30960
+ ...options.cacheRetention ? { "pi_coding_agent.cache_retention": options.cacheRetention } : {},
30961
+ ...options.sessionId ? { "pi_coding_agent.session_id": options.sessionId } : {},
30962
+ ...options.timeoutMs !== void 0 ? { "pi_coding_agent.timeout_ms": options.timeoutMs } : {},
30963
+ ...options.maxRetries !== void 0 ? { "pi_coding_agent.max_retries": options.maxRetries } : {},
30964
+ ...options.maxRetryDelayMs !== void 0 ? { "pi_coding_agent.max_retry_delay_ms": options.maxRetryDelayMs } : {},
30965
+ ...options.metadata ? { "pi_coding_agent.metadata": options.metadata } : {}
30966
+ };
30967
+ }
30968
+ function getLlmSpanName(model) {
30969
+ switch (model.api) {
30970
+ case "anthropic-messages":
30971
+ return "anthropic.messages.create";
30972
+ case "openai-completions":
30973
+ return "Chat Completion";
30974
+ case "openai-responses":
30975
+ case "azure-openai-responses":
30976
+ case "openai-codex-responses":
30977
+ return "openai.responses.create";
30978
+ case "google-generative-ai":
30979
+ case "google-vertex":
30980
+ return "generate_content";
30981
+ case "mistral-conversations":
30982
+ return "mistral.chat.stream";
30983
+ case "bedrock-converse-stream":
30984
+ return "bedrock.converse_stream";
30985
+ default:
30986
+ return "pi_ai.streamSimple";
30987
+ }
30988
+ }
30989
+ function extractUsageMetrics2(usage) {
30990
+ if (!usage) {
30991
+ return {};
30992
+ }
30993
+ return cleanMetrics5({
30994
+ completion_tokens: usage.output,
30995
+ prompt_cache_creation_tokens: usage.cacheWrite,
30996
+ prompt_cached_tokens: usage.cacheRead,
30997
+ prompt_tokens: usage.input,
30998
+ tokens: usage.totalTokens ?? usage.tokens
30999
+ });
31000
+ }
31001
+ function addMetrics(target, source) {
31002
+ for (const [key, value] of Object.entries(source)) {
31003
+ target[key] = (target[key] ?? 0) + value;
31004
+ }
31005
+ }
31006
+ function buildDurationMetrics3(startTime) {
31007
+ const end = getCurrentUnixTimestamp();
31008
+ return {
31009
+ duration: end - startTime,
31010
+ end,
31011
+ start: startTime
31012
+ };
31013
+ }
31014
+ function cleanMetrics5(metrics) {
31015
+ const cleaned = {};
31016
+ for (const [key, value] of Object.entries(metrics)) {
31017
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
31018
+ cleaned[key] = value;
31019
+ }
31020
+ }
31021
+ return cleaned;
31022
+ }
31023
+ function stringifyArguments(value) {
31024
+ if (typeof value === "string") {
31025
+ return value;
31026
+ }
31027
+ try {
31028
+ return JSON.stringify(value);
31029
+ } catch {
31030
+ return stringifyUnknown2(value);
31031
+ }
31032
+ }
31033
+ function stringifyUnknown2(value) {
31034
+ if (value instanceof Error) {
31035
+ return value.message;
31036
+ }
31037
+ if (typeof value === "string") {
31038
+ return value;
31039
+ }
31040
+ try {
31041
+ return JSON.stringify(value);
31042
+ } catch {
31043
+ return String(value);
31044
+ }
31045
+ }
31046
+ function safeLog4(span, event) {
31047
+ try {
31048
+ span.log(event);
31049
+ } catch (error2) {
31050
+ logInstrumentationError4("Pi Coding Agent span log", error2);
31051
+ }
31052
+ }
31053
+ function logInstrumentationError4(context2, error2) {
31054
+ debugLogger.debug(`${context2}:`, error2);
31055
+ }
31056
+
28981
31057
  // src/instrumentation/braintrust-plugin.ts
28982
31058
  function getIntegrationConfig(integrations, key) {
28983
31059
  return integrations[key];
@@ -29003,6 +31079,7 @@ var BraintrustPlugin = class extends BasePlugin {
29003
31079
  gitHubCopilotPlugin = null;
29004
31080
  fluePlugin = null;
29005
31081
  langChainPlugin = null;
31082
+ piCodingAgentPlugin = null;
29006
31083
  constructor(config3 = {}) {
29007
31084
  super();
29008
31085
  this.config = config3;
@@ -29077,6 +31154,10 @@ var BraintrustPlugin = class extends BasePlugin {
29077
31154
  this.gitHubCopilotPlugin = new GitHubCopilotPlugin();
29078
31155
  this.gitHubCopilotPlugin.enable();
29079
31156
  }
31157
+ if (integrations.piCodingAgent !== false) {
31158
+ this.piCodingAgentPlugin = new PiCodingAgentPlugin();
31159
+ this.piCodingAgentPlugin.enable();
31160
+ }
29080
31161
  if (getIntegrationConfig(integrations, "flue") !== false) {
29081
31162
  this.fluePlugin = new FluePlugin();
29082
31163
  this.fluePlugin.enable();
@@ -29155,6 +31236,10 @@ var BraintrustPlugin = class extends BasePlugin {
29155
31236
  this.gitHubCopilotPlugin.disable();
29156
31237
  this.gitHubCopilotPlugin = null;
29157
31238
  }
31239
+ if (this.piCodingAgentPlugin) {
31240
+ this.piCodingAgentPlugin.disable();
31241
+ this.piCodingAgentPlugin = null;
31242
+ }
29158
31243
  if (this.fluePlugin) {
29159
31244
  this.fluePlugin.disable();
29160
31245
  this.fluePlugin = null;
@@ -29174,6 +31259,11 @@ var envIntegrationAliases = {
29174
31259
  openaicodexsdk: "openaiCodexSDK",
29175
31260
  codex: "openaiCodexSDK",
29176
31261
  "codex-sdk": "openaiCodexSDK",
31262
+ "pi-coding-agent": "piCodingAgent",
31263
+ "pi-coding-agent-sdk": "piCodingAgent",
31264
+ picodingagent: "piCodingAgent",
31265
+ picodingagentsdk: "piCodingAgent",
31266
+ "@earendil-works/pi-coding-agent": "piCodingAgent",
29177
31267
  anthropic: "anthropic",
29178
31268
  aisdk: "aisdk",
29179
31269
  "ai-sdk": "aisdk",
@@ -29239,7 +31329,8 @@ function getDefaultInstrumentationIntegrations() {
29239
31329
  genkit: true,
29240
31330
  gitHubCopilot: true,
29241
31331
  langchain: true,
29242
- langgraph: true
31332
+ langgraph: true,
31333
+ piCodingAgent: true
29243
31334
  };
29244
31335
  }
29245
31336
  function readDisabledInstrumentationEnvConfig(disabledList) {
@@ -29970,6 +32061,7 @@ var Project2 = class {
29970
32061
  prompts;
29971
32062
  parameters;
29972
32063
  scorers;
32064
+ classifiers;
29973
32065
  _publishableCodeFunctions = [];
29974
32066
  _publishablePrompts = [];
29975
32067
  _publishableParameters = [];
@@ -29981,6 +32073,7 @@ var Project2 = class {
29981
32073
  this.prompts = new PromptBuilder(this);
29982
32074
  this.parameters = new ParametersBuilder(this);
29983
32075
  this.scorers = new ScorerBuilder(this);
32076
+ this.classifiers = new ClassifierBuilder(this);
29984
32077
  }
29985
32078
  addPrompt(prompt) {
29986
32079
  this._publishablePrompts.push(prompt);
@@ -30115,6 +32208,28 @@ var ScorerBuilder = class {
30115
32208
  }
30116
32209
  }
30117
32210
  };
32211
+ var ClassifierBuilder = class {
32212
+ constructor(project) {
32213
+ this.project = project;
32214
+ }
32215
+ taskCounter = 0;
32216
+ create(opts) {
32217
+ this.taskCounter++;
32218
+ let resolvedName = opts.name ?? opts.handler.name;
32219
+ if (!resolvedName || resolvedName.trim().length === 0) {
32220
+ resolvedName = `Classifier ${isomorph_default.basename(currentFilename)} ${this.taskCounter}`;
32221
+ }
32222
+ const slug = opts.slug ?? slugify(resolvedName, { lower: true, strict: true });
32223
+ const classifier = new CodeFunction(this.project, {
32224
+ ...opts,
32225
+ name: resolvedName,
32226
+ slug,
32227
+ type: "classifier"
32228
+ });
32229
+ this.project.addCodeFunction(classifier);
32230
+ return classifier;
32231
+ }
32232
+ };
30118
32233
  var CodeFunction = class {
30119
32234
  constructor(project, opts) {
30120
32235
  this.project = project;
@@ -31445,7 +33560,7 @@ var serializedParametersContainerSchema = import_v315.z.union([
31445
33560
  staticParametersSchema
31446
33561
  ]);
31447
33562
  var evaluatorDefinitionSchema = import_v315.z.object({
31448
- parameters: serializedParametersContainerSchema.optional(),
33563
+ parameters: serializedParametersContainerSchema.nullish(),
31449
33564
  scores: import_v315.z.array(import_v315.z.object({ name: import_v315.z.string() })).optional(),
31450
33565
  classifiers: import_v315.z.array(import_v315.z.object({ name: import_v315.z.string() })).optional()
31451
33566
  });