braintrust 3.11.0 → 3.13.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 (77) hide show
  1. package/README.md +8 -8
  2. package/dev/dist/index.d.mts +26 -7
  3. package/dev/dist/index.d.ts +26 -7
  4. package/dev/dist/index.js +2717 -335
  5. package/dev/dist/index.mjs +2499 -117
  6. package/dist/apply-auto-instrumentation.browser.d.mts +2 -0
  7. package/dist/apply-auto-instrumentation.browser.d.ts +2 -0
  8. package/dist/apply-auto-instrumentation.browser.js +18 -0
  9. package/dist/apply-auto-instrumentation.browser.mjs +0 -0
  10. package/dist/apply-auto-instrumentation.d.mts +2 -0
  11. package/dist/apply-auto-instrumentation.d.ts +2 -0
  12. package/dist/apply-auto-instrumentation.js +2534 -0
  13. package/dist/apply-auto-instrumentation.mjs +2534 -0
  14. package/dist/auto-instrumentations/bundler/esbuild.cjs +1803 -1283
  15. package/dist/auto-instrumentations/bundler/esbuild.d.mts +9 -5
  16. package/dist/auto-instrumentations/bundler/esbuild.d.ts +9 -5
  17. package/dist/auto-instrumentations/bundler/esbuild.mjs +10 -2
  18. package/dist/auto-instrumentations/bundler/next.cjs +3269 -0
  19. package/dist/auto-instrumentations/bundler/next.d.mts +3 -0
  20. package/dist/auto-instrumentations/bundler/next.d.ts +3 -0
  21. package/dist/auto-instrumentations/bundler/next.mjs +189 -0
  22. package/dist/auto-instrumentations/bundler/rollup.cjs +1803 -1283
  23. package/dist/auto-instrumentations/bundler/rollup.d.mts +9 -5
  24. package/dist/auto-instrumentations/bundler/rollup.d.ts +9 -5
  25. package/dist/auto-instrumentations/bundler/rollup.mjs +10 -2
  26. package/dist/auto-instrumentations/bundler/vite.cjs +1803 -1283
  27. package/dist/auto-instrumentations/bundler/vite.d.mts +9 -5
  28. package/dist/auto-instrumentations/bundler/vite.d.ts +9 -5
  29. package/dist/auto-instrumentations/bundler/vite.mjs +10 -2
  30. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +1861 -1308
  31. package/dist/auto-instrumentations/bundler/webpack-loader.d.ts +3 -3
  32. package/dist/auto-instrumentations/bundler/webpack.cjs +1803 -1283
  33. package/dist/auto-instrumentations/bundler/webpack.d.mts +9 -5
  34. package/dist/auto-instrumentations/bundler/webpack.d.ts +9 -5
  35. package/dist/auto-instrumentations/bundler/webpack.mjs +6 -6
  36. package/dist/auto-instrumentations/{chunk-DIV5TO4S.mjs → chunk-E5DUYJWK.mjs} +338 -1
  37. package/dist/auto-instrumentations/chunk-GJOO4ESL.mjs +300 -0
  38. package/dist/auto-instrumentations/chunk-WFEUJACP.mjs +18 -0
  39. package/dist/auto-instrumentations/hook.mjs +1713 -1460
  40. package/dist/auto-instrumentations/index.cjs +94 -0
  41. package/dist/auto-instrumentations/index.d.mts +5 -1
  42. package/dist/auto-instrumentations/index.d.ts +5 -1
  43. package/dist/auto-instrumentations/index.mjs +6 -247
  44. package/dist/auto-instrumentations/loader/esm-hook.mjs +19 -2
  45. package/dist/auto-instrumentations/plugin-D7nDswtC.d.mts +44 -0
  46. package/dist/auto-instrumentations/plugin-D7nDswtC.d.ts +44 -0
  47. package/dist/browser.d.mts +264 -47
  48. package/dist/browser.d.ts +264 -47
  49. package/dist/browser.js +2521 -159
  50. package/dist/browser.mjs +2521 -159
  51. package/dist/chunk-26JGOELH.js +817 -0
  52. package/dist/chunk-75IQCUB2.mjs +817 -0
  53. package/dist/cli.js +2510 -122
  54. package/dist/edge-light.d.mts +1 -1
  55. package/dist/edge-light.d.ts +1 -1
  56. package/dist/edge-light.js +2521 -159
  57. package/dist/edge-light.mjs +2521 -159
  58. package/dist/index.d.mts +264 -47
  59. package/dist/index.d.ts +264 -47
  60. package/dist/index.js +3498 -1850
  61. package/dist/index.mjs +2635 -987
  62. package/dist/instrumentation/index.d.mts +7897 -48
  63. package/dist/instrumentation/index.d.ts +7897 -48
  64. package/dist/instrumentation/index.js +2408 -95
  65. package/dist/instrumentation/index.mjs +2407 -95
  66. package/dist/workerd.d.mts +1 -1
  67. package/dist/workerd.d.ts +1 -1
  68. package/dist/workerd.js +2521 -159
  69. package/dist/workerd.mjs +2521 -159
  70. package/package.json +23 -17
  71. package/util/dist/index.d.mts +3 -1
  72. package/util/dist/index.d.ts +3 -1
  73. package/util/dist/index.js +6 -0
  74. package/util/dist/index.mjs +6 -0
  75. package/dist/auto-instrumentations/chunk-G6ZWXGZB.mjs +0 -116
  76. package/dist/auto-instrumentations/plugin-Df3qKIl2.d.mts +0 -22
  77. package/dist/auto-instrumentations/plugin-Df3qKIl2.d.ts +0 -22
@@ -32,6 +32,7 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  BasePlugin: () => BasePlugin,
34
34
  BraintrustPlugin: () => BraintrustPlugin,
35
+ OpenAIAgentsTraceProcessor: () => OpenAIAgentsTraceProcessor,
35
36
  configureInstrumentation: () => configureInstrumentation,
36
37
  createChannelName: () => createChannelName,
37
38
  isValidChannelName: () => isValidChannelName,
@@ -124,6 +125,7 @@ var iso = {
124
125
  getRepoInfo: async (_settings) => void 0,
125
126
  getPastNAncestors: async () => [],
126
127
  getEnv: (_name) => void 0,
128
+ getBraintrustApiKey: async () => void 0,
127
129
  getCallerLocation: () => void 0,
128
130
  newAsyncLocalStorage: () => new DefaultAsyncLocalStorage(),
129
131
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -1167,6 +1169,11 @@ function isPromiseLike(value) {
1167
1169
 
1168
1170
  // util/object_util.ts
1169
1171
  var SET_UNION_FIELDS = /* @__PURE__ */ new Set(["tags"]);
1172
+ var FORBIDDEN_MERGE_KEYS = /* @__PURE__ */ new Set([
1173
+ "__proto__",
1174
+ "constructor",
1175
+ "prototype"
1176
+ ]);
1170
1177
  function mergeDictsWithPaths({
1171
1178
  mergeInto,
1172
1179
  mergeFrom,
@@ -1189,6 +1196,7 @@ function mergeDictsWithPathsHelper({
1189
1196
  mergePaths
1190
1197
  }) {
1191
1198
  Object.entries(mergeFrom).forEach(([k, mergeFromV]) => {
1199
+ if (FORBIDDEN_MERGE_KEYS.has(k)) return;
1192
1200
  const fullPath = path.concat([k]);
1193
1201
  const fullPathSerialized = JSON.stringify(fullPath);
1194
1202
  const mergeIntoV = recordFind(mergeInto, k);
@@ -5058,6 +5066,13 @@ var HTTPConnection = class _HTTPConnection {
5058
5066
  debugLogger.debug(
5059
5067
  `Retrying API request ${object_type} ${JSON.stringify(args)} ${e.status} ${e.text}`
5060
5068
  );
5069
+ const sleepTimeS = HTTP_RETRY_BASE_SLEEP_TIME_S * 2 ** i;
5070
+ debugLogger.info(
5071
+ `Sleeping for ${sleepTimeS}s before retrying API request`
5072
+ );
5073
+ await new Promise(
5074
+ (resolve) => setTimeout(resolve, sleepTimeS * 1e3)
5075
+ );
5061
5076
  continue;
5062
5077
  }
5063
5078
  throw e;
@@ -5580,20 +5595,7 @@ function startSpanParentArgs(args) {
5580
5595
  `Mismatch between expected span parent object type ${args.parentObjectType} and provided type ${parentComponents.data.object_type}`
5581
5596
  );
5582
5597
  }
5583
- const parentComponentsObjectIdLambda = spanComponentsToObjectIdLambda(
5584
- args.state,
5585
- parentComponents
5586
- );
5587
- const computeParentObjectId = async () => {
5588
- const parentComponentsObjectId = await parentComponentsObjectIdLambda();
5589
- if (await args.parentObjectId.get() !== parentComponentsObjectId) {
5590
- throw new Error(
5591
- `Mismatch between expected span parent object id ${await args.parentObjectId.get()} and provided id ${parentComponentsObjectId}`
5592
- );
5593
- }
5594
- return await args.parentObjectId.get();
5595
- };
5596
- argParentObjectId = new LazyValue(computeParentObjectId);
5598
+ argParentObjectId = args.parentObjectId;
5597
5599
  if (parentComponents.data.row_id) {
5598
5600
  argParentSpanIds = {
5599
5601
  spanId: parentComponents.data.span_id,
@@ -5922,6 +5924,7 @@ function now() {
5922
5924
  }
5923
5925
  var DEFAULT_FLUSH_BACKPRESSURE_BYTES = 10 * 1024 * 1024;
5924
5926
  var BACKGROUND_LOGGER_BASE_SLEEP_TIME_S = 1;
5927
+ var HTTP_RETRY_BASE_SLEEP_TIME_S = 1;
5925
5928
  var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
5926
5929
  apiConn;
5927
5930
  queue;
@@ -6494,13 +6497,63 @@ async function computeLoggerMetadata(state, {
6494
6497
  };
6495
6498
  }
6496
6499
  }
6500
+ function initLogger(options = {}) {
6501
+ const {
6502
+ projectName,
6503
+ projectId,
6504
+ asyncFlush: asyncFlushArg,
6505
+ appUrl,
6506
+ apiKey,
6507
+ orgName,
6508
+ forceLogin,
6509
+ debugLogLevel,
6510
+ fetch: fetch2,
6511
+ state: stateArg
6512
+ } = options || {};
6513
+ const asyncFlush = asyncFlushArg === void 0 ? true : asyncFlushArg;
6514
+ const computeMetadataArgs = {
6515
+ project_name: projectName,
6516
+ project_id: projectId
6517
+ };
6518
+ const linkArgs = {
6519
+ org_name: orgName,
6520
+ app_url: appUrl,
6521
+ project_name: projectName,
6522
+ project_id: projectId
6523
+ };
6524
+ const state = stateArg ?? _globalState;
6525
+ state.setDebugLogLevel(debugLogLevel);
6526
+ state.enforceQueueSizeLimit(true);
6527
+ const lazyMetadata = new LazyValue(
6528
+ async () => {
6529
+ await state.login({
6530
+ orgName,
6531
+ apiKey,
6532
+ appUrl,
6533
+ forceLogin,
6534
+ fetch: fetch2
6535
+ });
6536
+ return computeLoggerMetadata(state, computeMetadataArgs);
6537
+ }
6538
+ );
6539
+ const ret = new Logger(state, lazyMetadata, {
6540
+ asyncFlush,
6541
+ computeMetadataArgs,
6542
+ linkArgs
6543
+ });
6544
+ if (options.setCurrent ?? true) {
6545
+ state.currentLogger = ret;
6546
+ }
6547
+ return ret;
6548
+ }
6497
6549
  async function loginToState(options = {}) {
6498
6550
  const {
6499
6551
  appUrl = isomorph_default.getEnv("BRAINTRUST_APP_URL") || "https://www.braintrust.dev",
6500
- apiKey = isomorph_default.getEnv("BRAINTRUST_API_KEY"),
6552
+ apiKey: apiKeyArg,
6501
6553
  orgName = isomorph_default.getEnv("BRAINTRUST_ORG_NAME"),
6502
6554
  fetch: fetch2 = globalThis.fetch
6503
6555
  } = options || {};
6556
+ const apiKey = apiKeyArg !== void 0 ? apiKeyArg : await isomorph_default.getBraintrustApiKey();
6504
6557
  const appPublicUrl = isomorph_default.getEnv("BRAINTRUST_APP_PUBLIC_URL") || appUrl;
6505
6558
  const state = new BraintrustState(options);
6506
6559
  state.resetLoginInfo();
@@ -7431,9 +7484,15 @@ var SpanImpl = class _SpanImpl {
7431
7484
  const cachedSpan = {
7432
7485
  input: partialRecord.input,
7433
7486
  output: partialRecord.output,
7487
+ expected: partialRecord.expected,
7488
+ error: partialRecord.error,
7489
+ scores: partialRecord.scores,
7490
+ metrics: partialRecord.metrics,
7434
7491
  metadata: partialRecord.metadata,
7492
+ tags: partialRecord.tags,
7435
7493
  span_id: this._spanId,
7436
7494
  span_parents: this._spanParents,
7495
+ is_root: this._spanId === this._rootSpanId,
7437
7496
  span_attributes: partialRecord.span_attributes
7438
7497
  };
7439
7498
  this._state.spanCache.queueWrite(
@@ -7769,6 +7828,7 @@ var Dataset2 = class extends ObjectFetcher {
7769
7828
  metadata,
7770
7829
  tags,
7771
7830
  output,
7831
+ origin,
7772
7832
  isMerge
7773
7833
  }) {
7774
7834
  return new LazyValue(async () => {
@@ -7783,6 +7843,7 @@ var Dataset2 = class extends ObjectFetcher {
7783
7843
  created: !isMerge ? (/* @__PURE__ */ new Date()).toISOString() : void 0,
7784
7844
  //if we're merging/updating an event we will not add this ts
7785
7845
  metadata,
7846
+ origin,
7786
7847
  ...!!isMerge ? {
7787
7848
  [IS_MERGE_FIELD]: true
7788
7849
  } : {}
@@ -7802,6 +7863,7 @@ var Dataset2 = class extends ObjectFetcher {
7802
7863
  * about anything else that's relevant, that you can use to help find and analyze examples later. For example, you could log the
7803
7864
  * `prompt`, example's `id`, or anything else that would be useful to slice/dice later. The values in `metadata` can be any
7804
7865
  * JSON-serializable type, but its keys must be strings.
7866
+ * @param event.origin (Optional) a reference to the source object this dataset record was derived from.
7805
7867
  * @param event.id (Optional) a unique identifier for the event. If you don't provide one, Braintrust will generate one for you.
7806
7868
  * @param event.output: (Deprecated) The output of your application. Use `expected` instead.
7807
7869
  * @returns The `id` of the logged record.
@@ -7812,7 +7874,8 @@ var Dataset2 = class extends ObjectFetcher {
7812
7874
  metadata,
7813
7875
  tags,
7814
7876
  id,
7815
- output
7877
+ output,
7878
+ origin
7816
7879
  }) {
7817
7880
  this.validateEvent({ metadata, expected, output, tags });
7818
7881
  const rowId = id || (0, import_uuid2.v4)();
@@ -7824,6 +7887,7 @@ var Dataset2 = class extends ObjectFetcher {
7824
7887
  metadata,
7825
7888
  tags,
7826
7889
  output,
7890
+ origin,
7827
7891
  isMerge: false
7828
7892
  })
7829
7893
  );
@@ -11417,11 +11481,11 @@ function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
11417
11481
  if (Array.isArray(event?.denyOutputPaths)) {
11418
11482
  return event.denyOutputPaths;
11419
11483
  }
11420
- const firstArgument = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
11421
- if (!firstArgument || typeof firstArgument !== "object") {
11484
+ const firstArgument2 = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
11485
+ if (!firstArgument2 || typeof firstArgument2 !== "object") {
11422
11486
  return defaultDenyOutputPaths;
11423
11487
  }
11424
- const runtimeDenyOutputPaths = firstArgument[RUNTIME_DENY_OUTPUT_PATHS];
11488
+ const runtimeDenyOutputPaths = firstArgument2[RUNTIME_DENY_OUTPUT_PATHS];
11425
11489
  if (Array.isArray(runtimeDenyOutputPaths) && runtimeDenyOutputPaths.every((path) => typeof path === "string")) {
11426
11490
  return runtimeDenyOutputPaths;
11427
11491
  }
@@ -15080,6 +15144,467 @@ function cleanMetrics2(metrics) {
15080
15144
  return cleaned;
15081
15145
  }
15082
15146
 
15147
+ // src/instrumentation/plugins/openai-agents-channels.ts
15148
+ var openAIAgentsCoreChannels = defineChannels("@openai/agents-core", {
15149
+ onTraceStart: channel({
15150
+ channelName: "tracing.processor.onTraceStart",
15151
+ kind: "async"
15152
+ }),
15153
+ onTraceEnd: channel({
15154
+ channelName: "tracing.processor.onTraceEnd",
15155
+ kind: "async"
15156
+ }),
15157
+ onSpanStart: channel({
15158
+ channelName: "tracing.processor.onSpanStart",
15159
+ kind: "async"
15160
+ }),
15161
+ onSpanEnd: channel({
15162
+ channelName: "tracing.processor.onSpanEnd",
15163
+ kind: "async"
15164
+ })
15165
+ });
15166
+
15167
+ // src/instrumentation/plugins/openai-agents-trace-processor.ts
15168
+ function isSpanData(spanData, type) {
15169
+ return spanData.type === type;
15170
+ }
15171
+ function spanTypeFromAgents(span) {
15172
+ const spanType = span.spanData.type;
15173
+ if (spanType === "function" || spanType === "guardrail" || spanType === "mcp_tools") {
15174
+ return "tool" /* TOOL */;
15175
+ }
15176
+ if (spanType === "generation" || spanType === "response" || spanType === "transcription" || spanType === "speech") {
15177
+ return "llm" /* LLM */;
15178
+ }
15179
+ return "task" /* TASK */;
15180
+ }
15181
+ function spanNameFromAgents(span) {
15182
+ const spanData = span.spanData;
15183
+ if ("name" in spanData && spanData.name) {
15184
+ return spanData.name;
15185
+ }
15186
+ switch (spanData.type) {
15187
+ case "generation":
15188
+ return "Generation";
15189
+ case "response":
15190
+ return "Response";
15191
+ case "handoff":
15192
+ return "Handoff";
15193
+ case "mcp_tools":
15194
+ return isSpanData(spanData, "mcp_tools") && spanData.server ? `List Tools (${spanData.server})` : "MCP List Tools";
15195
+ case "transcription":
15196
+ return "Transcription";
15197
+ case "speech":
15198
+ return "Speech";
15199
+ case "speech_group":
15200
+ return "Speech Group";
15201
+ default:
15202
+ return "Unknown";
15203
+ }
15204
+ }
15205
+ function getTimeElapsed(end, start) {
15206
+ if (!start || !end) {
15207
+ return void 0;
15208
+ }
15209
+ const startTime = new Date(start).getTime();
15210
+ const endTime = new Date(end).getTime();
15211
+ if (Number.isNaN(startTime) || Number.isNaN(endTime)) {
15212
+ return void 0;
15213
+ }
15214
+ return (endTime - startTime) / 1e3;
15215
+ }
15216
+ function getNumberProperty2(obj, key) {
15217
+ if (!isObject(obj) || !(key in obj)) {
15218
+ return void 0;
15219
+ }
15220
+ const value = obj[key];
15221
+ return typeof value === "number" ? value : void 0;
15222
+ }
15223
+ function parseUsageMetrics(usage) {
15224
+ const metrics = {};
15225
+ if (!isObject(usage)) {
15226
+ return metrics;
15227
+ }
15228
+ const promptTokens = getNumberProperty2(usage, "prompt_tokens") ?? getNumberProperty2(usage, "input_tokens") ?? getNumberProperty2(usage, "promptTokens") ?? getNumberProperty2(usage, "inputTokens");
15229
+ const completionTokens = getNumberProperty2(usage, "completion_tokens") ?? getNumberProperty2(usage, "output_tokens") ?? getNumberProperty2(usage, "completionTokens") ?? getNumberProperty2(usage, "outputTokens");
15230
+ const totalTokens = getNumberProperty2(usage, "total_tokens") ?? getNumberProperty2(usage, "totalTokens");
15231
+ if (promptTokens !== void 0) {
15232
+ metrics.prompt_tokens = promptTokens;
15233
+ }
15234
+ if (completionTokens !== void 0) {
15235
+ metrics.completion_tokens = completionTokens;
15236
+ }
15237
+ if (totalTokens !== void 0) {
15238
+ metrics.tokens = totalTokens;
15239
+ } else if (promptTokens !== void 0 && completionTokens !== void 0) {
15240
+ metrics.tokens = promptTokens + completionTokens;
15241
+ }
15242
+ const inputDetails = usage.input_tokens_details;
15243
+ const cachedTokens = getNumberProperty2(inputDetails, "cached_tokens");
15244
+ const cacheWriteTokens = getNumberProperty2(
15245
+ inputDetails,
15246
+ "cache_write_tokens"
15247
+ );
15248
+ if (cachedTokens !== void 0) {
15249
+ metrics.prompt_cached_tokens = cachedTokens;
15250
+ }
15251
+ if (cacheWriteTokens !== void 0) {
15252
+ metrics.prompt_cache_creation_tokens = cacheWriteTokens;
15253
+ }
15254
+ return metrics;
15255
+ }
15256
+ var OpenAIAgentsTraceProcessor = class _OpenAIAgentsTraceProcessor {
15257
+ static DEFAULT_MAX_TRACES = 1e4;
15258
+ logger;
15259
+ maxTraces;
15260
+ traceSpans = /* @__PURE__ */ new Map();
15261
+ traceOrder = [];
15262
+ _traceSpans = this.traceSpans;
15263
+ constructor(options = {}) {
15264
+ this.logger = options.logger;
15265
+ this.maxTraces = options.maxTraces ?? _OpenAIAgentsTraceProcessor.DEFAULT_MAX_TRACES;
15266
+ }
15267
+ evictOldestTrace() {
15268
+ const oldestTraceId = this.traceOrder.shift();
15269
+ if (oldestTraceId) {
15270
+ this.traceSpans.delete(oldestTraceId);
15271
+ }
15272
+ }
15273
+ onTraceStart(trace) {
15274
+ if (!trace?.traceId) {
15275
+ return Promise.resolve();
15276
+ }
15277
+ if (this.traceOrder.length >= this.maxTraces) {
15278
+ this.evictOldestTrace();
15279
+ }
15280
+ const current = currentSpan();
15281
+ const span = current && current !== NOOP_SPAN ? current.startSpan({
15282
+ name: trace.name,
15283
+ type: "task" /* TASK */
15284
+ }) : this.logger ? this.logger.startSpan({
15285
+ name: trace.name,
15286
+ type: "task" /* TASK */
15287
+ }) : startSpan({
15288
+ name: trace.name,
15289
+ type: "task" /* TASK */
15290
+ });
15291
+ span.log({
15292
+ input: "Agent workflow started",
15293
+ metadata: {
15294
+ group_id: trace.groupId,
15295
+ ...trace.metadata || {}
15296
+ }
15297
+ });
15298
+ this.traceSpans.set(trace.traceId, {
15299
+ rootSpan: span,
15300
+ childSpans: /* @__PURE__ */ new Map(),
15301
+ metadata: {
15302
+ firstInput: null,
15303
+ lastOutput: null
15304
+ }
15305
+ });
15306
+ this.traceOrder.push(trace.traceId);
15307
+ return Promise.resolve();
15308
+ }
15309
+ async onTraceEnd(trace) {
15310
+ const traceData = this.traceSpans.get(trace?.traceId);
15311
+ if (!traceData) {
15312
+ return;
15313
+ }
15314
+ try {
15315
+ traceData.rootSpan.log({
15316
+ input: traceData.metadata.firstInput,
15317
+ output: traceData.metadata.lastOutput
15318
+ });
15319
+ traceData.rootSpan.end();
15320
+ await traceData.rootSpan.flush();
15321
+ } finally {
15322
+ this.traceSpans.delete(trace.traceId);
15323
+ const orderIndex = this.traceOrder.indexOf(trace.traceId);
15324
+ if (orderIndex > -1) {
15325
+ this.traceOrder.splice(orderIndex, 1);
15326
+ }
15327
+ }
15328
+ }
15329
+ onSpanStart(span) {
15330
+ if (!span?.spanId || !span.traceId) {
15331
+ return Promise.resolve();
15332
+ }
15333
+ const traceData = this.traceSpans.get(span.traceId);
15334
+ if (!traceData) {
15335
+ return Promise.resolve();
15336
+ }
15337
+ const parentSpan = span.parentId ? traceData.childSpans.get(span.parentId) : traceData.rootSpan;
15338
+ if (!parentSpan) {
15339
+ return Promise.resolve();
15340
+ }
15341
+ const childSpan = parentSpan.startSpan({
15342
+ name: spanNameFromAgents(span),
15343
+ type: spanTypeFromAgents(span)
15344
+ });
15345
+ traceData.childSpans.set(span.spanId, childSpan);
15346
+ return Promise.resolve();
15347
+ }
15348
+ onSpanEnd(span) {
15349
+ if (!span?.spanId || !span.traceId) {
15350
+ return Promise.resolve();
15351
+ }
15352
+ const traceData = this.traceSpans.get(span.traceId);
15353
+ if (!traceData) {
15354
+ return Promise.resolve();
15355
+ }
15356
+ const braintrustSpan = traceData.childSpans.get(span.spanId);
15357
+ if (!braintrustSpan) {
15358
+ return Promise.resolve();
15359
+ }
15360
+ const logData = this.extractLogData(span);
15361
+ braintrustSpan.log({
15362
+ error: span.error,
15363
+ ...logData
15364
+ });
15365
+ braintrustSpan.end();
15366
+ traceData.childSpans.delete(span.spanId);
15367
+ const input = logData.input;
15368
+ const output = logData.output;
15369
+ if (traceData.metadata.firstInput === null && input != null) {
15370
+ traceData.metadata.firstInput = input;
15371
+ }
15372
+ if (output != null) {
15373
+ traceData.metadata.lastOutput = output;
15374
+ }
15375
+ return Promise.resolve();
15376
+ }
15377
+ async shutdown() {
15378
+ if (this.logger && typeof this.logger.flush === "function") {
15379
+ await this.logger.flush();
15380
+ }
15381
+ }
15382
+ async forceFlush() {
15383
+ if (this.logger && typeof this.logger.flush === "function") {
15384
+ await this.logger.flush();
15385
+ }
15386
+ }
15387
+ extractLogData(span) {
15388
+ const spanData = span.spanData;
15389
+ switch (spanData.type) {
15390
+ case "agent":
15391
+ return this.extractAgentLogData(spanData);
15392
+ case "response":
15393
+ return this.extractResponseLogData(spanData, span);
15394
+ case "function":
15395
+ return this.extractFunctionLogData(spanData);
15396
+ case "handoff":
15397
+ return this.extractHandoffLogData(spanData);
15398
+ case "guardrail":
15399
+ return this.extractGuardrailLogData(spanData);
15400
+ case "generation":
15401
+ return this.extractGenerationLogData(spanData, span);
15402
+ case "custom":
15403
+ return this.extractCustomLogData(spanData);
15404
+ case "mcp_tools":
15405
+ return this.extractMCPListToolsLogData(spanData);
15406
+ case "transcription":
15407
+ return this.extractTranscriptionLogData(spanData);
15408
+ case "speech":
15409
+ return this.extractSpeechLogData(spanData);
15410
+ case "speech_group":
15411
+ return this.extractSpeechGroupLogData(spanData);
15412
+ default:
15413
+ return {};
15414
+ }
15415
+ }
15416
+ extractAgentLogData(spanData) {
15417
+ return {
15418
+ metadata: {
15419
+ tools: spanData.tools,
15420
+ handoffs: spanData.handoffs,
15421
+ output_type: spanData.output_type
15422
+ }
15423
+ };
15424
+ }
15425
+ extractResponseLogData(spanData, span) {
15426
+ const response = spanData._response;
15427
+ const output = isObject(response) ? response.output : void 0;
15428
+ const usage = isObject(response) ? response.usage : void 0;
15429
+ const metrics = {
15430
+ ...this.extractTimingMetrics(span),
15431
+ ...parseUsageMetrics(usage)
15432
+ };
15433
+ return {
15434
+ input: spanData._input,
15435
+ output,
15436
+ metadata: isObject(response) ? this.omitKeys(response, ["output", "usage"]) : {},
15437
+ metrics
15438
+ };
15439
+ }
15440
+ extractFunctionLogData(spanData) {
15441
+ return {
15442
+ input: spanData.input,
15443
+ output: spanData.output
15444
+ };
15445
+ }
15446
+ extractHandoffLogData(spanData) {
15447
+ return {
15448
+ metadata: {
15449
+ from_agent: spanData.from_agent,
15450
+ to_agent: spanData.to_agent
15451
+ }
15452
+ };
15453
+ }
15454
+ extractGuardrailLogData(spanData) {
15455
+ return {
15456
+ metadata: {
15457
+ triggered: spanData.triggered
15458
+ }
15459
+ };
15460
+ }
15461
+ extractGenerationLogData(spanData, span) {
15462
+ return {
15463
+ input: spanData.input,
15464
+ output: spanData.output,
15465
+ metadata: {
15466
+ model: spanData.model,
15467
+ model_config: spanData.model_config
15468
+ },
15469
+ metrics: {
15470
+ ...this.extractTimingMetrics(span),
15471
+ ...parseUsageMetrics(spanData.usage)
15472
+ }
15473
+ };
15474
+ }
15475
+ extractCustomLogData(spanData) {
15476
+ return spanData.data || {};
15477
+ }
15478
+ extractMCPListToolsLogData(spanData) {
15479
+ return {
15480
+ output: spanData.result,
15481
+ metadata: {
15482
+ server: spanData.server
15483
+ }
15484
+ };
15485
+ }
15486
+ extractTranscriptionLogData(spanData) {
15487
+ return {
15488
+ input: spanData.input,
15489
+ output: spanData.output,
15490
+ metadata: {
15491
+ model: spanData.model,
15492
+ model_config: spanData.model_config
15493
+ }
15494
+ };
15495
+ }
15496
+ extractSpeechLogData(spanData) {
15497
+ return {
15498
+ input: spanData.input,
15499
+ output: spanData.output,
15500
+ metadata: {
15501
+ model: spanData.model,
15502
+ model_config: spanData.model_config
15503
+ }
15504
+ };
15505
+ }
15506
+ extractSpeechGroupLogData(spanData) {
15507
+ return {
15508
+ input: spanData.input
15509
+ };
15510
+ }
15511
+ extractTimingMetrics(span) {
15512
+ const timeToFirstToken = getTimeElapsed(
15513
+ span.endedAt ?? void 0,
15514
+ span.startedAt ?? void 0
15515
+ );
15516
+ return timeToFirstToken === void 0 ? {} : { time_to_first_token: timeToFirstToken };
15517
+ }
15518
+ omitKeys(value, keys) {
15519
+ const result = {};
15520
+ for (const [key, fieldValue] of Object.entries(value)) {
15521
+ if (!keys.includes(key)) {
15522
+ result[key] = fieldValue;
15523
+ }
15524
+ }
15525
+ return result;
15526
+ }
15527
+ };
15528
+
15529
+ // src/instrumentation/plugins/openai-agents-plugin.ts
15530
+ function firstArgument(args) {
15531
+ if (Array.isArray(args)) {
15532
+ return args[0];
15533
+ }
15534
+ if (isObject(args) && "length" in args && typeof args.length === "number" && Number.isInteger(args.length) && args.length >= 0) {
15535
+ return Array.from(args)[0];
15536
+ }
15537
+ return void 0;
15538
+ }
15539
+ function isOpenAIAgentsTrace(value) {
15540
+ return isObject(value) && value.type === "trace" && typeof value.traceId === "string";
15541
+ }
15542
+ function isOpenAIAgentsSpan(value) {
15543
+ return isObject(value) && value.type === "trace.span" && typeof value.traceId === "string" && typeof value.spanId === "string";
15544
+ }
15545
+ var OpenAIAgentsPlugin = class extends BasePlugin {
15546
+ processor = new OpenAIAgentsTraceProcessor();
15547
+ onEnable() {
15548
+ this.subscribeToTraceLifecycle();
15549
+ }
15550
+ onDisable() {
15551
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
15552
+ void this.processor.shutdown();
15553
+ }
15554
+ subscribeToTraceLifecycle() {
15555
+ const traceStartChannel = openAIAgentsCoreChannels.onTraceStart.tracingChannel();
15556
+ const traceStartHandlers = {
15557
+ start: (event) => {
15558
+ const trace = firstArgument(event.arguments);
15559
+ if (isOpenAIAgentsTrace(trace)) {
15560
+ void this.processor.onTraceStart(trace);
15561
+ }
15562
+ }
15563
+ };
15564
+ traceStartChannel.subscribe(traceStartHandlers);
15565
+ this.unsubscribers.push(
15566
+ () => traceStartChannel.unsubscribe(traceStartHandlers)
15567
+ );
15568
+ const traceEndChannel = openAIAgentsCoreChannels.onTraceEnd.tracingChannel();
15569
+ const traceEndHandlers = {
15570
+ start: (event) => {
15571
+ const trace = firstArgument(event.arguments);
15572
+ if (isOpenAIAgentsTrace(trace)) {
15573
+ void this.processor.onTraceEnd(trace);
15574
+ }
15575
+ }
15576
+ };
15577
+ traceEndChannel.subscribe(traceEndHandlers);
15578
+ this.unsubscribers.push(
15579
+ () => traceEndChannel.unsubscribe(traceEndHandlers)
15580
+ );
15581
+ const spanStartChannel = openAIAgentsCoreChannels.onSpanStart.tracingChannel();
15582
+ const spanStartHandlers = {
15583
+ start: (event) => {
15584
+ const span = firstArgument(event.arguments);
15585
+ if (isOpenAIAgentsSpan(span)) {
15586
+ void this.processor.onSpanStart(span);
15587
+ }
15588
+ }
15589
+ };
15590
+ spanStartChannel.subscribe(spanStartHandlers);
15591
+ this.unsubscribers.push(
15592
+ () => spanStartChannel.unsubscribe(spanStartHandlers)
15593
+ );
15594
+ const spanEndChannel = openAIAgentsCoreChannels.onSpanEnd.tracingChannel();
15595
+ const spanEndHandlers = {
15596
+ start: (event) => {
15597
+ const span = firstArgument(event.arguments);
15598
+ if (isOpenAIAgentsSpan(span)) {
15599
+ void this.processor.onSpanEnd(span);
15600
+ }
15601
+ }
15602
+ };
15603
+ spanEndChannel.subscribe(spanEndHandlers);
15604
+ this.unsubscribers.push(() => spanEndChannel.unsubscribe(spanEndHandlers));
15605
+ }
15606
+ };
15607
+
15083
15608
  // src/instrumentation/plugins/google-genai-channels.ts
15084
15609
  var googleGenAIChannels = defineChannels("@google/genai", {
15085
15610
  generateContent: channel({
@@ -21372,49 +21897,1759 @@ var GitHubCopilotPlugin = class extends BasePlugin {
21372
21897
  }
21373
21898
  };
21374
21899
 
21375
- // src/instrumentation/braintrust-plugin.ts
21376
- function getIntegrationConfig(integrations, key) {
21377
- return integrations[key];
21900
+ // src/instrumentation/plugins/flue-channels.ts
21901
+ var flueChannels = defineChannels("@flue/runtime", {
21902
+ createContext: channel({
21903
+ channelName: "createFlueContext",
21904
+ kind: "sync-stream"
21905
+ }),
21906
+ openSession: channel({
21907
+ channelName: "Harness.openSession",
21908
+ kind: "async"
21909
+ }),
21910
+ contextEvent: channel({
21911
+ channelName: "context.event",
21912
+ kind: "sync-stream"
21913
+ }),
21914
+ prompt: channel({
21915
+ channelName: "session.prompt",
21916
+ kind: "async"
21917
+ }),
21918
+ skill: channel({
21919
+ channelName: "session.skill",
21920
+ kind: "async"
21921
+ }),
21922
+ task: channel({
21923
+ channelName: "session.task",
21924
+ kind: "async"
21925
+ }),
21926
+ compact: channel({
21927
+ channelName: "session.compact",
21928
+ kind: "async"
21929
+ })
21930
+ });
21931
+
21932
+ // src/wrappers/flue.ts
21933
+ var WRAPPED_FLUE_CONTEXT = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-context");
21934
+ var WRAPPED_FLUE_HARNESS = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-harness");
21935
+ var WRAPPED_FLUE_SESSION = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-session");
21936
+ var SUBSCRIBED_FLUE_CONTEXT_EVENTS = /* @__PURE__ */ Symbol.for(
21937
+ "braintrust.flue.subscribed-context-events"
21938
+ );
21939
+ function patchFlueContextInPlace(ctx) {
21940
+ const context = ctx;
21941
+ if (context[WRAPPED_FLUE_CONTEXT]) {
21942
+ return ctx;
21943
+ }
21944
+ const originalInit = context.init.bind(context);
21945
+ try {
21946
+ Object.defineProperty(context, WRAPPED_FLUE_CONTEXT, {
21947
+ configurable: false,
21948
+ enumerable: false,
21949
+ value: true
21950
+ });
21951
+ Object.defineProperty(context, "init", {
21952
+ configurable: true,
21953
+ value: async function wrappedFlueInit(options) {
21954
+ const harness = await originalInit(options);
21955
+ return wrapFlueHarness(harness);
21956
+ },
21957
+ writable: true
21958
+ });
21959
+ } catch {
21960
+ }
21961
+ return ctx;
21378
21962
  }
21379
- var BraintrustPlugin = class extends BasePlugin {
21380
- config;
21381
- openaiPlugin = null;
21382
- openAICodexPlugin = null;
21383
- anthropicPlugin = null;
21384
- aiSDKPlugin = null;
21385
- claudeAgentSDKPlugin = null;
21386
- cursorSDKPlugin = null;
21387
- googleGenAIPlugin = null;
21388
- huggingFacePlugin = null;
21389
- openRouterPlugin = null;
21390
- openRouterAgentPlugin = null;
21391
- mistralPlugin = null;
21392
- googleADKPlugin = null;
21393
- coherePlugin = null;
21394
- groqPlugin = null;
21395
- genkitPlugin = null;
21396
- gitHubCopilotPlugin = null;
21397
- constructor(config = {}) {
21398
- super();
21399
- this.config = config;
21963
+ function subscribeFlueContextEvents(ctx, options = {}) {
21964
+ if (!ctx || typeof ctx !== "object" || typeof ctx.subscribeEvent !== "function") {
21965
+ return void 0;
21400
21966
  }
21401
- onEnable() {
21402
- const integrations = this.config.integrations || {};
21403
- if (integrations.openai !== false) {
21404
- this.openaiPlugin = new OpenAIPlugin();
21405
- this.openaiPlugin.enable();
21967
+ const context = ctx;
21968
+ const captureTurnSpans = options.captureTurnSpans ?? true;
21969
+ const existingSubscription = context[SUBSCRIBED_FLUE_CONTEXT_EVENTS];
21970
+ if (existingSubscription) {
21971
+ if (existingSubscription.captureTurnSpans || !captureTurnSpans) {
21972
+ return void 0;
21406
21973
  }
21407
- if (integrations.openaiCodexSDK !== false) {
21408
- this.openAICodexPlugin = new OpenAICodexPlugin();
21409
- this.openAICodexPlugin.enable();
21974
+ try {
21975
+ existingSubscription.unsubscribe();
21976
+ } catch {
21410
21977
  }
21411
- if (integrations.anthropic !== false) {
21412
- this.anthropicPlugin = new AnthropicPlugin();
21413
- this.anthropicPlugin.enable();
21978
+ }
21979
+ try {
21980
+ const unsubscribe = ctx.subscribeEvent((event) => {
21981
+ flueChannels.contextEvent.traceSync(() => void 0, {
21982
+ arguments: [event],
21983
+ captureTurnSpans,
21984
+ context: ctx
21985
+ });
21986
+ });
21987
+ if (existingSubscription) {
21988
+ existingSubscription.captureTurnSpans = captureTurnSpans;
21989
+ existingSubscription.unsubscribe = unsubscribe;
21990
+ } else {
21991
+ Object.defineProperty(context, SUBSCRIBED_FLUE_CONTEXT_EVENTS, {
21992
+ configurable: false,
21993
+ enumerable: false,
21994
+ value: {
21995
+ captureTurnSpans,
21996
+ unsubscribe
21997
+ }
21998
+ });
21414
21999
  }
21415
- if (integrations.aisdk !== false && integrations.vercel !== false) {
21416
- this.aiSDKPlugin = new AISDKPlugin();
21417
- this.aiSDKPlugin.enable();
22000
+ return unsubscribe;
22001
+ } catch {
22002
+ return void 0;
22003
+ }
22004
+ }
22005
+ function wrapFlueHarness(harness) {
22006
+ if (!isPlausibleFlueHarness(harness)) {
22007
+ return harness;
22008
+ }
22009
+ const target = harness;
22010
+ if (target[WRAPPED_FLUE_HARNESS]) {
22011
+ return harness;
22012
+ }
22013
+ const originalSession = target.session.bind(target);
22014
+ try {
22015
+ Object.defineProperty(target, WRAPPED_FLUE_HARNESS, {
22016
+ configurable: false,
22017
+ enumerable: false,
22018
+ value: true
22019
+ });
22020
+ Object.defineProperty(target, "session", {
22021
+ configurable: true,
22022
+ value: async function wrappedFlueHarnessSession(name, options) {
22023
+ const session = await originalSession(name, options);
22024
+ return patchFlueSessionInPlace(session);
22025
+ },
22026
+ writable: true
22027
+ });
22028
+ const sessions = target.sessions;
22029
+ if (sessions && typeof sessions === "object") {
22030
+ patchFlueSessionFactory(sessions, "get");
22031
+ patchFlueSessionFactory(sessions, "create");
22032
+ }
22033
+ } catch {
22034
+ }
22035
+ return harness;
22036
+ }
22037
+ function patchFlueSessionInPlace(session) {
22038
+ if (session[WRAPPED_FLUE_SESSION]) {
22039
+ return session;
22040
+ }
22041
+ try {
22042
+ Object.defineProperty(session, WRAPPED_FLUE_SESSION, {
22043
+ configurable: false,
22044
+ enumerable: false,
22045
+ value: true
22046
+ });
22047
+ patchCallHandleMethod(session, "prompt", flueChannels.prompt);
22048
+ patchCallHandleMethod(session, "skill", flueChannels.skill);
22049
+ patchCallHandleMethod(session, "task", flueChannels.task);
22050
+ patchCompact(session);
22051
+ } catch {
22052
+ }
22053
+ return session;
22054
+ }
22055
+ function patchFlueSessionFactory(sessions, method) {
22056
+ const original = sessions[method];
22057
+ if (typeof original !== "function") {
22058
+ return;
22059
+ }
22060
+ const bound = original.bind(sessions);
22061
+ Object.defineProperty(sessions, method, {
22062
+ configurable: true,
22063
+ value: async function wrappedFlueSessionFactory(name, options) {
22064
+ const session = await bound(name, options);
22065
+ return patchFlueSessionInPlace(session);
22066
+ },
22067
+ writable: true
22068
+ });
22069
+ }
22070
+ function patchCallHandleMethod(session, method, channel2) {
22071
+ const original = session[method];
22072
+ if (typeof original !== "function") {
22073
+ return;
22074
+ }
22075
+ const bound = original.bind(session);
22076
+ Object.defineProperty(session, method, {
22077
+ configurable: true,
22078
+ value(input, options) {
22079
+ const args = [input, options];
22080
+ const { originalResult, traced } = traceFlueOperation(channel2, {
22081
+ context: {
22082
+ arguments: args,
22083
+ operation: method,
22084
+ session
22085
+ },
22086
+ run: () => bound(input, options)
22087
+ });
22088
+ return preserveCallHandle(originalResult, traced);
22089
+ },
22090
+ writable: true
22091
+ });
22092
+ }
22093
+ function patchCompact(session) {
22094
+ const original = session.compact;
22095
+ if (typeof original !== "function") {
22096
+ return;
22097
+ }
22098
+ const bound = original.bind(session);
22099
+ Object.defineProperty(session, "compact", {
22100
+ configurable: true,
22101
+ value() {
22102
+ const context = {
22103
+ arguments: [],
22104
+ operation: "compact",
22105
+ session
22106
+ };
22107
+ return flueChannels.compact.tracePromise(() => bound(), context);
22108
+ },
22109
+ writable: true
22110
+ });
22111
+ }
22112
+ function traceFlueOperation(channel2, args) {
22113
+ const tracingChannel = channel2.tracingChannel();
22114
+ const context = args.context;
22115
+ let originalResult;
22116
+ let traced;
22117
+ const run = () => {
22118
+ try {
22119
+ originalResult = args.run();
22120
+ tracingChannel.end?.publish(context);
22121
+ } catch (error) {
22122
+ context.error = normalizeError3(error);
22123
+ tracingChannel.error?.publish(context);
22124
+ tracingChannel.end?.publish(context);
22125
+ throw error;
22126
+ }
22127
+ traced = Promise.resolve(originalResult).then(
22128
+ (result) => {
22129
+ context.result = result;
22130
+ tracingChannel.asyncStart?.publish(context);
22131
+ tracingChannel.asyncEnd?.publish(context);
22132
+ return result;
22133
+ },
22134
+ (error) => {
22135
+ context.error = normalizeError3(error);
22136
+ tracingChannel.error?.publish(context);
22137
+ tracingChannel.asyncStart?.publish(context);
22138
+ tracingChannel.asyncEnd?.publish(context);
22139
+ throw error;
22140
+ }
22141
+ );
22142
+ };
22143
+ if (tracingChannel.start?.runStores) {
22144
+ tracingChannel.start.runStores(context, run);
22145
+ } else {
22146
+ tracingChannel.start?.publish(context);
22147
+ run();
22148
+ }
22149
+ return { originalResult, traced };
22150
+ }
22151
+ function normalizeError3(error) {
22152
+ return error instanceof Error ? error : new Error(String(error));
22153
+ }
22154
+ function preserveCallHandle(originalHandle, traced) {
22155
+ if (!isFlueCallHandle(originalHandle)) {
22156
+ return traced;
22157
+ }
22158
+ const handle = originalHandle;
22159
+ const wrapped = {
22160
+ get signal() {
22161
+ return handle.signal;
22162
+ },
22163
+ abort(reason) {
22164
+ return handle.abort(reason);
22165
+ },
22166
+ then(onfulfilled, onrejected) {
22167
+ return traced.then(onfulfilled, onrejected);
22168
+ }
22169
+ };
22170
+ return wrapped;
22171
+ }
22172
+ function isPlausibleFlueHarness(value) {
22173
+ return !!value && typeof value === "object" && typeof value.session === "function";
22174
+ }
22175
+ function isFlueCallHandle(value) {
22176
+ return !!value && typeof value === "object" && typeof value.then === "function" && typeof value.abort === "function" && "signal" in value;
22177
+ }
22178
+
22179
+ // src/instrumentation/plugins/flue-plugin.ts
22180
+ var FluePlugin = class extends BasePlugin {
22181
+ activeOperationsById = /* @__PURE__ */ new Map();
22182
+ activeOperationsByScope = /* @__PURE__ */ new Map();
22183
+ compactionsByScope = /* @__PURE__ */ new Map();
22184
+ pendingOperationsByKey = /* @__PURE__ */ new Map();
22185
+ tasksById = /* @__PURE__ */ new Map();
22186
+ toolsById = /* @__PURE__ */ new Map();
22187
+ turnsByScope = /* @__PURE__ */ new Map();
22188
+ onEnable() {
22189
+ this.subscribeToContextCreation();
22190
+ this.subscribeToSessionCreation();
22191
+ this.subscribeToContextEvents();
22192
+ this.subscribeToSessionOperations();
22193
+ }
22194
+ onDisable() {
22195
+ for (const unsubscribe of this.unsubscribers) {
22196
+ unsubscribe();
22197
+ }
22198
+ this.unsubscribers = [];
22199
+ this.activeOperationsById.clear();
22200
+ this.activeOperationsByScope.clear();
22201
+ this.compactionsByScope.clear();
22202
+ this.pendingOperationsByKey.clear();
22203
+ this.tasksById.clear();
22204
+ this.toolsById.clear();
22205
+ this.turnsByScope.clear();
22206
+ }
22207
+ subscribeToContextCreation() {
22208
+ const channel2 = flueChannels.createContext.tracingChannel();
22209
+ const handlers = {
22210
+ end: (event) => {
22211
+ const ctx = event.result;
22212
+ if (!ctx) {
22213
+ return;
22214
+ }
22215
+ subscribeFlueContextEvents(ctx, { captureTurnSpans: false });
22216
+ patchFlueContextInPlace(ctx);
22217
+ },
22218
+ error: () => {
22219
+ }
22220
+ };
22221
+ channel2.subscribe(handlers);
22222
+ this.unsubscribers.push(() => {
22223
+ channel2.unsubscribe(handlers);
22224
+ });
22225
+ }
22226
+ subscribeToSessionCreation() {
22227
+ const channel2 = flueChannels.openSession.tracingChannel();
22228
+ const handlers = {
22229
+ asyncEnd: (event) => {
22230
+ if (event.result) {
22231
+ patchFlueSessionInPlace(
22232
+ event.result
22233
+ );
22234
+ }
22235
+ if (event.harness) {
22236
+ wrapFlueHarness(event.harness);
22237
+ }
22238
+ },
22239
+ error: () => {
22240
+ }
22241
+ };
22242
+ channel2.subscribe(handlers);
22243
+ this.unsubscribers.push(() => {
22244
+ channel2.unsubscribe(handlers);
22245
+ });
22246
+ }
22247
+ subscribeToSessionOperations() {
22248
+ this.subscribeToSessionOperation(flueChannels.prompt);
22249
+ this.subscribeToSessionOperation(flueChannels.skill);
22250
+ this.subscribeToSessionOperation(flueChannels.task);
22251
+ this.subscribeToCompact();
22252
+ }
22253
+ subscribeToSessionOperation(channel2) {
22254
+ const tracingChannel = channel2.tracingChannel();
22255
+ const states = /* @__PURE__ */ new WeakMap();
22256
+ const ensureState2 = (event) => {
22257
+ const existing = states.get(event);
22258
+ if (existing) {
22259
+ return existing;
22260
+ }
22261
+ const state = this.startOperationState({
22262
+ args: event.arguments,
22263
+ moduleVersion: typeof event.moduleVersion === "string" ? event.moduleVersion : void 0,
22264
+ operation: event.operation,
22265
+ session: event.session
22266
+ });
22267
+ states.set(event, state);
22268
+ return state;
22269
+ };
22270
+ const unbindCurrentSpanStore = this.bindCurrentSpanStoreToOperationStart(
22271
+ tracingChannel,
22272
+ ensureState2
22273
+ );
22274
+ const handlers = {
22275
+ start: (event) => {
22276
+ ensureState2(event);
22277
+ },
22278
+ asyncEnd: (event) => {
22279
+ this.endOperationState(states.get(event), event.result);
22280
+ states.delete(event);
22281
+ },
22282
+ error: (event) => {
22283
+ const state = states.get(event);
22284
+ if (state && event.error) {
22285
+ safeLog3(state.span, { error: errorToString(event.error) });
22286
+ this.finishOperationState(state);
22287
+ }
22288
+ states.delete(event);
22289
+ }
22290
+ };
22291
+ tracingChannel.subscribe(handlers);
22292
+ this.unsubscribers.push(() => {
22293
+ unbindCurrentSpanStore?.();
22294
+ tracingChannel.unsubscribe(handlers);
22295
+ });
22296
+ }
22297
+ subscribeToCompact() {
22298
+ const tracingChannel = flueChannels.compact.tracingChannel();
22299
+ const states = /* @__PURE__ */ new WeakMap();
22300
+ const ensureState2 = (event) => {
22301
+ const existing = states.get(event);
22302
+ if (existing) {
22303
+ return existing;
22304
+ }
22305
+ const state = this.startOperationState({
22306
+ args: [],
22307
+ moduleVersion: typeof event.moduleVersion === "string" ? event.moduleVersion : void 0,
22308
+ operation: event.operation,
22309
+ session: event.session
22310
+ });
22311
+ states.set(event, state);
22312
+ return state;
22313
+ };
22314
+ const unbindCurrentSpanStore = this.bindCurrentSpanStoreToOperationStart(
22315
+ tracingChannel,
22316
+ ensureState2
22317
+ );
22318
+ const handlers = {
22319
+ start: (event) => {
22320
+ ensureState2(event);
22321
+ },
22322
+ asyncEnd: (event) => {
22323
+ this.endOperationState(states.get(event), void 0);
22324
+ states.delete(event);
22325
+ },
22326
+ error: (event) => {
22327
+ const state = states.get(event);
22328
+ if (state && event.error) {
22329
+ safeLog3(state.span, { error: errorToString(event.error) });
22330
+ this.finishOperationState(state);
22331
+ }
22332
+ states.delete(event);
22333
+ }
22334
+ };
22335
+ tracingChannel.subscribe(handlers);
22336
+ this.unsubscribers.push(() => {
22337
+ unbindCurrentSpanStore?.();
22338
+ tracingChannel.unsubscribe(handlers);
22339
+ });
22340
+ }
22341
+ subscribeToContextEvents() {
22342
+ const channel2 = flueChannels.contextEvent.tracingChannel();
22343
+ const handlers = {
22344
+ start: (event) => {
22345
+ const flueEvent = event.arguments[0];
22346
+ if (!flueEvent) {
22347
+ return;
22348
+ }
22349
+ try {
22350
+ this.handleFlueEvent(flueEvent, {
22351
+ captureTurnSpans: event.captureTurnSpans !== false
22352
+ });
22353
+ } catch (error) {
22354
+ logInstrumentationError3("Flue event", error);
22355
+ }
22356
+ },
22357
+ error: () => {
22358
+ }
22359
+ };
22360
+ channel2.subscribe(handlers);
22361
+ this.unsubscribers.push(() => {
22362
+ channel2.unsubscribe(handlers);
22363
+ });
22364
+ }
22365
+ bindCurrentSpanStoreToOperationStart(tracingChannel, ensureState2) {
22366
+ const state = _internalGetGlobalState();
22367
+ const startChannel = tracingChannel.start;
22368
+ const contextManager = state?.contextManager;
22369
+ const currentSpanStore = contextManager ? contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
22370
+ if (!currentSpanStore || !startChannel) {
22371
+ return void 0;
22372
+ }
22373
+ startChannel.bindStore(currentSpanStore, (event) => {
22374
+ const operationState = ensureState2(event);
22375
+ return contextManager.wrapSpanForStore(operationState.span);
22376
+ });
22377
+ return () => {
22378
+ startChannel.unbindStore(currentSpanStore);
22379
+ };
22380
+ }
22381
+ startOperationState(args) {
22382
+ const sessionName = getSessionName(args.session);
22383
+ const metadata = {
22384
+ ...extractOperationInputMetadata(args.operation, args.args),
22385
+ ...extractSessionMetadata(args.session),
22386
+ "flue.operation": args.operation,
22387
+ provider: "flue",
22388
+ ...args.moduleVersion ? { "flue.version": args.moduleVersion } : {}
22389
+ };
22390
+ const span = startSpan({
22391
+ name: `flue.session.${args.operation}`,
22392
+ spanAttributes: { type: "task" /* TASK */ }
22393
+ });
22394
+ const state = {
22395
+ metadata,
22396
+ operation: args.operation,
22397
+ sessionName,
22398
+ span,
22399
+ startTime: getCurrentUnixTimestamp()
22400
+ };
22401
+ safeLog3(span, {
22402
+ input: extractOperationInput(args.operation, args.args),
22403
+ metadata
22404
+ });
22405
+ this.pendingOperationQueue(operationKey(sessionName, args.operation)).push(
22406
+ state
22407
+ );
22408
+ addOperationToScope(
22409
+ this.activeOperationsByScope,
22410
+ sessionName ?? "unknown",
22411
+ state
22412
+ );
22413
+ return state;
22414
+ }
22415
+ endOperationState(state, result) {
22416
+ if (!state) {
22417
+ return;
22418
+ }
22419
+ const metadata = {
22420
+ ...state.metadata,
22421
+ ...extractPromptResponseMetadata(result)
22422
+ };
22423
+ const metrics = {
22424
+ ...buildDurationMetrics3(state.startTime),
22425
+ ...metricsFromUsage(result?.usage)
22426
+ };
22427
+ safeLog3(state.span, {
22428
+ metadata,
22429
+ metrics,
22430
+ output: extractOperationOutput(result)
22431
+ });
22432
+ this.finishCompactionsForOperation(state);
22433
+ this.finishOperationState(state);
22434
+ }
22435
+ finishOperationState(state) {
22436
+ removePendingOperation(this.pendingOperationsByKey, state);
22437
+ if (state.operationId) {
22438
+ this.activeOperationsById.delete(state.operationId);
22439
+ }
22440
+ removeScopedOperation(this.activeOperationsByScope, state);
22441
+ state.span.end();
22442
+ }
22443
+ handleFlueEvent(event, options) {
22444
+ switch (event.type) {
22445
+ case "operation_start":
22446
+ this.handleOperationStart(event);
22447
+ return;
22448
+ case "operation":
22449
+ this.handleOperation(event);
22450
+ return;
22451
+ case "text_delta":
22452
+ if (!options.captureTurnSpans) {
22453
+ return;
22454
+ }
22455
+ this.ensureTurnState(event).text.push(
22456
+ typeof event.text === "string" ? event.text : ""
22457
+ );
22458
+ return;
22459
+ case "thinking_start":
22460
+ if (!options.captureTurnSpans) {
22461
+ return;
22462
+ }
22463
+ this.handleThinkingStart(event);
22464
+ return;
22465
+ case "thinking_delta":
22466
+ if (!options.captureTurnSpans) {
22467
+ return;
22468
+ }
22469
+ this.handleThinkingDelta(event);
22470
+ return;
22471
+ case "thinking_end":
22472
+ if (!options.captureTurnSpans) {
22473
+ return;
22474
+ }
22475
+ this.handleThinkingEnd(event);
22476
+ return;
22477
+ case "turn":
22478
+ if (!options.captureTurnSpans) {
22479
+ return;
22480
+ }
22481
+ this.handleTurn(event);
22482
+ return;
22483
+ case "tool_start":
22484
+ this.handleToolStart(event, options);
22485
+ return;
22486
+ case "tool_call":
22487
+ this.handleToolCall(event);
22488
+ return;
22489
+ case "task_start":
22490
+ this.handleTaskStart(event);
22491
+ return;
22492
+ case "task":
22493
+ this.handleTask(event);
22494
+ return;
22495
+ case "compaction_start":
22496
+ this.handleCompactionStart(event);
22497
+ return;
22498
+ case "compaction":
22499
+ this.handleCompaction(event);
22500
+ return;
22501
+ default:
22502
+ return;
22503
+ }
22504
+ }
22505
+ handleOperationStart(event) {
22506
+ if (!isInstrumentedOperation(event.operationKind)) {
22507
+ return;
22508
+ }
22509
+ const state = this.takePendingOperationForEvent(event);
22510
+ if (!state) {
22511
+ return;
22512
+ }
22513
+ state.operationId = event.operationId;
22514
+ this.activeOperationsById.set(event.operationId, state);
22515
+ addScopedOperation(this.activeOperationsByScope, event, state);
22516
+ state.metadata = {
22517
+ ...state.metadata,
22518
+ ...extractEventMetadata(event),
22519
+ "flue.operation_id": event.operationId
22520
+ };
22521
+ safeLog3(state.span, { metadata: state.metadata });
22522
+ }
22523
+ handleOperation(event) {
22524
+ const state = event.operationId ? this.activeOperationsById.get(event.operationId) : void 0;
22525
+ if (!state) {
22526
+ return;
22527
+ }
22528
+ const metadata = {
22529
+ ...state.metadata,
22530
+ ...extractEventMetadata(event),
22531
+ ...typeof event.durationMs === "number" ? { "flue.duration_ms": event.durationMs } : {},
22532
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
22533
+ };
22534
+ const metrics = metricsFromUsage(event.usage);
22535
+ safeLog3(state.span, {
22536
+ ...event.error ? { error: errorToString(event.error) } : {},
22537
+ metadata,
22538
+ ...Object.keys(metrics).length ? { metrics } : {}
22539
+ });
22540
+ }
22541
+ ensureTurnState(event) {
22542
+ const scope = scopeKey(event);
22543
+ const existing = this.turnsByScope.get(scope);
22544
+ if (existing) {
22545
+ return existing;
22546
+ }
22547
+ const parent = this.parentSpanForEvent(event);
22548
+ const metadata = {
22549
+ ...extractEventMetadata(event),
22550
+ provider: "flue"
22551
+ };
22552
+ const span = startFlueSpan(parent, {
22553
+ name: "flue.turn",
22554
+ spanAttributes: { type: "llm" /* LLM */ }
22555
+ });
22556
+ const state = {
22557
+ metadata,
22558
+ span,
22559
+ hasThinking: false,
22560
+ startTime: getCurrentUnixTimestamp(),
22561
+ text: [],
22562
+ thinking: [],
22563
+ toolCalls: []
22564
+ };
22565
+ safeLog3(span, { metadata });
22566
+ this.turnsByScope.set(scope, state);
22567
+ return state;
22568
+ }
22569
+ handleTurn(event) {
22570
+ const scope = scopeKey(event);
22571
+ const state = this.ensureTurnState(event);
22572
+ const text = state.text.join("");
22573
+ const reasoning = state.finalThinking ?? state.thinking.join("");
22574
+ const outputReasoning = reasoning || (state.hasThinking ? "[reasoning stream present; content unavailable]" : void 0);
22575
+ const metadata = {
22576
+ ...state.metadata,
22577
+ ...extractEventMetadata(event),
22578
+ ...event.model ? { model: event.model, "flue.model": event.model } : {},
22579
+ ...event.stopReason ? { "flue.stop_reason": event.stopReason } : {},
22580
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {},
22581
+ provider: "flue"
22582
+ };
22583
+ safeLog3(state.span, {
22584
+ ...event.error ? { error: errorToString(event.error) } : {},
22585
+ metadata,
22586
+ metrics: {
22587
+ ...durationMsMetrics(event.durationMs),
22588
+ ...metricsFromUsage(event.usage)
22589
+ },
22590
+ output: toAssistantOutput(
22591
+ text,
22592
+ event.stopReason,
22593
+ outputReasoning,
22594
+ state.toolCalls
22595
+ )
22596
+ });
22597
+ state.span.end();
22598
+ this.turnsByScope.delete(scope);
22599
+ }
22600
+ handleThinkingDelta(event) {
22601
+ const delta = event.delta;
22602
+ if (typeof delta !== "string" || !delta) {
22603
+ return;
22604
+ }
22605
+ const state = this.ensureTurnState(event);
22606
+ state.hasThinking = true;
22607
+ state.metadata["flue.thinking"] = true;
22608
+ state.thinking.push(delta);
22609
+ }
22610
+ handleThinkingStart(event) {
22611
+ const state = this.ensureTurnState(event);
22612
+ state.hasThinking = true;
22613
+ state.metadata["flue.thinking"] = true;
22614
+ }
22615
+ handleThinkingEnd(event) {
22616
+ const state = this.ensureTurnState(event);
22617
+ state.hasThinking = true;
22618
+ state.metadata["flue.thinking"] = true;
22619
+ if (typeof event.content === "string" && event.content) {
22620
+ state.finalThinking = event.content;
22621
+ }
22622
+ }
22623
+ handleToolStart(event, options) {
22624
+ const toolCallId = event.toolCallId;
22625
+ if (!toolCallId) {
22626
+ return;
22627
+ }
22628
+ const parent = this.parentSpanForEvent(event);
22629
+ const scope = scopeKey(event);
22630
+ let turnState = this.turnsByScope.get(scope);
22631
+ if (!turnState && parent && options.captureTurnSpans) {
22632
+ turnState = this.ensureTurnState(event);
22633
+ }
22634
+ const metadata = {
22635
+ ...extractEventMetadata(event),
22636
+ ...event.toolName ? { "flue.tool_name": event.toolName } : {},
22637
+ "flue.tool_call_id": toolCallId,
22638
+ provider: "flue"
22639
+ };
22640
+ const span = startFlueSpan(parent, {
22641
+ name: `tool: ${event.toolName ?? "unknown"}`,
22642
+ spanAttributes: { type: "tool" /* TOOL */ }
22643
+ });
22644
+ if (turnState) {
22645
+ turnState.toolCalls.push({
22646
+ args: event.args,
22647
+ toolCallId,
22648
+ toolName: event.toolName
22649
+ });
22650
+ }
22651
+ safeLog3(span, {
22652
+ input: event.args,
22653
+ metadata
22654
+ });
22655
+ this.toolsById.set(toolKey(event), {
22656
+ metadata,
22657
+ span,
22658
+ startTime: getCurrentUnixTimestamp()
22659
+ });
22660
+ }
22661
+ handleToolCall(event) {
22662
+ const key = toolKey(event);
22663
+ const state = this.toolsById.get(key) ?? this.startSyntheticToolState(event, event.toolName ?? "unknown");
22664
+ const metadata = {
22665
+ ...state.metadata,
22666
+ ...extractEventMetadata(event),
22667
+ ...event.toolName ? { "flue.tool_name": event.toolName } : {},
22668
+ ...event.toolCallId ? { "flue.tool_call_id": event.toolCallId } : {},
22669
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
22670
+ };
22671
+ safeLog3(state.span, {
22672
+ ...event.isError ? { error: errorToString(event.result) } : {},
22673
+ metadata,
22674
+ metrics: durationMsMetrics(event.durationMs),
22675
+ output: event.result
22676
+ });
22677
+ state.span.end();
22678
+ this.toolsById.delete(key);
22679
+ }
22680
+ handleTaskStart(event) {
22681
+ const parent = this.parentSpanForEvent(event);
22682
+ const metadata = {
22683
+ ...extractEventMetadata(event),
22684
+ ...event.role ? { "flue.role": event.role } : {},
22685
+ ...event.cwd ? { "flue.cwd": event.cwd } : {},
22686
+ "flue.task_id": event.taskId,
22687
+ provider: "flue"
22688
+ };
22689
+ const span = startFlueSpan(parent, {
22690
+ name: "flue.task",
22691
+ spanAttributes: { type: "task" /* TASK */ }
22692
+ });
22693
+ safeLog3(span, {
22694
+ input: event.prompt,
22695
+ metadata
22696
+ });
22697
+ this.tasksById.set(event.taskId, {
22698
+ metadata,
22699
+ span,
22700
+ startTime: getCurrentUnixTimestamp()
22701
+ });
22702
+ }
22703
+ handleTask(event) {
22704
+ const state = this.tasksById.get(event.taskId);
22705
+ if (!state) {
22706
+ return;
22707
+ }
22708
+ safeLog3(state.span, {
22709
+ ...event.isError ? { error: errorToString(event.result) } : {},
22710
+ metadata: {
22711
+ ...state.metadata,
22712
+ ...extractEventMetadata(event),
22713
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
22714
+ },
22715
+ metrics: durationMsMetrics(event.durationMs),
22716
+ output: event.result
22717
+ });
22718
+ state.span.end();
22719
+ this.tasksById.delete(event.taskId);
22720
+ }
22721
+ handleCompactionStart(event) {
22722
+ const operationState = this.operationStateForEvent(event);
22723
+ const parent = operationState?.span ?? this.parentSpanForEvent(event);
22724
+ const metadata = {
22725
+ ...extractEventMetadata(event),
22726
+ ...event.reason ? { "flue.compaction_reason": event.reason } : {},
22727
+ provider: "flue"
22728
+ };
22729
+ const input = {
22730
+ ...typeof event.estimatedTokens === "number" ? { estimatedTokens: event.estimatedTokens } : {},
22731
+ ...event.reason ? { reason: event.reason } : {}
22732
+ };
22733
+ const span = startFlueSpan(parent, {
22734
+ name: "flue.compaction",
22735
+ spanAttributes: { type: "task" /* TASK */ }
22736
+ });
22737
+ safeLog3(span, {
22738
+ input,
22739
+ metadata
22740
+ });
22741
+ this.compactionsByScope.set(scopeKey(event), {
22742
+ input,
22743
+ metadata,
22744
+ operationState,
22745
+ span,
22746
+ startTime: getCurrentUnixTimestamp()
22747
+ });
22748
+ }
22749
+ handleCompaction(event) {
22750
+ const key = scopeKey(event);
22751
+ const state = this.compactionsByScope.get(key) ?? this.findCompactionState(event);
22752
+ if (!state) {
22753
+ return;
22754
+ }
22755
+ safeLog3(state.span, {
22756
+ metadata: {
22757
+ ...state.metadata,
22758
+ ...extractEventMetadata(event),
22759
+ ...typeof event.messagesBefore === "number" ? { "flue.messages_before": event.messagesBefore } : {},
22760
+ ...typeof event.messagesAfter === "number" ? { "flue.messages_after": event.messagesAfter } : {}
22761
+ },
22762
+ metrics: {
22763
+ ...durationMsMetrics(event.durationMs),
22764
+ ...metricsFromUsage(event.usage)
22765
+ },
22766
+ output: {
22767
+ messagesAfter: event.messagesAfter,
22768
+ messagesBefore: event.messagesBefore
22769
+ }
22770
+ });
22771
+ state.span.end();
22772
+ this.deleteCompactionState(state);
22773
+ }
22774
+ findCompactionState(event) {
22775
+ const operationState = this.operationStateForEvent(event);
22776
+ for (const state of this.compactionsByScope.values()) {
22777
+ if (operationState && state.operationState === operationState) {
22778
+ return state;
22779
+ }
22780
+ }
22781
+ return void 0;
22782
+ }
22783
+ finishCompactionsForOperation(operationState) {
22784
+ for (const state of [...this.compactionsByScope.values()]) {
22785
+ if (state.operationState !== operationState) {
22786
+ continue;
22787
+ }
22788
+ safeLog3(state.span, {
22789
+ input: state.input,
22790
+ metadata: state.metadata,
22791
+ metrics: {
22792
+ ...buildDurationMetrics3(state.startTime)
22793
+ },
22794
+ output: { completed: true }
22795
+ });
22796
+ state.span.end();
22797
+ this.deleteCompactionState(state);
22798
+ }
22799
+ }
22800
+ deleteCompactionState(state) {
22801
+ for (const [key, candidate] of this.compactionsByScope) {
22802
+ if (candidate !== state) {
22803
+ continue;
22804
+ }
22805
+ this.compactionsByScope.delete(key);
22806
+ return;
22807
+ }
22808
+ }
22809
+ startSyntheticToolState(event, toolName) {
22810
+ const parent = this.parentSpanForEvent(event);
22811
+ const metadata = {
22812
+ ...extractEventMetadata(event),
22813
+ ...event.toolCallId ? { "flue.tool_call_id": event.toolCallId } : {},
22814
+ "flue.tool_name": toolName,
22815
+ provider: "flue"
22816
+ };
22817
+ const span = startFlueSpan(parent, {
22818
+ name: `tool: ${toolName}`,
22819
+ spanAttributes: { type: "tool" /* TOOL */ }
22820
+ });
22821
+ safeLog3(span, { metadata });
22822
+ return { metadata, span, startTime: getCurrentUnixTimestamp() };
22823
+ }
22824
+ operationStateForEvent(event) {
22825
+ if (event.operationId) {
22826
+ const operation = this.activeOperationsById.get(event.operationId) ?? this.promotePendingOperationForEvent(event);
22827
+ if (operation) {
22828
+ return operation;
22829
+ }
22830
+ }
22831
+ return this.activeOperationForEventScope(event) ?? this.pendingOperationForEventScope(event);
22832
+ }
22833
+ parentSpanForEvent(event) {
22834
+ if (event.operationId) {
22835
+ const operation = this.operationStateForEvent(event);
22836
+ if (operation) {
22837
+ return operation.span;
22838
+ }
22839
+ }
22840
+ if (event.taskId) {
22841
+ return this.tasksById.get(event.taskId)?.span;
22842
+ }
22843
+ return this.operationStateForEvent(event)?.span;
22844
+ }
22845
+ promotePendingOperationForEvent(event) {
22846
+ if (!event.operationId) {
22847
+ return void 0;
22848
+ }
22849
+ const scopePrefixes = operationScopePrefixes(event);
22850
+ for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
22851
+ if (!candidateQueue.length || !operationKeyMatchesScopes(candidateKey, scopePrefixes)) {
22852
+ continue;
22853
+ }
22854
+ const state = candidateQueue.shift();
22855
+ if (!state) {
22856
+ return void 0;
22857
+ }
22858
+ state.operationId = event.operationId;
22859
+ this.activeOperationsById.set(event.operationId, state);
22860
+ addScopedOperation(this.activeOperationsByScope, event, state);
22861
+ state.metadata = {
22862
+ ...state.metadata,
22863
+ ...extractEventMetadata(event),
22864
+ "flue.operation_id": event.operationId
22865
+ };
22866
+ safeLog3(state.span, { metadata: state.metadata });
22867
+ return state;
22868
+ }
22869
+ return void 0;
22870
+ }
22871
+ activeOperationForEventScope(event) {
22872
+ for (const scope of operationScopeNames(event)) {
22873
+ const operations = this.activeOperationsByScope.get(scope);
22874
+ if (operations?.length) {
22875
+ return operations[operations.length - 1];
22876
+ }
22877
+ }
22878
+ return void 0;
22879
+ }
22880
+ pendingOperationForEventScope(event) {
22881
+ const scopePrefixes = operationScopePrefixes(event);
22882
+ for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
22883
+ if (!candidateQueue.length || !operationKeyMatchesScopes(candidateKey, scopePrefixes)) {
22884
+ continue;
22885
+ }
22886
+ return candidateQueue[0];
22887
+ }
22888
+ return void 0;
22889
+ }
22890
+ takePendingOperationForEvent(event) {
22891
+ const key = operationKey(event.session, event.operationKind);
22892
+ const queue = this.pendingOperationsByKey.get(key);
22893
+ if (queue?.length) {
22894
+ return queue.shift();
22895
+ }
22896
+ for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
22897
+ if (candidateKey.endsWith(`::${event.operationKind}`) && candidateQueue.length) {
22898
+ return candidateQueue.shift();
22899
+ }
22900
+ }
22901
+ return void 0;
22902
+ }
22903
+ pendingOperationQueue(key) {
22904
+ const existing = this.pendingOperationsByKey.get(key);
22905
+ if (existing) {
22906
+ return existing;
22907
+ }
22908
+ const queue = [];
22909
+ this.pendingOperationsByKey.set(key, queue);
22910
+ return queue;
22911
+ }
22912
+ };
22913
+ function isInstrumentedOperation(operation) {
22914
+ return operation === "prompt" || operation === "skill" || operation === "task" || operation === "compact";
22915
+ }
22916
+ function getSessionName(session) {
22917
+ return typeof session?.name === "string" ? session.name : void 0;
22918
+ }
22919
+ function operationKey(sessionName, operation) {
22920
+ return `${sessionName ?? "unknown"}::${operation}`;
22921
+ }
22922
+ function operationScopePrefixes(event) {
22923
+ const scopes = /* @__PURE__ */ new Set();
22924
+ for (const scope of operationScopeNames(event)) {
22925
+ scopes.add(`${scope}::`);
22926
+ }
22927
+ return scopes;
22928
+ }
22929
+ function operationKeyMatchesScopes(key, scopes) {
22930
+ for (const scope of scopes) {
22931
+ if (key.startsWith(scope)) {
22932
+ return true;
22933
+ }
22934
+ }
22935
+ return false;
22936
+ }
22937
+ function operationScopeNames(event) {
22938
+ const scopes = /* @__PURE__ */ new Set();
22939
+ if (event.session) {
22940
+ scopes.add(event.session);
22941
+ }
22942
+ if (event.parentSession) {
22943
+ scopes.add(event.parentSession);
22944
+ }
22945
+ if (!scopes.size) {
22946
+ scopes.add("unknown");
22947
+ }
22948
+ return scopes;
22949
+ }
22950
+ function addScopedOperation(operationsByScope, event, state) {
22951
+ for (const scope of operationScopeNames(event)) {
22952
+ addOperationToScope(operationsByScope, scope, state);
22953
+ }
22954
+ }
22955
+ function addOperationToScope(operationsByScope, scope, state) {
22956
+ const operations = operationsByScope.get(scope);
22957
+ if (operations) {
22958
+ if (!operations.includes(state)) {
22959
+ operations.push(state);
22960
+ }
22961
+ } else {
22962
+ operationsByScope.set(scope, [state]);
22963
+ }
22964
+ }
22965
+ function removeScopedOperation(operationsByScope, state) {
22966
+ for (const [scope, operations] of operationsByScope) {
22967
+ const index = operations.indexOf(state);
22968
+ if (index === -1) {
22969
+ continue;
22970
+ }
22971
+ operations.splice(index, 1);
22972
+ if (operations.length === 0) {
22973
+ operationsByScope.delete(scope);
22974
+ }
22975
+ }
22976
+ }
22977
+ function removePendingOperation(pendingOperationsByKey, state) {
22978
+ for (const [key, queue] of pendingOperationsByKey) {
22979
+ const index = queue.indexOf(state);
22980
+ if (index === -1) {
22981
+ continue;
22982
+ }
22983
+ queue.splice(index, 1);
22984
+ if (queue.length === 0) {
22985
+ pendingOperationsByKey.delete(key);
22986
+ }
22987
+ return;
22988
+ }
22989
+ }
22990
+ function extractSessionMetadata(session) {
22991
+ const sessionName = getSessionName(session);
22992
+ return sessionName ? { "flue.session": sessionName } : {};
22993
+ }
22994
+ function extractEventMetadata(event) {
22995
+ return {
22996
+ ...event.runId ? { "flue.run_id": event.runId } : {},
22997
+ ...typeof event.eventIndex === "number" ? { "flue.event_index": event.eventIndex } : {},
22998
+ ...event.session ? { "flue.session": event.session } : {},
22999
+ ...event.parentSession ? { "flue.parent_session": event.parentSession } : {},
23000
+ ...event.harness ? { "flue.harness": event.harness } : {},
23001
+ ...event.taskId ? { "flue.task_id": event.taskId } : {},
23002
+ ...event.operationId ? { "flue.operation_id": event.operationId } : {}
23003
+ };
23004
+ }
23005
+ function extractOperationInput(operation, args) {
23006
+ switch (operation) {
23007
+ case "prompt":
23008
+ case "task":
23009
+ return args[0];
23010
+ case "skill":
23011
+ return {
23012
+ args: getOptionObject(args[1])?.args,
23013
+ name: args[0]
23014
+ };
23015
+ case "compact":
23016
+ return void 0;
23017
+ }
23018
+ }
23019
+ function extractOperationInputMetadata(operation, args) {
23020
+ const options = getOptionObject(args[1]);
23021
+ return {
23022
+ ...operation === "skill" && typeof args[0] === "string" ? { "flue.skill_name": args[0] } : {},
23023
+ ...options?.model ? { model: options.model, "flue.model": options.model } : {},
23024
+ ...options?.role ? { "flue.role": options.role } : {},
23025
+ ...options?.thinkingLevel ? { "flue.thinking_level": options.thinkingLevel } : {},
23026
+ ...typeof options?.cwd === "string" ? { "flue.cwd": options.cwd } : {},
23027
+ ...Array.isArray(options?.tools) ? {
23028
+ "flue.tools_count": options.tools.length,
23029
+ tools: summarizeTools(options.tools)
23030
+ } : {},
23031
+ ...Array.isArray(options?.images) ? { "flue.images_count": options.images.length } : {},
23032
+ ...options?.result || options?.schema ? { "flue.result_schema": true } : {}
23033
+ };
23034
+ }
23035
+ function getOptionObject(value) {
23036
+ return isObject(value) ? value : void 0;
23037
+ }
23038
+ function summarizeTools(tools) {
23039
+ return tools.flatMap((tool) => {
23040
+ if (!isObject(tool)) {
23041
+ return [];
23042
+ }
23043
+ const name = typeof tool.name === "string" ? tool.name : void 0;
23044
+ if (!name) {
23045
+ return [];
23046
+ }
23047
+ return [
23048
+ {
23049
+ function: {
23050
+ description: typeof tool.description === "string" ? tool.description : void 0,
23051
+ name,
23052
+ parameters: tool.parameters
23053
+ },
23054
+ type: "function"
23055
+ }
23056
+ ];
23057
+ });
23058
+ }
23059
+ function extractPromptResponseMetadata(result) {
23060
+ const modelId = result?.model && typeof result.model.id === "string" ? result.model.id : void 0;
23061
+ return modelId ? {
23062
+ model: modelId,
23063
+ "flue.model": modelId
23064
+ } : {};
23065
+ }
23066
+ function extractOperationOutput(result) {
23067
+ if (!result) {
23068
+ return void 0;
23069
+ }
23070
+ if ("data" in result) {
23071
+ return result.data;
23072
+ }
23073
+ if ("text" in result) {
23074
+ return result.text;
23075
+ }
23076
+ return result;
23077
+ }
23078
+ function metricsFromUsage(usage) {
23079
+ return {
23080
+ ...typeof usage?.input === "number" ? { prompt_tokens: usage.input } : {},
23081
+ ...typeof usage?.output === "number" ? { completion_tokens: usage.output } : {},
23082
+ ...typeof usage?.cacheRead === "number" ? { prompt_cached_tokens: usage.cacheRead } : {},
23083
+ ...typeof usage?.cacheWrite === "number" ? { prompt_cache_creation_tokens: usage.cacheWrite } : {},
23084
+ ...typeof usage?.totalTokens === "number" ? { tokens: usage.totalTokens } : {},
23085
+ ...typeof usage?.cost?.total === "number" ? { estimated_cost: usage.cost.total } : {}
23086
+ };
23087
+ }
23088
+ function buildDurationMetrics3(startTime) {
23089
+ return {
23090
+ duration_ms: Math.max(0, (getCurrentUnixTimestamp() - startTime) * 1e3)
23091
+ };
23092
+ }
23093
+ function durationMsMetrics(durationMs) {
23094
+ return typeof durationMs === "number" ? { duration_ms: durationMs } : {};
23095
+ }
23096
+ function scopeKey(event) {
23097
+ if (event.operationId) {
23098
+ return `operation:${event.operationId}`;
23099
+ }
23100
+ if (event.taskId) {
23101
+ return `task:${event.taskId}`;
23102
+ }
23103
+ if (event.session) {
23104
+ return `session:${event.session}`;
23105
+ }
23106
+ return "flue:unknown";
23107
+ }
23108
+ function toolKey(event) {
23109
+ return `${scopeKey(event)}::tool:${event.toolCallId ?? "unknown"}`;
23110
+ }
23111
+ function toAssistantOutput(text, finishReason, reasoning, toolCalls) {
23112
+ return [
23113
+ {
23114
+ finish_reason: finishReason ?? "stop",
23115
+ index: 0,
23116
+ message: {
23117
+ content: text,
23118
+ ...reasoning ? { reasoning } : {},
23119
+ role: "assistant",
23120
+ ...toolCalls?.length ? {
23121
+ tool_calls: toolCalls.map((toolCall) => ({
23122
+ function: {
23123
+ arguments: toolCall.args === void 0 ? "{}" : JSON.stringify(toolCall.args),
23124
+ name: toolCall.toolName ?? "unknown"
23125
+ },
23126
+ ...toolCall.toolCallId ? { id: toolCall.toolCallId } : {},
23127
+ type: "function"
23128
+ }))
23129
+ } : {}
23130
+ }
23131
+ }
23132
+ ];
23133
+ }
23134
+ function startFlueSpan(parent, args) {
23135
+ return parent ? withCurrent(parent, () => startSpan(args)) : startSpan(args);
23136
+ }
23137
+ function safeLog3(span, event) {
23138
+ try {
23139
+ span.log(event);
23140
+ } catch (error) {
23141
+ logInstrumentationError3("Flue span log", error);
23142
+ }
23143
+ }
23144
+ function errorToString(error) {
23145
+ if (error instanceof Error) {
23146
+ return error.message;
23147
+ }
23148
+ if (typeof error === "string") {
23149
+ return error;
23150
+ }
23151
+ try {
23152
+ return JSON.stringify(error);
23153
+ } catch {
23154
+ return String(error);
23155
+ }
23156
+ }
23157
+ function logInstrumentationError3(label, error) {
23158
+ console.error(`Error in ${label} instrumentation:`, error);
23159
+ }
23160
+
23161
+ // src/wrappers/langchain/callback-handler.ts
23162
+ var BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME = "BraintrustCallbackHandler";
23163
+ var BraintrustLangChainCallbackHandler = class {
23164
+ name = BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME;
23165
+ spans = /* @__PURE__ */ new Map();
23166
+ skippedRuns = /* @__PURE__ */ new Set();
23167
+ parent;
23168
+ rootRunId;
23169
+ options;
23170
+ startTimes = /* @__PURE__ */ new Map();
23171
+ firstTokenTimes = /* @__PURE__ */ new Map();
23172
+ ttftMs = /* @__PURE__ */ new Map();
23173
+ constructor(options) {
23174
+ this.parent = options?.parent;
23175
+ this.options = {
23176
+ debug: options?.debug ?? false,
23177
+ excludeMetadataProps: options?.excludeMetadataProps ?? /^(l[sc]_|langgraph_|__pregel_|checkpoint_ns)/,
23178
+ logger: options?.logger
23179
+ };
23180
+ }
23181
+ startSpan({
23182
+ runId,
23183
+ parentRunId,
23184
+ ...args
23185
+ }) {
23186
+ if (this.spans.has(runId)) {
23187
+ return;
23188
+ }
23189
+ if (!parentRunId) {
23190
+ this.rootRunId = runId;
23191
+ }
23192
+ const tags = args.event?.tags;
23193
+ const spanAttributes = args.spanAttributes || {};
23194
+ spanAttributes.type = args.type || spanAttributes.type || "task";
23195
+ args.type = spanAttributes.type;
23196
+ const currentParent = (typeof this.parent === "function" ? this.parent() : this.parent) ?? currentSpan();
23197
+ let parentSpan;
23198
+ if (parentRunId && this.spans.has(parentRunId)) {
23199
+ parentSpan = this.spans.get(parentRunId);
23200
+ } else if (!Object.is(currentParent, NOOP_SPAN)) {
23201
+ parentSpan = currentParent;
23202
+ } else if (this.options.logger) {
23203
+ parentSpan = this.options.logger;
23204
+ } else {
23205
+ parentSpan = { startSpan };
23206
+ }
23207
+ args.event = {
23208
+ ...args.event,
23209
+ tags: void 0,
23210
+ metadata: {
23211
+ ...tags ? { tags } : {},
23212
+ ...args.event?.metadata,
23213
+ braintrust: {
23214
+ integration_name: "langchain-js",
23215
+ sdk_language: "javascript"
23216
+ },
23217
+ run_id: runId,
23218
+ parent_run_id: parentRunId,
23219
+ ...this.options.debug ? { runId, parentRunId } : {}
23220
+ }
23221
+ };
23222
+ let span = parentSpan.startSpan(args);
23223
+ if (!Object.is(this.options.logger, NOOP_SPAN) && Object.is(span, NOOP_SPAN)) {
23224
+ span = initLogger().startSpan(args);
23225
+ }
23226
+ this.spans.set(runId, span);
23227
+ }
23228
+ endSpan({
23229
+ runId,
23230
+ parentRunId,
23231
+ tags,
23232
+ metadata,
23233
+ ...args
23234
+ }) {
23235
+ if (!this.spans.has(runId)) {
23236
+ return;
23237
+ }
23238
+ if (this.skippedRuns.has(runId)) {
23239
+ this.skippedRuns.delete(runId);
23240
+ return;
23241
+ }
23242
+ const span = this.spans.get(runId);
23243
+ this.spans.delete(runId);
23244
+ if (runId === this.rootRunId) {
23245
+ this.rootRunId = void 0;
23246
+ }
23247
+ span.log({ ...args, metadata: { tags, ...metadata } });
23248
+ span.end();
23249
+ }
23250
+ async handleLLMStart(llm, prompts, runId, parentRunId, extraParams, tags, metadata, runName) {
23251
+ this.startSpan({
23252
+ runId,
23253
+ parentRunId,
23254
+ name: runName ?? getSerializedName(llm) ?? "LLM",
23255
+ type: "llm",
23256
+ event: {
23257
+ input: prompts,
23258
+ tags,
23259
+ metadata: {
23260
+ serialized: llm,
23261
+ name: runName,
23262
+ metadata,
23263
+ ...extraParams
23264
+ }
23265
+ }
23266
+ });
23267
+ }
23268
+ async handleLLMError(err, runId, parentRunId, tags) {
23269
+ this.endSpan({ runId, parentRunId, error: err, tags });
23270
+ }
23271
+ async handleLLMEnd(output, runId, parentRunId, tags) {
23272
+ const metrics = getMetricsFromResponse(output);
23273
+ const modelName2 = getModelNameFromResponse(output);
23274
+ const ttft = this.ttftMs.get(runId);
23275
+ if (ttft !== void 0) {
23276
+ metrics.time_to_first_token = ttft;
23277
+ }
23278
+ this.startTimes.delete(runId);
23279
+ this.firstTokenTimes.delete(runId);
23280
+ this.ttftMs.delete(runId);
23281
+ this.endSpan({
23282
+ runId,
23283
+ parentRunId,
23284
+ output,
23285
+ metrics,
23286
+ tags,
23287
+ metadata: {
23288
+ model: modelName2
23289
+ }
23290
+ });
23291
+ }
23292
+ async handleChatModelStart(llm, messages, runId, parentRunId, extraParams, tags, metadata, runName) {
23293
+ this.startTimes.set(runId, Date.now());
23294
+ this.firstTokenTimes.delete(runId);
23295
+ this.ttftMs.delete(runId);
23296
+ this.startSpan({
23297
+ runId,
23298
+ parentRunId,
23299
+ name: runName ?? getSerializedName(llm) ?? "Chat Model",
23300
+ type: "llm",
23301
+ event: {
23302
+ input: messages,
23303
+ tags,
23304
+ metadata: {
23305
+ serialized: llm,
23306
+ name: runName,
23307
+ metadata,
23308
+ ...extraParams
23309
+ }
23310
+ }
23311
+ });
23312
+ }
23313
+ async handleChainStart(chain, inputs, runId, parentRunId, tags, metadata, runType, runName) {
23314
+ if (tags?.includes("langsmith:hidden")) {
23315
+ this.skippedRuns.add(runId);
23316
+ return;
23317
+ }
23318
+ this.startSpan({
23319
+ runId,
23320
+ parentRunId,
23321
+ name: runName ?? getSerializedName(chain) ?? "Chain",
23322
+ event: {
23323
+ input: inputs,
23324
+ tags,
23325
+ metadata: {
23326
+ serialized: chain,
23327
+ name: runName,
23328
+ metadata,
23329
+ run_type: runType
23330
+ }
23331
+ }
23332
+ });
23333
+ }
23334
+ async handleChainError(err, runId, parentRunId, tags, kwargs) {
23335
+ this.endSpan({ runId, parentRunId, error: err, tags, metadata: kwargs });
23336
+ }
23337
+ async handleChainEnd(outputs, runId, parentRunId, tags, kwargs) {
23338
+ this.endSpan({
23339
+ runId,
23340
+ parentRunId,
23341
+ tags,
23342
+ output: outputs,
23343
+ metadata: { ...kwargs }
23344
+ });
23345
+ }
23346
+ async handleToolStart(tool, input, runId, parentRunId, tags, metadata, runName) {
23347
+ this.startSpan({
23348
+ runId,
23349
+ parentRunId,
23350
+ name: runName ?? getSerializedName(tool) ?? "Tool",
23351
+ type: "llm",
23352
+ event: {
23353
+ input: safeJsonParse(input),
23354
+ tags,
23355
+ metadata: {
23356
+ metadata,
23357
+ serialized: tool,
23358
+ input_str: input,
23359
+ input: safeJsonParse(input),
23360
+ name: runName
23361
+ }
23362
+ }
23363
+ });
23364
+ }
23365
+ async handleToolError(err, runId, parentRunId, tags) {
23366
+ this.endSpan({ runId, parentRunId, error: err, tags });
23367
+ }
23368
+ async handleToolEnd(output, runId, parentRunId, tags) {
23369
+ this.endSpan({ runId, parentRunId, output, tags });
23370
+ }
23371
+ async handleAgentAction(action, runId, parentRunId, tags) {
23372
+ this.startSpan({
23373
+ runId,
23374
+ parentRunId,
23375
+ type: "llm",
23376
+ name: typeof action.tool === "string" ? action.tool : "Agent",
23377
+ event: {
23378
+ input: action,
23379
+ tags
23380
+ }
23381
+ });
23382
+ }
23383
+ async handleAgentEnd(action, runId, parentRunId, tags) {
23384
+ this.endSpan({ runId, parentRunId, output: action, tags });
23385
+ }
23386
+ async handleRetrieverStart(retriever, query, runId, parentRunId, tags, metadata, name) {
23387
+ this.startSpan({
23388
+ runId,
23389
+ parentRunId,
23390
+ name: name ?? getSerializedName(retriever) ?? "Retriever",
23391
+ type: "function",
23392
+ event: {
23393
+ input: query,
23394
+ tags,
23395
+ metadata: {
23396
+ serialized: retriever,
23397
+ metadata,
23398
+ name
23399
+ }
23400
+ }
23401
+ });
23402
+ }
23403
+ async handleRetrieverEnd(documents, runId, parentRunId, tags) {
23404
+ this.endSpan({ runId, parentRunId, output: documents, tags });
23405
+ }
23406
+ async handleRetrieverError(err, runId, parentRunId, tags) {
23407
+ this.endSpan({ runId, parentRunId, error: err, tags });
23408
+ }
23409
+ async handleLLMNewToken(_token, _idx, runId, _parentRunId, _tags) {
23410
+ if (!this.firstTokenTimes.has(runId)) {
23411
+ const now2 = Date.now();
23412
+ this.firstTokenTimes.set(runId, now2);
23413
+ const start = this.startTimes.get(runId);
23414
+ if (start !== void 0) {
23415
+ this.ttftMs.set(runId, (now2 - start) / 1e3);
23416
+ }
23417
+ }
23418
+ }
23419
+ };
23420
+ function getSerializedName(serialized) {
23421
+ if (typeof serialized.name === "string") {
23422
+ return serialized.name;
23423
+ }
23424
+ const lastIdPart = serialized.id?.at(-1);
23425
+ return typeof lastIdPart === "string" ? lastIdPart : void 0;
23426
+ }
23427
+ function cleanObject(obj) {
23428
+ return Object.fromEntries(
23429
+ Object.entries(obj).filter(([, value]) => {
23430
+ if (typeof value !== "number") {
23431
+ return false;
23432
+ }
23433
+ return Number.isFinite(value);
23434
+ })
23435
+ );
23436
+ }
23437
+ function walkGenerations(response) {
23438
+ const result = [];
23439
+ const generations = response.generations || [];
23440
+ for (const batch of generations) {
23441
+ if (Array.isArray(batch)) {
23442
+ for (const generation of batch) {
23443
+ if (isRecord(generation)) {
23444
+ result.push(generation);
23445
+ }
23446
+ }
23447
+ } else if (isRecord(batch)) {
23448
+ result.push(batch);
23449
+ }
23450
+ }
23451
+ return result;
23452
+ }
23453
+ function getModelNameFromResponse(response) {
23454
+ for (const generation of walkGenerations(response)) {
23455
+ const message = generation.message;
23456
+ if (!isRecord(message)) {
23457
+ continue;
23458
+ }
23459
+ const responseMetadata = message.response_metadata;
23460
+ if (!isRecord(responseMetadata)) {
23461
+ continue;
23462
+ }
23463
+ const modelName3 = responseMetadata.model_name ?? responseMetadata.model;
23464
+ if (typeof modelName3 === "string") {
23465
+ return modelName3;
23466
+ }
23467
+ }
23468
+ const llmOutput = response.llmOutput || {};
23469
+ const modelName2 = llmOutput.model_name ?? llmOutput.model;
23470
+ return typeof modelName2 === "string" ? modelName2 : void 0;
23471
+ }
23472
+ function getMetricsFromResponse(response) {
23473
+ for (const generation of walkGenerations(response)) {
23474
+ const message = generation.message;
23475
+ if (!isRecord(message)) {
23476
+ continue;
23477
+ }
23478
+ const usageMetadata = message.usage_metadata;
23479
+ if (!isRecord(usageMetadata)) {
23480
+ continue;
23481
+ }
23482
+ const inputTokenDetails = usageMetadata.input_token_details;
23483
+ return cleanObject({
23484
+ total_tokens: usageMetadata.total_tokens,
23485
+ prompt_tokens: usageMetadata.input_tokens,
23486
+ completion_tokens: usageMetadata.output_tokens,
23487
+ prompt_cache_creation_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_creation : void 0,
23488
+ prompt_cached_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_read : void 0
23489
+ });
23490
+ }
23491
+ const llmOutput = response.llmOutput || {};
23492
+ const tokenUsage = isRecord(llmOutput.tokenUsage) ? llmOutput.tokenUsage : isRecord(llmOutput.estimatedTokens) ? llmOutput.estimatedTokens : {};
23493
+ return cleanObject({
23494
+ total_tokens: tokenUsage.totalTokens,
23495
+ prompt_tokens: tokenUsage.promptTokens,
23496
+ completion_tokens: tokenUsage.completionTokens
23497
+ });
23498
+ }
23499
+ function safeJsonParse(input) {
23500
+ try {
23501
+ return JSON.parse(input);
23502
+ } catch {
23503
+ return input;
23504
+ }
23505
+ }
23506
+ function isRecord(value) {
23507
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23508
+ }
23509
+
23510
+ // src/instrumentation/plugins/langchain-channels.ts
23511
+ var langChainChannels = defineChannels("@langchain/core", {
23512
+ configure: channel({
23513
+ channelName: "CallbackManager.configure",
23514
+ kind: "sync-stream"
23515
+ }),
23516
+ configureSync: channel({
23517
+ channelName: "CallbackManager._configureSync",
23518
+ kind: "sync-stream"
23519
+ })
23520
+ });
23521
+
23522
+ // src/instrumentation/plugins/langchain-plugin.ts
23523
+ var LangChainPlugin = class extends BasePlugin {
23524
+ injectedManagers = /* @__PURE__ */ new WeakSet();
23525
+ onEnable() {
23526
+ this.subscribeToConfigure(langChainChannels.configure);
23527
+ this.subscribeToConfigure(langChainChannels.configureSync);
23528
+ }
23529
+ onDisable() {
23530
+ for (const unsubscribe of this.unsubscribers) {
23531
+ unsubscribe();
23532
+ }
23533
+ this.unsubscribers = [];
23534
+ this.injectedManagers = /* @__PURE__ */ new WeakSet();
23535
+ }
23536
+ subscribeToConfigure(channel2) {
23537
+ const tracingChannel = channel2.tracingChannel();
23538
+ const handlers = {
23539
+ start: (event) => {
23540
+ injectHandlerIntoArguments(event.arguments);
23541
+ },
23542
+ end: (event) => {
23543
+ this.injectHandler(event.result);
23544
+ }
23545
+ };
23546
+ tracingChannel.subscribe(handlers);
23547
+ this.unsubscribers.push(() => {
23548
+ tracingChannel.unsubscribe(handlers);
23549
+ });
23550
+ }
23551
+ injectHandler(result) {
23552
+ if (!isCallbackManager(result)) {
23553
+ return;
23554
+ }
23555
+ if (this.injectedManagers.has(result) || hasBraintrustHandler(result)) {
23556
+ return;
23557
+ }
23558
+ try {
23559
+ result.addHandler(new BraintrustLangChainCallbackHandler(), true);
23560
+ this.injectedManagers.add(result);
23561
+ } catch {
23562
+ }
23563
+ }
23564
+ };
23565
+ function isCallbackManager(value) {
23566
+ if (typeof value !== "object" || value === null) {
23567
+ return false;
23568
+ }
23569
+ const maybeManager = value;
23570
+ return typeof maybeManager.addHandler === "function";
23571
+ }
23572
+ function hasBraintrustHandler(manager) {
23573
+ return manager.handlers?.some((handler) => {
23574
+ if (typeof handler !== "object" || handler === null) {
23575
+ return false;
23576
+ }
23577
+ const name = Reflect.get(handler, "name");
23578
+ return name === BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME;
23579
+ }) ?? false;
23580
+ }
23581
+ function injectHandlerIntoArguments(args) {
23582
+ if (!isWritableArgumentsObject(args)) {
23583
+ return;
23584
+ }
23585
+ const inheritedHandlers = Reflect.get(args, "0");
23586
+ const handler = new BraintrustLangChainCallbackHandler();
23587
+ if (inheritedHandlers === void 0 || inheritedHandlers === null) {
23588
+ Reflect.set(args, "0", [handler]);
23589
+ return;
23590
+ }
23591
+ if (Array.isArray(inheritedHandlers)) {
23592
+ if (!inheritedHandlers.some(isBraintrustHandler)) {
23593
+ inheritedHandlers.push(handler);
23594
+ }
23595
+ }
23596
+ }
23597
+ function isWritableArgumentsObject(args) {
23598
+ return typeof args === "object" && args !== null;
23599
+ }
23600
+ function isBraintrustHandler(handler) {
23601
+ if (typeof handler !== "object" || handler === null) {
23602
+ return false;
23603
+ }
23604
+ return Reflect.get(handler, "name") === BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME;
23605
+ }
23606
+
23607
+ // src/instrumentation/braintrust-plugin.ts
23608
+ function getIntegrationConfig(integrations, key) {
23609
+ return integrations[key];
23610
+ }
23611
+ var BraintrustPlugin = class extends BasePlugin {
23612
+ config;
23613
+ openaiPlugin = null;
23614
+ openAICodexPlugin = null;
23615
+ anthropicPlugin = null;
23616
+ aiSDKPlugin = null;
23617
+ claudeAgentSDKPlugin = null;
23618
+ cursorSDKPlugin = null;
23619
+ openAIAgentsPlugin = null;
23620
+ googleGenAIPlugin = null;
23621
+ huggingFacePlugin = null;
23622
+ openRouterPlugin = null;
23623
+ openRouterAgentPlugin = null;
23624
+ mistralPlugin = null;
23625
+ googleADKPlugin = null;
23626
+ coherePlugin = null;
23627
+ groqPlugin = null;
23628
+ genkitPlugin = null;
23629
+ gitHubCopilotPlugin = null;
23630
+ fluePlugin = null;
23631
+ langChainPlugin = null;
23632
+ constructor(config = {}) {
23633
+ super();
23634
+ this.config = config;
23635
+ }
23636
+ onEnable() {
23637
+ const integrations = this.config.integrations || {};
23638
+ if (integrations.openai !== false) {
23639
+ this.openaiPlugin = new OpenAIPlugin();
23640
+ this.openaiPlugin.enable();
23641
+ }
23642
+ if (integrations.openaiCodexSDK !== false) {
23643
+ this.openAICodexPlugin = new OpenAICodexPlugin();
23644
+ this.openAICodexPlugin.enable();
23645
+ }
23646
+ if (integrations.anthropic !== false) {
23647
+ this.anthropicPlugin = new AnthropicPlugin();
23648
+ this.anthropicPlugin.enable();
23649
+ }
23650
+ if (integrations.aisdk !== false && integrations.vercel !== false) {
23651
+ this.aiSDKPlugin = new AISDKPlugin();
23652
+ this.aiSDKPlugin.enable();
21418
23653
  }
21419
23654
  if (integrations.claudeAgentSDK !== false) {
21420
23655
  this.claudeAgentSDKPlugin = new ClaudeAgentSDKPlugin();
@@ -21424,6 +23659,10 @@ var BraintrustPlugin = class extends BasePlugin {
21424
23659
  this.cursorSDKPlugin = new CursorSDKPlugin();
21425
23660
  this.cursorSDKPlugin.enable();
21426
23661
  }
23662
+ if (integrations.openAIAgents !== false) {
23663
+ this.openAIAgentsPlugin = new OpenAIAgentsPlugin();
23664
+ this.openAIAgentsPlugin.enable();
23665
+ }
21427
23666
  if (integrations.googleGenAI !== false && integrations.google !== false) {
21428
23667
  this.googleGenAIPlugin = new GoogleGenAIPlugin();
21429
23668
  this.googleGenAIPlugin.enable();
@@ -21464,6 +23703,14 @@ var BraintrustPlugin = class extends BasePlugin {
21464
23703
  this.gitHubCopilotPlugin = new GitHubCopilotPlugin();
21465
23704
  this.gitHubCopilotPlugin.enable();
21466
23705
  }
23706
+ if (getIntegrationConfig(integrations, "flue") !== false) {
23707
+ this.fluePlugin = new FluePlugin();
23708
+ this.fluePlugin.enable();
23709
+ }
23710
+ if (integrations.langchain !== false && integrations.langgraph !== false) {
23711
+ this.langChainPlugin = new LangChainPlugin();
23712
+ this.langChainPlugin.enable();
23713
+ }
21467
23714
  }
21468
23715
  onDisable() {
21469
23716
  if (this.openaiPlugin) {
@@ -21490,6 +23737,10 @@ var BraintrustPlugin = class extends BasePlugin {
21490
23737
  this.cursorSDKPlugin.disable();
21491
23738
  this.cursorSDKPlugin = null;
21492
23739
  }
23740
+ if (this.openAIAgentsPlugin) {
23741
+ this.openAIAgentsPlugin.disable();
23742
+ this.openAIAgentsPlugin = null;
23743
+ }
21493
23744
  if (this.googleGenAIPlugin) {
21494
23745
  this.googleGenAIPlugin.disable();
21495
23746
  this.googleGenAIPlugin = null;
@@ -21530,9 +23781,104 @@ var BraintrustPlugin = class extends BasePlugin {
21530
23781
  this.gitHubCopilotPlugin.disable();
21531
23782
  this.gitHubCopilotPlugin = null;
21532
23783
  }
23784
+ if (this.fluePlugin) {
23785
+ this.fluePlugin.disable();
23786
+ this.fluePlugin = null;
23787
+ }
23788
+ if (this.langChainPlugin) {
23789
+ this.langChainPlugin.disable();
23790
+ this.langChainPlugin = null;
23791
+ }
21533
23792
  }
21534
23793
  };
21535
23794
 
23795
+ // src/instrumentation/config.ts
23796
+ var envIntegrationAliases = {
23797
+ openai: "openai",
23798
+ "openai-codex": "openaiCodexSDK",
23799
+ "openai-codex-sdk": "openaiCodexSDK",
23800
+ openaicodexsdk: "openaiCodexSDK",
23801
+ codex: "openaiCodexSDK",
23802
+ "codex-sdk": "openaiCodexSDK",
23803
+ anthropic: "anthropic",
23804
+ aisdk: "aisdk",
23805
+ "ai-sdk": "aisdk",
23806
+ "vercel-ai": "aisdk",
23807
+ vercel: "vercel",
23808
+ claudeagentsdk: "claudeAgentSDK",
23809
+ "claude-agent-sdk": "claudeAgentSDK",
23810
+ cursor: "cursor",
23811
+ "cursor-sdk": "cursorSDK",
23812
+ cursorsdk: "cursorSDK",
23813
+ flue: "flue",
23814
+ "flue-runtime": "flue",
23815
+ "openai-agents": "openAIAgents",
23816
+ openaiagents: "openAIAgents",
23817
+ "openai-agents-core": "openAIAgents",
23818
+ openaiagentscore: "openAIAgents",
23819
+ google: "google",
23820
+ "google-genai": "googleGenAI",
23821
+ googlegenai: "googleGenAI",
23822
+ huggingface: "huggingface",
23823
+ openrouter: "openrouter",
23824
+ openrouteragent: "openrouterAgent",
23825
+ "openrouter-agent": "openrouterAgent",
23826
+ mistral: "mistral",
23827
+ googleadk: "googleADK",
23828
+ "google-adk": "googleADK",
23829
+ cohere: "cohere",
23830
+ groq: "groq",
23831
+ "groq-sdk": "groq",
23832
+ genkit: "genkit",
23833
+ "firebase-genkit": "genkit",
23834
+ githubcopilot: "gitHubCopilot",
23835
+ "github-copilot": "gitHubCopilot",
23836
+ "copilot-sdk": "gitHubCopilot",
23837
+ langchain: "langchain",
23838
+ "langchain-js": "langchain",
23839
+ "@langchain": "langchain",
23840
+ langgraph: "langgraph"
23841
+ };
23842
+ function getDefaultInstrumentationIntegrations() {
23843
+ return {
23844
+ openai: true,
23845
+ openaiCodexSDK: true,
23846
+ anthropic: true,
23847
+ vercel: true,
23848
+ aisdk: true,
23849
+ google: true,
23850
+ googleGenAI: true,
23851
+ googleADK: true,
23852
+ huggingface: true,
23853
+ claudeAgentSDK: true,
23854
+ cursor: true,
23855
+ cursorSDK: true,
23856
+ flue: true,
23857
+ openAIAgents: true,
23858
+ openrouter: true,
23859
+ openrouterAgent: true,
23860
+ mistral: true,
23861
+ cohere: true,
23862
+ groq: true,
23863
+ genkit: true,
23864
+ gitHubCopilot: true,
23865
+ langchain: true,
23866
+ langgraph: true
23867
+ };
23868
+ }
23869
+ function readDisabledInstrumentationEnvConfig(disabledList) {
23870
+ const integrations = {};
23871
+ if (disabledList) {
23872
+ for (const value of disabledList.split(",")) {
23873
+ const sdk = value.trim().toLowerCase();
23874
+ if (sdk.length > 0) {
23875
+ integrations[envIntegrationAliases[sdk] ?? sdk] = false;
23876
+ }
23877
+ }
23878
+ }
23879
+ return { integrations };
23880
+ }
23881
+
21536
23882
  // src/instrumentation/registry.ts
21537
23883
  var REGISTRY_STATE_KEY = /* @__PURE__ */ Symbol.for("braintrust.registry");
21538
23884
  function getSharedState() {
@@ -21611,50 +23957,16 @@ var PluginRegistry = class {
21611
23957
  * Get default configuration (all integrations enabled).
21612
23958
  */
21613
23959
  getDefaultConfig() {
21614
- return {
21615
- openai: true,
21616
- openaiCodexSDK: true,
21617
- anthropic: true,
21618
- vercel: true,
21619
- aisdk: true,
21620
- google: true,
21621
- googleGenAI: true,
21622
- googleADK: true,
21623
- huggingface: true,
21624
- claudeAgentSDK: true,
21625
- cursor: true,
21626
- cursorSDK: true,
21627
- openrouter: true,
21628
- openrouterAgent: true,
21629
- mistral: true,
21630
- cohere: true,
21631
- groq: true,
21632
- genkit: true,
21633
- gitHubCopilot: true
21634
- };
23960
+ return getDefaultInstrumentationIntegrations();
21635
23961
  }
21636
23962
  /**
21637
23963
  * Read configuration from environment variables.
21638
23964
  * Supports: BRAINTRUST_DISABLE_INSTRUMENTATION=openai,anthropic,...
21639
23965
  */
21640
23966
  readEnvConfig() {
21641
- const integrations = {};
21642
- const disabledList = isomorph_default.getEnv("BRAINTRUST_DISABLE_INSTRUMENTATION");
21643
- if (disabledList) {
21644
- const disabled = disabledList.split(",").map((s) => s.trim().toLowerCase()).filter((s) => s.length > 0);
21645
- for (const sdk of disabled) {
21646
- if (sdk === "cursor-sdk") {
21647
- integrations.cursorSDK = false;
21648
- } else if (sdk === "githubcopilot" || sdk === "github-copilot" || sdk === "copilot-sdk") {
21649
- integrations.gitHubCopilot = false;
21650
- } else if (sdk === "openai-codex-sdk") {
21651
- integrations.openaiCodexSDK = false;
21652
- } else {
21653
- integrations[sdk] = false;
21654
- }
21655
- }
21656
- }
21657
- return { integrations };
23967
+ return readDisabledInstrumentationEnvConfig(
23968
+ isomorph_default.getEnv("BRAINTRUST_DISABLE_INSTRUMENTATION")
23969
+ );
21658
23970
  }
21659
23971
  };
21660
23972
  var registry = new PluginRegistry();
@@ -21665,6 +23977,7 @@ function configureInstrumentation(config) {
21665
23977
  0 && (module.exports = {
21666
23978
  BasePlugin,
21667
23979
  BraintrustPlugin,
23980
+ OpenAIAgentsTraceProcessor,
21668
23981
  configureInstrumentation,
21669
23982
  createChannelName,
21670
23983
  isValidChannelName,