braintrust 3.13.0 → 3.15.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 (50) hide show
  1. package/dev/dist/index.d.mts +6 -8
  2. package/dev/dist/index.d.ts +6 -8
  3. package/dev/dist/index.js +1399 -1466
  4. package/dev/dist/index.mjs +971 -1038
  5. package/dist/apply-auto-instrumentation.js +208 -188
  6. package/dist/apply-auto-instrumentation.mjs +40 -20
  7. package/dist/auto-instrumentations/bundler/esbuild.cjs +230 -40
  8. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -1
  9. package/dist/auto-instrumentations/bundler/next.cjs +230 -40
  10. package/dist/auto-instrumentations/bundler/next.mjs +3 -2
  11. package/dist/auto-instrumentations/bundler/rollup.cjs +230 -40
  12. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -1
  13. package/dist/auto-instrumentations/bundler/vite.cjs +230 -40
  14. package/dist/auto-instrumentations/bundler/vite.mjs +2 -1
  15. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +13 -39
  16. package/dist/auto-instrumentations/bundler/webpack.cjs +230 -40
  17. package/dist/auto-instrumentations/bundler/webpack.mjs +3 -2
  18. package/dist/auto-instrumentations/{chunk-WFEUJACP.mjs → chunk-CNQ7BUKN.mjs} +1 -1
  19. package/dist/auto-instrumentations/chunk-J57YF7WS.mjs +208 -0
  20. package/dist/auto-instrumentations/chunk-QFMACSOL.mjs +280 -0
  21. package/dist/auto-instrumentations/{chunk-GJOO4ESL.mjs → chunk-VXJONZVX.mjs} +28 -40
  22. package/dist/auto-instrumentations/hook.mjs +7985 -46
  23. package/dist/auto-instrumentations/loader/cjs-patch.cjs +194 -4
  24. package/dist/auto-instrumentations/loader/cjs-patch.mjs +13 -27
  25. package/dist/auto-instrumentations/loader/esm-hook.mjs +24 -10
  26. package/dist/browser.d.mts +138 -26
  27. package/dist/browser.d.ts +138 -26
  28. package/dist/browser.js +898 -1063
  29. package/dist/browser.mjs +898 -1063
  30. package/dist/{chunk-75IQCUB2.mjs → chunk-O4ZIWXO3.mjs} +179 -25
  31. package/dist/{chunk-26JGOELH.js → chunk-VMBQETG3.js} +179 -25
  32. package/dist/cli.js +990 -1077
  33. package/dist/edge-light.d.mts +1 -1
  34. package/dist/edge-light.d.ts +1 -1
  35. package/dist/edge-light.js +898 -1063
  36. package/dist/edge-light.mjs +898 -1063
  37. package/dist/index.d.mts +138 -26
  38. package/dist/index.d.ts +138 -26
  39. package/dist/index.js +1690 -1825
  40. package/dist/index.mjs +918 -1053
  41. package/dist/instrumentation/index.d.mts +18 -9
  42. package/dist/instrumentation/index.d.ts +18 -9
  43. package/dist/instrumentation/index.js +711 -1038
  44. package/dist/instrumentation/index.mjs +710 -1038
  45. package/dist/workerd.d.mts +1 -1
  46. package/dist/workerd.d.ts +1 -1
  47. package/dist/workerd.js +898 -1063
  48. package/dist/workerd.mjs +898 -1063
  49. package/package.json +1 -7
  50. package/dist/auto-instrumentations/chunk-MWZXZQUO.mjs +0 -81
package/dist/cli.js CHANGED
@@ -1232,7 +1232,7 @@ var require_package = __commonJS({
1232
1232
  "package.json"(exports2, module2) {
1233
1233
  module2.exports = {
1234
1234
  name: "braintrust",
1235
- version: "3.13.0",
1235
+ version: "3.15.0",
1236
1236
  description: "SDK for integrating Braintrust",
1237
1237
  repository: {
1238
1238
  type: "git",
@@ -1394,17 +1394,13 @@ var require_package = __commonJS({
1394
1394
  author: "",
1395
1395
  license: "MIT",
1396
1396
  devDependencies: {
1397
- "@ai-sdk/anthropic": "2.0.37",
1398
1397
  "@anthropic-ai/sdk": "^0.60.0",
1399
- "@google/adk": "^0.6.1",
1400
- "@google/genai": "^1.25.0",
1401
1398
  "@nodelib/fs.walk": "^1.2.8",
1402
1399
  "@types/argparse": "^2.0.14",
1403
1400
  "@types/async": "^3.2.24",
1404
1401
  "@types/cli-progress": "^3.11.5",
1405
1402
  "@types/cors": "^2.8.17",
1406
1403
  "@types/express": "^5.0.0",
1407
- "@types/graceful-fs": "^4.1.9",
1408
1404
  "@types/http-errors": "^2.0.4",
1409
1405
  "@types/mustache": "^4.2.5",
1410
1406
  "@types/node": "^20.10.5",
@@ -1414,7 +1410,6 @@ var require_package = __commonJS({
1414
1410
  "@typescript-eslint/parser": "^8.49.0",
1415
1411
  ai: "^6.0.0",
1416
1412
  async: "^3.2.5",
1417
- autoevals: "^0.0.131",
1418
1413
  "cross-env": "^7.0.3",
1419
1414
  "eslint-plugin-node-import": "^1.0.5",
1420
1415
  openai: "6.25.0",
@@ -1446,7 +1441,6 @@ var require_package = __commonJS({
1446
1441
  esbuild: "0.28.0",
1447
1442
  "eventsource-parser": "^1.1.2",
1448
1443
  express: "^5.2.1",
1449
- "graceful-fs": "^4.2.11",
1450
1444
  "http-errors": "^2.0.0",
1451
1445
  minimatch: "^10.2.5",
1452
1446
  "module-details-from-path": "^1.0.4",
@@ -1481,7 +1475,7 @@ __export(cli_exports, {
1481
1475
  module.exports = __toCommonJS(cli_exports);
1482
1476
  var esbuild = __toESM(require("esbuild"));
1483
1477
  var dotenv3 = __toESM(require("dotenv"));
1484
- var import_node_fs2 = __toESM(require("node:fs"));
1478
+ var import_node_fs3 = __toESM(require("node:fs"));
1485
1479
  var import_node_os = __toESM(require("node:os"));
1486
1480
  var import_node_path5 = __toESM(require("node:path"));
1487
1481
  var import_node_util5 = __toESM(require("node:util"));
@@ -5443,6 +5437,76 @@ var LRUCache = class {
5443
5437
  }
5444
5438
  };
5445
5439
 
5440
+ // src/prompt-cache/cache-config.ts
5441
+ var CACHE_LOCATION_ENV_VAR = "BRAINTRUST_CACHE_LOCATION";
5442
+ var DEFAULT_CACHE_MEMORY_MAX = 1 << 10;
5443
+ var DEFAULT_CACHE_DISK_MAX = 1 << 20;
5444
+ var warnedInvalidCacheModeEnvValue = false;
5445
+ var warnedUnavailableDiskCacheMode = false;
5446
+ function warnInvalidCacheMode(value) {
5447
+ if (warnedInvalidCacheModeEnvValue) {
5448
+ return;
5449
+ }
5450
+ warnedInvalidCacheModeEnvValue = true;
5451
+ debugLogger.warn(
5452
+ `Invalid ${CACHE_LOCATION_ENV_VAR} value "${value}". Expected "mixed", "memory", "disk", or "none". Falling back to "mixed".`
5453
+ );
5454
+ }
5455
+ function warnUnavailableDiskCache() {
5456
+ if (warnedUnavailableDiskCacheMode) {
5457
+ return;
5458
+ }
5459
+ warnedUnavailableDiskCacheMode = true;
5460
+ debugLogger.warn(
5461
+ `Disk cache is not supported on this platform, so ${CACHE_LOCATION_ENV_VAR}="disk" disables prompt and parameters caching.`
5462
+ );
5463
+ }
5464
+ function parseCacheMode() {
5465
+ const value = isomorph_default.getEnv(CACHE_LOCATION_ENV_VAR);
5466
+ const normalized = value?.trim().toLowerCase();
5467
+ if (!normalized) {
5468
+ return "mixed";
5469
+ }
5470
+ if (normalized === "mixed" || normalized === "memory" || normalized === "disk" || normalized === "none") {
5471
+ return normalized;
5472
+ }
5473
+ warnInvalidCacheMode(value ?? "");
5474
+ return "mixed";
5475
+ }
5476
+ function parsePositiveIntegerEnv(envVar, defaultValue) {
5477
+ const value = Number(isomorph_default.getEnv(envVar));
5478
+ return Number.isInteger(value) && value > 0 ? value : defaultValue;
5479
+ }
5480
+ function createCacheLayers({
5481
+ memoryMaxEnvVar,
5482
+ diskCacheDirEnvVar,
5483
+ diskMaxEnvVar,
5484
+ getDefaultDiskCacheDir
5485
+ }) {
5486
+ const mode = parseCacheMode();
5487
+ const memoryCache = mode === "mixed" || mode === "memory" ? new LRUCache({
5488
+ max: parsePositiveIntegerEnv(
5489
+ memoryMaxEnvVar,
5490
+ DEFAULT_CACHE_MEMORY_MAX
5491
+ )
5492
+ }) : void 0;
5493
+ let diskCache;
5494
+ if (mode === "mixed" || mode === "disk") {
5495
+ if (canUseDiskCache()) {
5496
+ diskCache = new DiskCache({
5497
+ cacheDir: isomorph_default.getEnv(diskCacheDirEnvVar) ?? getDefaultDiskCacheDir(),
5498
+ max: parsePositiveIntegerEnv(diskMaxEnvVar, DEFAULT_CACHE_DISK_MAX)
5499
+ });
5500
+ } else if (mode === "disk") {
5501
+ warnUnavailableDiskCache();
5502
+ }
5503
+ }
5504
+ if (diskCache) {
5505
+ return { memoryCache, diskCache };
5506
+ }
5507
+ return { memoryCache };
5508
+ }
5509
+
5446
5510
  // src/prompt-cache/prompt-cache.ts
5447
5511
  function createCacheKey(key) {
5448
5512
  if (key.id) {
@@ -5470,16 +5534,18 @@ var PromptCache = class {
5470
5534
  */
5471
5535
  async get(key) {
5472
5536
  const cacheKey = createCacheKey(key);
5473
- const memoryPrompt = this.memoryCache.get(cacheKey);
5474
- if (memoryPrompt !== void 0) {
5475
- return memoryPrompt;
5537
+ if (this.memoryCache) {
5538
+ const memoryPrompt = this.memoryCache.get(cacheKey);
5539
+ if (memoryPrompt !== void 0) {
5540
+ return memoryPrompt;
5541
+ }
5476
5542
  }
5477
5543
  if (this.diskCache) {
5478
5544
  const diskPrompt = await this.diskCache.get(cacheKey);
5479
5545
  if (!diskPrompt) {
5480
5546
  return void 0;
5481
5547
  }
5482
- this.memoryCache.set(cacheKey, diskPrompt);
5548
+ this.memoryCache?.set(cacheKey, diskPrompt);
5483
5549
  return diskPrompt;
5484
5550
  }
5485
5551
  return void 0;
@@ -5494,7 +5560,7 @@ var PromptCache = class {
5494
5560
  */
5495
5561
  async set(key, value) {
5496
5562
  const cacheKey = createCacheKey(key);
5497
- this.memoryCache.set(cacheKey, value);
5563
+ this.memoryCache?.set(cacheKey, value);
5498
5564
  if (this.diskCache) {
5499
5565
  await this.diskCache.set(cacheKey, value);
5500
5566
  }
@@ -5524,23 +5590,25 @@ var ParametersCache = class {
5524
5590
  }
5525
5591
  async get(key) {
5526
5592
  const cacheKey = createCacheKey2(key);
5527
- const memoryParams = this.memoryCache.get(cacheKey);
5528
- if (memoryParams !== void 0) {
5529
- return memoryParams;
5593
+ if (this.memoryCache) {
5594
+ const memoryParams = this.memoryCache.get(cacheKey);
5595
+ if (memoryParams !== void 0) {
5596
+ return memoryParams;
5597
+ }
5530
5598
  }
5531
5599
  if (this.diskCache) {
5532
5600
  const diskParams = await this.diskCache.get(cacheKey);
5533
5601
  if (!diskParams) {
5534
5602
  return void 0;
5535
5603
  }
5536
- this.memoryCache.set(cacheKey, diskParams);
5604
+ this.memoryCache?.set(cacheKey, diskParams);
5537
5605
  return diskParams;
5538
5606
  }
5539
5607
  return void 0;
5540
5608
  }
5541
5609
  async set(key, value) {
5542
5610
  const cacheKey = createCacheKey2(key);
5543
- this.memoryCache.set(cacheKey, value);
5611
+ this.memoryCache?.set(cacheKey, value);
5544
5612
  if (this.diskCache) {
5545
5613
  await this.diskCache.set(cacheKey, value);
5546
5614
  }
@@ -6072,21 +6140,22 @@ var BraintrustState = class _BraintrustState {
6072
6140
  setGlobalDebugLogLevel(void 0);
6073
6141
  }
6074
6142
  this.resetLoginInfo();
6075
- const memoryCache = new LRUCache({
6076
- max: Number(isomorph_default.getEnv("BRAINTRUST_PROMPT_CACHE_MEMORY_MAX")) ?? 1 << 10
6143
+ const { memoryCache, diskCache } = createCacheLayers({
6144
+ memoryMaxEnvVar: "BRAINTRUST_PROMPT_CACHE_MEMORY_MAX",
6145
+ diskCacheDirEnvVar: "BRAINTRUST_PROMPT_CACHE_DIR",
6146
+ diskMaxEnvVar: "BRAINTRUST_PROMPT_CACHE_DISK_MAX",
6147
+ getDefaultDiskCacheDir: () => `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/prompt_cache`
6077
6148
  });
6078
- const diskCache = canUseDiskCache() ? new DiskCache({
6079
- cacheDir: isomorph_default.getEnv("BRAINTRUST_PROMPT_CACHE_DIR") ?? `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/prompt_cache`,
6080
- max: Number(isomorph_default.getEnv("BRAINTRUST_PROMPT_CACHE_DISK_MAX")) ?? 1 << 20
6081
- }) : void 0;
6082
6149
  this.promptCache = new PromptCache({ memoryCache, diskCache });
6083
- const parametersMemoryCache = new LRUCache({
6084
- max: Number(isomorph_default.getEnv("BRAINTRUST_PARAMETERS_CACHE_MEMORY_MAX")) ?? 1 << 10
6150
+ const {
6151
+ memoryCache: parametersMemoryCache,
6152
+ diskCache: parametersDiskCache
6153
+ } = createCacheLayers({
6154
+ memoryMaxEnvVar: "BRAINTRUST_PARAMETERS_CACHE_MEMORY_MAX",
6155
+ diskCacheDirEnvVar: "BRAINTRUST_PARAMETERS_CACHE_DIR",
6156
+ diskMaxEnvVar: "BRAINTRUST_PARAMETERS_CACHE_DISK_MAX",
6157
+ getDefaultDiskCacheDir: () => `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/parameters_cache`
6085
6158
  });
6086
- const parametersDiskCache = canUseDiskCache() ? new DiskCache({
6087
- cacheDir: isomorph_default.getEnv("BRAINTRUST_PARAMETERS_CACHE_DIR") ?? `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/parameters_cache`,
6088
- max: Number(isomorph_default.getEnv("BRAINTRUST_PARAMETERS_CACHE_DISK_MAX")) ?? 1 << 20
6089
- }) : void 0;
6090
6159
  this.parametersCache = new ParametersCache({
6091
6160
  memoryCache: parametersMemoryCache,
6092
6161
  diskCache: parametersDiskCache
@@ -27611,803 +27680,440 @@ var flueChannels = defineChannels("@flue/runtime", {
27611
27680
  createContext: channel({
27612
27681
  channelName: "createFlueContext",
27613
27682
  kind: "sync-stream"
27614
- }),
27615
- openSession: channel({
27616
- channelName: "Harness.openSession",
27617
- kind: "async"
27618
- }),
27619
- contextEvent: channel({
27620
- channelName: "context.event",
27621
- kind: "sync-stream"
27622
- }),
27623
- prompt: channel({
27624
- channelName: "session.prompt",
27625
- kind: "async"
27626
- }),
27627
- skill: channel({
27628
- channelName: "session.skill",
27629
- kind: "async"
27630
- }),
27631
- task: channel({
27632
- channelName: "session.task",
27633
- kind: "async"
27634
- }),
27635
- compact: channel({
27636
- channelName: "session.compact",
27637
- kind: "async"
27638
27683
  })
27639
27684
  });
27640
27685
 
27641
- // src/wrappers/flue.ts
27642
- var WRAPPED_FLUE_CONTEXT = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-context");
27643
- var WRAPPED_FLUE_HARNESS = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-harness");
27644
- var WRAPPED_FLUE_SESSION = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-session");
27645
- var SUBSCRIBED_FLUE_CONTEXT_EVENTS = /* @__PURE__ */ Symbol.for(
27646
- "braintrust.flue.subscribed-context-events"
27647
- );
27648
- function patchFlueContextInPlace(ctx) {
27649
- const context2 = ctx;
27650
- if (context2[WRAPPED_FLUE_CONTEXT]) {
27651
- return ctx;
27652
- }
27653
- const originalInit = context2.init.bind(context2);
27654
- try {
27655
- Object.defineProperty(context2, WRAPPED_FLUE_CONTEXT, {
27656
- configurable: false,
27657
- enumerable: false,
27658
- value: true
27659
- });
27660
- Object.defineProperty(context2, "init", {
27661
- configurable: true,
27662
- value: async function wrappedFlueInit(options) {
27663
- const harness = await originalInit(options);
27664
- return wrapFlueHarness(harness);
27665
- },
27666
- writable: true
27667
- });
27668
- } catch {
27669
- }
27670
- return ctx;
27671
- }
27672
- function subscribeFlueContextEvents(ctx, options = {}) {
27673
- if (!ctx || typeof ctx !== "object" || typeof ctx.subscribeEvent !== "function") {
27674
- return void 0;
27686
+ // src/instrumentation/plugins/flue-plugin.ts
27687
+ var FLUE_AUTO_STATE = /* @__PURE__ */ Symbol.for("braintrust.flue.auto-state");
27688
+ var FLUE_OBSERVE_BRIDGE = /* @__PURE__ */ Symbol.for("braintrust.flue.observe-bridge");
27689
+ var braintrustFlueObserver = (event, ctx) => {
27690
+ getObserveBridge().handle(event, ctx);
27691
+ };
27692
+ var FluePlugin = class extends BasePlugin {
27693
+ onEnable() {
27694
+ this.unsubscribers.push(enableFlueAutoInstrumentation());
27675
27695
  }
27676
- const context2 = ctx;
27677
- const captureTurnSpans = options.captureTurnSpans ?? true;
27678
- const existingSubscription = context2[SUBSCRIBED_FLUE_CONTEXT_EVENTS];
27679
- if (existingSubscription) {
27680
- if (existingSubscription.captureTurnSpans || !captureTurnSpans) {
27681
- return void 0;
27682
- }
27683
- try {
27684
- existingSubscription.unsubscribe();
27685
- } catch {
27696
+ onDisable() {
27697
+ for (const unsubscribe of this.unsubscribers) {
27698
+ unsubscribe();
27686
27699
  }
27700
+ this.unsubscribers = [];
27687
27701
  }
27688
- try {
27689
- const unsubscribe = ctx.subscribeEvent((event) => {
27690
- flueChannels.contextEvent.traceSync(() => void 0, {
27691
- arguments: [event],
27692
- captureTurnSpans,
27693
- context: ctx
27694
- });
27695
- });
27696
- if (existingSubscription) {
27697
- existingSubscription.captureTurnSpans = captureTurnSpans;
27698
- existingSubscription.unsubscribe = unsubscribe;
27699
- } else {
27700
- Object.defineProperty(context2, SUBSCRIBED_FLUE_CONTEXT_EVENTS, {
27701
- configurable: false,
27702
- enumerable: false,
27703
- value: {
27704
- captureTurnSpans,
27705
- unsubscribe
27706
- }
27707
- });
27702
+ };
27703
+ function enableFlueAutoInstrumentation() {
27704
+ const state = getAutoState();
27705
+ state.refCount += 1;
27706
+ if (!state.handlers) {
27707
+ const channel2 = flueChannels.createContext.tracingChannel();
27708
+ const handlers = {
27709
+ end: (event) => {
27710
+ subscribeToFlueContext(event.result, state);
27711
+ }
27712
+ };
27713
+ channel2.subscribe(handlers);
27714
+ state.channel = channel2;
27715
+ state.handlers = handlers;
27716
+ }
27717
+ let released = false;
27718
+ return () => {
27719
+ if (released) {
27720
+ return;
27708
27721
  }
27709
- return unsubscribe;
27710
- } catch {
27711
- return void 0;
27722
+ released = true;
27723
+ releaseAutoState(state);
27724
+ };
27725
+ }
27726
+ function getAutoState() {
27727
+ const existing = Reflect.get(globalThis, FLUE_AUTO_STATE);
27728
+ if (isAutoState(existing)) {
27729
+ return existing;
27712
27730
  }
27731
+ const state = {
27732
+ contexts: /* @__PURE__ */ new WeakSet(),
27733
+ refCount: 0
27734
+ };
27735
+ Reflect.set(globalThis, FLUE_AUTO_STATE, state);
27736
+ return state;
27713
27737
  }
27714
- function wrapFlueHarness(harness) {
27715
- if (!isPlausibleFlueHarness(harness)) {
27716
- return harness;
27738
+ function getObserveBridge() {
27739
+ const existing = Reflect.get(globalThis, FLUE_OBSERVE_BRIDGE);
27740
+ if (isFlueObserveBridge(existing)) {
27741
+ return existing;
27717
27742
  }
27718
- const target = harness;
27719
- if (target[WRAPPED_FLUE_HARNESS]) {
27720
- return harness;
27743
+ const bridge = new FlueObserveBridge();
27744
+ Reflect.set(globalThis, FLUE_OBSERVE_BRIDGE, bridge);
27745
+ return bridge;
27746
+ }
27747
+ function isFlueObserveBridge(value) {
27748
+ return isObjectLike(value) && typeof Reflect.get(value, "handle") === "function" && typeof Reflect.get(value, "reset") === "function";
27749
+ }
27750
+ function isAutoState(value) {
27751
+ return isObjectLike(value) && Reflect.get(value, "contexts") instanceof WeakSet && typeof Reflect.get(value, "refCount") === "number";
27752
+ }
27753
+ function releaseAutoState(state) {
27754
+ state.refCount -= 1;
27755
+ if (state.refCount > 0) {
27756
+ return;
27721
27757
  }
27722
- const originalSession = target.session.bind(target);
27723
27758
  try {
27724
- Object.defineProperty(target, WRAPPED_FLUE_HARNESS, {
27725
- configurable: false,
27726
- enumerable: false,
27727
- value: true
27728
- });
27729
- Object.defineProperty(target, "session", {
27730
- configurable: true,
27731
- value: async function wrappedFlueHarnessSession(name, options) {
27732
- const session = await originalSession(name, options);
27733
- return patchFlueSessionInPlace(session);
27734
- },
27735
- writable: true
27736
- });
27737
- const sessions = target.sessions;
27738
- if (sessions && typeof sessions === "object") {
27739
- patchFlueSessionFactory(sessions, "get");
27740
- patchFlueSessionFactory(sessions, "create");
27759
+ if (state.channel && state.handlers) {
27760
+ state.channel.unsubscribe(state.handlers);
27741
27761
  }
27742
- } catch {
27762
+ } finally {
27763
+ Reflect.deleteProperty(globalThis, FLUE_AUTO_STATE);
27743
27764
  }
27744
- return harness;
27745
27765
  }
27746
- function patchFlueSessionInPlace(session) {
27747
- if (session[WRAPPED_FLUE_SESSION]) {
27748
- return session;
27766
+ function subscribeToFlueContext(value, state) {
27767
+ if (!isObservableFlueContext(value) || state.contexts.has(value)) {
27768
+ return;
27749
27769
  }
27770
+ const ctx = flueContextFromUnknown(value);
27771
+ let released = false;
27772
+ let unsubscribe;
27773
+ const release = () => {
27774
+ if (released) {
27775
+ return;
27776
+ }
27777
+ released = true;
27778
+ try {
27779
+ unsubscribe?.();
27780
+ } catch (error2) {
27781
+ logInstrumentationError3("Flue context unsubscribe", error2);
27782
+ }
27783
+ };
27750
27784
  try {
27751
- Object.defineProperty(session, WRAPPED_FLUE_SESSION, {
27752
- configurable: false,
27753
- enumerable: false,
27754
- value: true
27785
+ unsubscribe = value.subscribeEvent((event) => {
27786
+ if (state.refCount <= 0) {
27787
+ release();
27788
+ return;
27789
+ }
27790
+ braintrustFlueObserver(event, ctx);
27791
+ if (isAutoContextTerminalEvent(event, ctx)) {
27792
+ release();
27793
+ }
27755
27794
  });
27756
- patchCallHandleMethod(session, "prompt", flueChannels.prompt);
27757
- patchCallHandleMethod(session, "skill", flueChannels.skill);
27758
- patchCallHandleMethod(session, "task", flueChannels.task);
27759
- patchCompact(session);
27760
- } catch {
27795
+ state.contexts.add(value);
27796
+ } catch (error2) {
27797
+ logInstrumentationError3("Flue context subscription", error2);
27761
27798
  }
27762
- return session;
27763
27799
  }
27764
- function patchFlueSessionFactory(sessions, method) {
27765
- const original = sessions[method];
27766
- if (typeof original !== "function") {
27767
- return;
27800
+ function isAutoContextTerminalEvent(event, ctx) {
27801
+ if (!isObjectLike(event)) {
27802
+ return false;
27768
27803
  }
27769
- const bound = original.bind(sessions);
27770
- Object.defineProperty(sessions, method, {
27771
- configurable: true,
27772
- value: async function wrappedFlueSessionFactory(name, options) {
27773
- const session = await bound(name, options);
27774
- return patchFlueSessionInPlace(session);
27775
- },
27776
- writable: true
27777
- });
27778
- }
27779
- function patchCallHandleMethod(session, method, channel2) {
27780
- const original = session[method];
27781
- if (typeof original !== "function") {
27782
- return;
27804
+ const type = Reflect.get(event, "type");
27805
+ if (type === "run_end") {
27806
+ return true;
27783
27807
  }
27784
- const bound = original.bind(session);
27785
- Object.defineProperty(session, method, {
27786
- configurable: true,
27787
- value(input, options) {
27788
- const args = [input, options];
27789
- const { originalResult, traced: traced2 } = traceFlueOperation(channel2, {
27790
- context: {
27791
- arguments: args,
27792
- operation: method,
27793
- session
27794
- },
27795
- run: () => bound(input, options)
27796
- });
27797
- return preserveCallHandle(originalResult, traced2);
27798
- },
27799
- writable: true
27800
- });
27801
- }
27802
- function patchCompact(session) {
27803
- const original = session.compact;
27804
- if (typeof original !== "function") {
27805
- return;
27808
+ if (type !== "operation") {
27809
+ return false;
27806
27810
  }
27807
- const bound = original.bind(session);
27808
- Object.defineProperty(session, "compact", {
27809
- configurable: true,
27810
- value() {
27811
- const context2 = {
27812
- arguments: [],
27813
- operation: "compact",
27814
- session
27815
- };
27816
- return flueChannels.compact.tracePromise(() => bound(), context2);
27817
- },
27818
- writable: true
27819
- });
27811
+ return !ctx?.runId && typeof Reflect.get(event, "runId") !== "string";
27820
27812
  }
27821
- function traceFlueOperation(channel2, args) {
27822
- const tracingChannel2 = channel2.tracingChannel();
27823
- const context2 = args.context;
27824
- let originalResult;
27825
- let traced2;
27826
- const run2 = () => {
27827
- try {
27828
- originalResult = args.run();
27829
- tracingChannel2.end?.publish(context2);
27830
- } catch (error2) {
27831
- context2.error = normalizeError3(error2);
27832
- tracingChannel2.error?.publish(context2);
27833
- tracingChannel2.end?.publish(context2);
27834
- throw error2;
27835
- }
27836
- traced2 = Promise.resolve(originalResult).then(
27837
- (result) => {
27838
- context2.result = result;
27839
- tracingChannel2.asyncStart?.publish(context2);
27840
- tracingChannel2.asyncEnd?.publish(context2);
27841
- return result;
27842
- },
27843
- (error2) => {
27844
- context2.error = normalizeError3(error2);
27845
- tracingChannel2.error?.publish(context2);
27846
- tracingChannel2.asyncStart?.publish(context2);
27847
- tracingChannel2.asyncEnd?.publish(context2);
27848
- throw error2;
27849
- }
27850
- );
27851
- };
27852
- if (tracingChannel2.start?.runStores) {
27853
- tracingChannel2.start.runStores(context2, run2);
27854
- } else {
27855
- tracingChannel2.start?.publish(context2);
27856
- run2();
27857
- }
27858
- return { originalResult, traced: traced2 };
27813
+ function isObservableFlueContext(value) {
27814
+ return isObjectLike(value) && typeof Reflect.get(value, "subscribeEvent") === "function";
27859
27815
  }
27860
- function normalizeError3(error2) {
27861
- return error2 instanceof Error ? error2 : new Error(String(error2));
27816
+ function isFlueEvent(event) {
27817
+ const type = Reflect.get(event, "type");
27818
+ return type === "run_start" || type === "run_end" || type === "operation_start" || type === "operation" || type === "turn_request" || type === "turn" || type === "tool_start" || type === "tool_call" || type === "task_start" || type === "task" || type === "compaction_start" || type === "compaction";
27862
27819
  }
27863
- function preserveCallHandle(originalHandle, traced2) {
27864
- if (!isFlueCallHandle(originalHandle)) {
27865
- return traced2;
27820
+ function flueContextFromUnknown(ctx) {
27821
+ if (!isObjectLike(ctx)) {
27822
+ return void 0;
27866
27823
  }
27867
- const handle = originalHandle;
27868
- const wrapped = {
27869
- get signal() {
27870
- return handle.signal;
27871
- },
27872
- abort(reason) {
27873
- return handle.abort(reason);
27874
- },
27875
- then(onfulfilled, onrejected) {
27876
- return traced2.then(onfulfilled, onrejected);
27877
- }
27824
+ const id = Reflect.get(ctx, "id");
27825
+ const runId = Reflect.get(ctx, "runId");
27826
+ return {
27827
+ ...typeof id === "string" ? { id } : {},
27828
+ ...typeof runId === "string" ? { runId } : {}
27878
27829
  };
27879
- return wrapped;
27880
- }
27881
- function isPlausibleFlueHarness(value) {
27882
- return !!value && typeof value === "object" && typeof value.session === "function";
27883
27830
  }
27884
- function isFlueCallHandle(value) {
27885
- return !!value && typeof value === "object" && typeof value.then === "function" && typeof value.abort === "function" && "signal" in value;
27831
+ function isObjectLike(value) {
27832
+ return typeof value === "object" && value !== null && !Array.isArray(value);
27886
27833
  }
27887
-
27888
- // src/instrumentation/plugins/flue-plugin.ts
27889
- var FluePlugin = class extends BasePlugin {
27890
- activeOperationsById = /* @__PURE__ */ new Map();
27891
- activeOperationsByScope = /* @__PURE__ */ new Map();
27892
- compactionsByScope = /* @__PURE__ */ new Map();
27893
- pendingOperationsByKey = /* @__PURE__ */ new Map();
27834
+ var FlueObserveBridge = class {
27835
+ compactionsByKey = /* @__PURE__ */ new Map();
27836
+ operationsById = /* @__PURE__ */ new Map();
27837
+ runsById = /* @__PURE__ */ new Map();
27838
+ seenEvents = /* @__PURE__ */ new WeakSet();
27894
27839
  tasksById = /* @__PURE__ */ new Map();
27895
- toolsById = /* @__PURE__ */ new Map();
27896
- turnsByScope = /* @__PURE__ */ new Map();
27897
- onEnable() {
27898
- this.subscribeToContextCreation();
27899
- this.subscribeToSessionCreation();
27900
- this.subscribeToContextEvents();
27901
- this.subscribeToSessionOperations();
27902
- }
27903
- onDisable() {
27904
- for (const unsubscribe of this.unsubscribers) {
27905
- unsubscribe();
27840
+ toolsByKey = /* @__PURE__ */ new Map();
27841
+ turnsByKey = /* @__PURE__ */ new Map();
27842
+ handle(event, ctx) {
27843
+ if (!isObjectLike(event) || !isFlueEvent(event)) {
27844
+ return;
27906
27845
  }
27907
- this.unsubscribers = [];
27908
- this.activeOperationsById.clear();
27909
- this.activeOperationsByScope.clear();
27910
- this.compactionsByScope.clear();
27911
- this.pendingOperationsByKey.clear();
27846
+ if (this.seenEvents.has(event)) {
27847
+ return;
27848
+ }
27849
+ this.seenEvents.add(event);
27850
+ try {
27851
+ this.handleEvent(event, flueContextFromUnknown(ctx));
27852
+ } catch (error2) {
27853
+ logInstrumentationError3("Flue observe", error2);
27854
+ }
27855
+ }
27856
+ reset() {
27857
+ this.compactionsByKey.clear();
27858
+ this.operationsById.clear();
27859
+ this.runsById.clear();
27860
+ this.seenEvents = /* @__PURE__ */ new WeakSet();
27912
27861
  this.tasksById.clear();
27913
- this.toolsById.clear();
27914
- this.turnsByScope.clear();
27862
+ this.toolsByKey.clear();
27863
+ this.turnsByKey.clear();
27915
27864
  }
27916
- subscribeToContextCreation() {
27917
- const channel2 = flueChannels.createContext.tracingChannel();
27918
- const handlers = {
27919
- end: (event) => {
27920
- const ctx = event.result;
27921
- if (!ctx) {
27922
- return;
27923
- }
27924
- subscribeFlueContextEvents(ctx, { captureTurnSpans: false });
27925
- patchFlueContextInPlace(ctx);
27926
- },
27927
- error: () => {
27928
- }
27929
- };
27930
- channel2.subscribe(handlers);
27931
- this.unsubscribers.push(() => {
27932
- channel2.unsubscribe(handlers);
27933
- });
27865
+ handleEvent(event, ctx) {
27866
+ switch (event.type) {
27867
+ case "run_start":
27868
+ this.handleRunStart(event, ctx);
27869
+ return;
27870
+ case "run_end":
27871
+ this.handleRunEnd(event);
27872
+ return;
27873
+ case "operation_start":
27874
+ this.handleOperationStart(event);
27875
+ return;
27876
+ case "operation":
27877
+ this.handleOperation(event);
27878
+ return;
27879
+ case "turn_request":
27880
+ this.handleTurnRequest(event);
27881
+ return;
27882
+ case "turn":
27883
+ this.handleTurn(event);
27884
+ return;
27885
+ case "tool_start":
27886
+ this.handleToolStart(event);
27887
+ return;
27888
+ case "tool_call":
27889
+ this.handleToolCall(event);
27890
+ return;
27891
+ case "task_start":
27892
+ this.handleTaskStart(event);
27893
+ return;
27894
+ case "task":
27895
+ this.handleTask(event);
27896
+ return;
27897
+ case "compaction_start":
27898
+ this.handleCompactionStart(event);
27899
+ return;
27900
+ case "compaction":
27901
+ this.handleCompaction(event);
27902
+ return;
27903
+ default:
27904
+ return;
27905
+ }
27934
27906
  }
27935
- subscribeToSessionCreation() {
27936
- const channel2 = flueChannels.openSession.tracingChannel();
27937
- const handlers = {
27938
- asyncEnd: (event) => {
27939
- if (event.result) {
27940
- patchFlueSessionInPlace(
27941
- event.result
27942
- );
27943
- }
27944
- if (event.harness) {
27945
- wrapFlueHarness(event.harness);
27946
- }
27947
- },
27948
- error: () => {
27949
- }
27907
+ handleRunStart(event, ctx) {
27908
+ if (!event.runId) {
27909
+ return;
27910
+ }
27911
+ const workflowName = event.workflowName ?? event.owner?.workflowName ?? (typeof ctx?.id === "string" ? ctx.id : "unknown");
27912
+ const metadata = {
27913
+ ...extractPayloadMetadata(event.payload),
27914
+ ...extractEventMetadata(event, ctx),
27915
+ ...workflowName ? { "flue.workflow_name": workflowName } : {},
27916
+ provider: "flue"
27950
27917
  };
27951
- channel2.subscribe(handlers);
27952
- this.unsubscribers.push(() => {
27953
- channel2.unsubscribe(handlers);
27918
+ const span = startSpan({
27919
+ name: `workflow:${workflowName}`,
27920
+ spanAttributes: { type: "task" /* TASK */ },
27921
+ startTime: eventTime(event.startedAt ?? event.timestamp),
27922
+ event: {
27923
+ input: event.payload,
27924
+ metadata
27925
+ }
27954
27926
  });
27927
+ this.runsById.set(event.runId, { metadata, span });
27955
27928
  }
27956
- subscribeToSessionOperations() {
27957
- this.subscribeToSessionOperation(flueChannels.prompt);
27958
- this.subscribeToSessionOperation(flueChannels.skill);
27959
- this.subscribeToSessionOperation(flueChannels.task);
27960
- this.subscribeToCompact();
27961
- }
27962
- subscribeToSessionOperation(channel2) {
27963
- const tracingChannel2 = channel2.tracingChannel();
27964
- const states = /* @__PURE__ */ new WeakMap();
27965
- const ensureState2 = (event) => {
27966
- const existing = states.get(event);
27967
- if (existing) {
27968
- return existing;
27969
- }
27970
- const state = this.startOperationState({
27971
- args: event.arguments,
27972
- moduleVersion: typeof event.moduleVersion === "string" ? event.moduleVersion : void 0,
27973
- operation: event.operation,
27974
- session: event.session
27975
- });
27976
- states.set(event, state);
27977
- return state;
27978
- };
27979
- const unbindCurrentSpanStore = this.bindCurrentSpanStoreToOperationStart(
27980
- tracingChannel2,
27981
- ensureState2
27982
- );
27983
- const handlers = {
27984
- start: (event) => {
27985
- ensureState2(event);
27986
- },
27987
- asyncEnd: (event) => {
27988
- this.endOperationState(states.get(event), event.result);
27989
- states.delete(event);
27990
- },
27991
- error: (event) => {
27992
- const state = states.get(event);
27993
- if (state && event.error) {
27994
- safeLog3(state.span, { error: errorToString(event.error) });
27995
- this.finishOperationState(state);
27996
- }
27997
- states.delete(event);
27998
- }
27999
- };
28000
- tracingChannel2.subscribe(handlers);
28001
- this.unsubscribers.push(() => {
28002
- unbindCurrentSpanStore?.();
28003
- tracingChannel2.unsubscribe(handlers);
28004
- });
28005
- }
28006
- subscribeToCompact() {
28007
- const tracingChannel2 = flueChannels.compact.tracingChannel();
28008
- const states = /* @__PURE__ */ new WeakMap();
28009
- const ensureState2 = (event) => {
28010
- const existing = states.get(event);
28011
- if (existing) {
28012
- return existing;
28013
- }
28014
- const state = this.startOperationState({
28015
- args: [],
28016
- moduleVersion: typeof event.moduleVersion === "string" ? event.moduleVersion : void 0,
28017
- operation: event.operation,
28018
- session: event.session
28019
- });
28020
- states.set(event, state);
28021
- return state;
28022
- };
28023
- const unbindCurrentSpanStore = this.bindCurrentSpanStoreToOperationStart(
28024
- tracingChannel2,
28025
- ensureState2
28026
- );
28027
- const handlers = {
28028
- start: (event) => {
28029
- ensureState2(event);
28030
- },
28031
- asyncEnd: (event) => {
28032
- this.endOperationState(states.get(event), void 0);
28033
- states.delete(event);
28034
- },
28035
- error: (event) => {
28036
- const state = states.get(event);
28037
- if (state && event.error) {
28038
- safeLog3(state.span, { error: errorToString(event.error) });
28039
- this.finishOperationState(state);
28040
- }
28041
- states.delete(event);
28042
- }
28043
- };
28044
- tracingChannel2.subscribe(handlers);
28045
- this.unsubscribers.push(() => {
28046
- unbindCurrentSpanStore?.();
28047
- tracingChannel2.unsubscribe(handlers);
28048
- });
28049
- }
28050
- subscribeToContextEvents() {
28051
- const channel2 = flueChannels.contextEvent.tracingChannel();
28052
- const handlers = {
28053
- start: (event) => {
28054
- const flueEvent = event.arguments[0];
28055
- if (!flueEvent) {
28056
- return;
28057
- }
28058
- try {
28059
- this.handleFlueEvent(flueEvent, {
28060
- captureTurnSpans: event.captureTurnSpans !== false
28061
- });
28062
- } catch (error2) {
28063
- logInstrumentationError3("Flue event", error2);
28064
- }
28065
- },
28066
- error: () => {
28067
- }
28068
- };
28069
- channel2.subscribe(handlers);
28070
- this.unsubscribers.push(() => {
28071
- channel2.unsubscribe(handlers);
28072
- });
28073
- }
28074
- bindCurrentSpanStoreToOperationStart(tracingChannel2, ensureState2) {
28075
- const state = _internalGetGlobalState();
28076
- const startChannel = tracingChannel2.start;
28077
- const contextManager = state?.contextManager;
28078
- const currentSpanStore = contextManager ? contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
28079
- if (!currentSpanStore || !startChannel) {
28080
- return void 0;
28081
- }
28082
- startChannel.bindStore(currentSpanStore, (event) => {
28083
- const operationState = ensureState2(event);
28084
- return contextManager.wrapSpanForStore(operationState.span);
28085
- });
28086
- return () => {
28087
- startChannel.unbindStore(currentSpanStore);
28088
- };
28089
- }
28090
- startOperationState(args) {
28091
- const sessionName = getSessionName(args.session);
28092
- const metadata = {
28093
- ...extractOperationInputMetadata(args.operation, args.args),
28094
- ...extractSessionMetadata(args.session),
28095
- "flue.operation": args.operation,
28096
- provider: "flue",
28097
- ...args.moduleVersion ? { "flue.version": args.moduleVersion } : {}
28098
- };
28099
- const span = startSpan({
28100
- name: `flue.session.${args.operation}`,
28101
- spanAttributes: { type: "task" /* TASK */ }
28102
- });
28103
- const state = {
28104
- metadata,
28105
- operation: args.operation,
28106
- sessionName,
28107
- span,
28108
- startTime: getCurrentUnixTimestamp()
28109
- };
28110
- safeLog3(span, {
28111
- input: extractOperationInput(args.operation, args.args),
28112
- metadata
28113
- });
28114
- this.pendingOperationQueue(operationKey(sessionName, args.operation)).push(
28115
- state
28116
- );
28117
- addOperationToScope(
28118
- this.activeOperationsByScope,
28119
- sessionName ?? "unknown",
28120
- state
28121
- );
28122
- return state;
28123
- }
28124
- endOperationState(state, result) {
28125
- if (!state) {
28126
- return;
28127
- }
28128
- const metadata = {
28129
- ...state.metadata,
28130
- ...extractPromptResponseMetadata(result)
28131
- };
28132
- const metrics = {
28133
- ...buildDurationMetrics3(state.startTime),
28134
- ...metricsFromUsage(result?.usage)
28135
- };
28136
- safeLog3(state.span, {
28137
- metadata,
28138
- metrics,
28139
- output: extractOperationOutput(result)
28140
- });
28141
- this.finishCompactionsForOperation(state);
28142
- this.finishOperationState(state);
28143
- }
28144
- finishOperationState(state) {
28145
- removePendingOperation(this.pendingOperationsByKey, state);
28146
- if (state.operationId) {
28147
- this.activeOperationsById.delete(state.operationId);
28148
- }
28149
- removeScopedOperation(this.activeOperationsByScope, state);
28150
- state.span.end();
28151
- }
28152
- handleFlueEvent(event, options) {
28153
- switch (event.type) {
28154
- case "operation_start":
28155
- this.handleOperationStart(event);
28156
- return;
28157
- case "operation":
28158
- this.handleOperation(event);
28159
- return;
28160
- case "text_delta":
28161
- if (!options.captureTurnSpans) {
28162
- return;
28163
- }
28164
- this.ensureTurnState(event).text.push(
28165
- typeof event.text === "string" ? event.text : ""
28166
- );
28167
- return;
28168
- case "thinking_start":
28169
- if (!options.captureTurnSpans) {
28170
- return;
28171
- }
28172
- this.handleThinkingStart(event);
28173
- return;
28174
- case "thinking_delta":
28175
- if (!options.captureTurnSpans) {
28176
- return;
28177
- }
28178
- this.handleThinkingDelta(event);
28179
- return;
28180
- case "thinking_end":
28181
- if (!options.captureTurnSpans) {
28182
- return;
28183
- }
28184
- this.handleThinkingEnd(event);
28185
- return;
28186
- case "turn":
28187
- if (!options.captureTurnSpans) {
28188
- return;
28189
- }
28190
- this.handleTurn(event);
28191
- return;
28192
- case "tool_start":
28193
- this.handleToolStart(event, options);
28194
- return;
28195
- case "tool_call":
28196
- this.handleToolCall(event);
28197
- return;
28198
- case "task_start":
28199
- this.handleTaskStart(event);
28200
- return;
28201
- case "task":
28202
- this.handleTask(event);
28203
- return;
28204
- case "compaction_start":
28205
- this.handleCompactionStart(event);
28206
- return;
28207
- case "compaction":
28208
- this.handleCompaction(event);
28209
- return;
28210
- default:
28211
- return;
28212
- }
27929
+ handleRunEnd(event) {
27930
+ const state = this.runsById.get(event.runId);
27931
+ this.finishPendingSpansForRun(event);
27932
+ if (state) {
27933
+ safeLog3(state.span, {
27934
+ ...event.isError ? { error: errorToString(event.error) } : {},
27935
+ metadata: {
27936
+ ...state.metadata,
27937
+ ...extractEventMetadata(event),
27938
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
27939
+ },
27940
+ metrics: durationMetrics2(event.durationMs),
27941
+ output: event.result
27942
+ });
27943
+ safeEnd(state.span, eventTime(event.timestamp));
27944
+ this.runsById.delete(event.runId);
27945
+ }
27946
+ void flush().catch((error2) => {
27947
+ logInstrumentationError3("Flue flush", error2);
27948
+ });
28213
27949
  }
28214
27950
  handleOperationStart(event) {
28215
- if (!isInstrumentedOperation(event.operationKind)) {
27951
+ if (!event.operationId || !isInstrumentedOperation(event.operationKind)) {
28216
27952
  return;
28217
27953
  }
28218
- const state = this.takePendingOperationForEvent(event);
28219
- if (!state) {
28220
- return;
28221
- }
28222
- state.operationId = event.operationId;
28223
- this.activeOperationsById.set(event.operationId, state);
28224
- addScopedOperation(this.activeOperationsByScope, event, state);
28225
- state.metadata = {
28226
- ...state.metadata,
27954
+ const metadata = {
28227
27955
  ...extractEventMetadata(event),
28228
- "flue.operation_id": event.operationId
27956
+ "flue.operation": event.operationKind,
27957
+ provider: "flue"
28229
27958
  };
28230
- safeLog3(state.span, { metadata: state.metadata });
27959
+ const parent = this.parentSpanForEvent(event);
27960
+ const span = startFlueSpan(parent, {
27961
+ name: `flue.${event.operationKind}`,
27962
+ spanAttributes: { type: "task" /* TASK */ },
27963
+ startTime: eventTime(event.timestamp),
27964
+ event: { metadata }
27965
+ });
27966
+ this.operationsById.set(event.operationId, { metadata, span });
28231
27967
  }
28232
27968
  handleOperation(event) {
28233
- const state = event.operationId ? this.activeOperationsById.get(event.operationId) : void 0;
28234
- if (!state) {
27969
+ if (!isInstrumentedOperation(event.operationKind)) {
28235
27970
  return;
28236
27971
  }
27972
+ const state = this.operationsById.get(event.operationId) ?? this.startSyntheticOperation(event);
27973
+ const output = operationOutput(event);
28237
27974
  const metadata = {
28238
27975
  ...state.metadata,
28239
27976
  ...extractEventMetadata(event),
28240
- ...typeof event.durationMs === "number" ? { "flue.duration_ms": event.durationMs } : {},
28241
- ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
27977
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {},
27978
+ ...event.usage ? { "flue.usage": event.usage } : {}
28242
27979
  };
28243
- const metrics = metricsFromUsage(event.usage);
27980
+ this.finishPendingChildrenForOperation(event, output);
28244
27981
  safeLog3(state.span, {
28245
- ...event.error ? { error: errorToString(event.error) } : {},
27982
+ ...event.isError ? { error: errorToString(event.error) } : {},
28246
27983
  metadata,
28247
- ...Object.keys(metrics).length ? { metrics } : {}
27984
+ metrics: durationMetrics2(event.durationMs),
27985
+ output
28248
27986
  });
27987
+ safeEnd(state.span, eventTime(event.timestamp));
27988
+ this.operationsById.delete(event.operationId);
28249
27989
  }
28250
- ensureTurnState(event) {
28251
- const scope = scopeKey(event);
28252
- const existing = this.turnsByScope.get(scope);
28253
- if (existing) {
28254
- return existing;
27990
+ handleTurnRequest(event) {
27991
+ const key = turnKey(event);
27992
+ if (!key) {
27993
+ return;
28255
27994
  }
28256
- const parent = this.parentSpanForEvent(event);
28257
27995
  const metadata = {
28258
27996
  ...extractEventMetadata(event),
28259
- provider: "flue"
27997
+ ...event.api ? { "flue.api": event.api } : {},
27998
+ ...event.model ? { model: event.model, "flue.model": event.model } : {},
27999
+ ...event.provider ? { provider: event.provider } : { provider: "flue" },
28000
+ ...event.provider ? { "flue.provider": event.provider } : {},
28001
+ ...event.purpose ? { "flue.turn_purpose": event.purpose } : {},
28002
+ ...event.reasoning ? { reasoning: event.reasoning } : {},
28003
+ ...event.input?.systemPrompt ? { "flue.system_prompt": event.input.systemPrompt } : {},
28004
+ ...event.input?.tools ? { tools: event.input.tools } : {}
28260
28005
  };
28006
+ const parent = this.parentSpanForTurn(event);
28261
28007
  const span = startFlueSpan(parent, {
28262
- name: "flue.turn",
28263
- spanAttributes: { type: "llm" /* LLM */ }
28008
+ name: `llm:${event.model ?? event.purpose ?? "unknown"}`,
28009
+ spanAttributes: { type: "llm" /* LLM */ },
28010
+ startTime: eventTime(event.timestamp),
28011
+ event: {
28012
+ input: event.input?.messages,
28013
+ metadata
28014
+ }
28264
28015
  });
28265
- const state = {
28266
- metadata,
28267
- span,
28268
- hasThinking: false,
28269
- startTime: getCurrentUnixTimestamp(),
28270
- text: [],
28271
- thinking: [],
28272
- toolCalls: []
28273
- };
28274
- safeLog3(span, { metadata });
28275
- this.turnsByScope.set(scope, state);
28276
- return state;
28016
+ this.logOperationInput(
28017
+ event.operationId,
28018
+ event.input?.messages ?? event.input
28019
+ );
28020
+ this.turnsByKey.set(key, { metadata, span });
28277
28021
  }
28278
28022
  handleTurn(event) {
28279
- const scope = scopeKey(event);
28280
- const state = this.ensureTurnState(event);
28281
- const text = state.text.join("");
28282
- const reasoning = state.finalThinking ?? state.thinking.join("");
28283
- const outputReasoning = reasoning || (state.hasThinking ? "[reasoning stream present; content unavailable]" : void 0);
28023
+ const key = turnKey(event);
28024
+ if (!key) {
28025
+ return;
28026
+ }
28027
+ const state = this.turnsByKey.get(key) ?? this.startSyntheticTurn(event);
28284
28028
  const metadata = {
28285
28029
  ...state.metadata,
28286
28030
  ...extractEventMetadata(event),
28031
+ ...event.api ? { "flue.api": event.api } : {},
28287
28032
  ...event.model ? { model: event.model, "flue.model": event.model } : {},
28033
+ ...event.provider ? { provider: event.provider } : {},
28034
+ ...event.provider ? { "flue.provider": event.provider } : {},
28035
+ ...event.purpose ? { "flue.turn_purpose": event.purpose } : {},
28288
28036
  ...event.stopReason ? { "flue.stop_reason": event.stopReason } : {},
28289
- ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {},
28290
- provider: "flue"
28037
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
28291
28038
  };
28292
28039
  safeLog3(state.span, {
28293
- ...event.error ? { error: errorToString(event.error) } : {},
28040
+ ...event.isError ? { error: errorToString(event.error) } : {},
28294
28041
  metadata,
28295
28042
  metrics: {
28296
- ...durationMsMetrics(event.durationMs),
28043
+ ...durationMetrics2(event.durationMs),
28297
28044
  ...metricsFromUsage(event.usage)
28298
28045
  },
28299
- output: toAssistantOutput(
28300
- text,
28301
- event.stopReason,
28302
- outputReasoning,
28303
- state.toolCalls
28304
- )
28046
+ output: event.output
28305
28047
  });
28306
- state.span.end();
28307
- this.turnsByScope.delete(scope);
28308
- }
28309
- handleThinkingDelta(event) {
28310
- const delta = event.delta;
28311
- if (typeof delta !== "string" || !delta) {
28312
- return;
28313
- }
28314
- const state = this.ensureTurnState(event);
28315
- state.hasThinking = true;
28316
- state.metadata["flue.thinking"] = true;
28317
- state.thinking.push(delta);
28318
- }
28319
- handleThinkingStart(event) {
28320
- const state = this.ensureTurnState(event);
28321
- state.hasThinking = true;
28322
- state.metadata["flue.thinking"] = true;
28323
- }
28324
- handleThinkingEnd(event) {
28325
- const state = this.ensureTurnState(event);
28326
- state.hasThinking = true;
28327
- state.metadata["flue.thinking"] = true;
28328
- if (typeof event.content === "string" && event.content) {
28329
- state.finalThinking = event.content;
28330
- }
28048
+ safeEnd(state.span, eventTime(event.timestamp));
28049
+ this.turnsByKey.delete(key);
28331
28050
  }
28332
- handleToolStart(event, options) {
28333
- const toolCallId = event.toolCallId;
28334
- if (!toolCallId) {
28051
+ handleToolStart(event) {
28052
+ if (!event.toolCallId) {
28335
28053
  return;
28336
28054
  }
28337
- const parent = this.parentSpanForEvent(event);
28338
- const scope = scopeKey(event);
28339
- let turnState = this.turnsByScope.get(scope);
28340
- if (!turnState && parent && options.captureTurnSpans) {
28341
- turnState = this.ensureTurnState(event);
28342
- }
28343
28055
  const metadata = {
28344
28056
  ...extractEventMetadata(event),
28345
28057
  ...event.toolName ? { "flue.tool_name": event.toolName } : {},
28346
- "flue.tool_call_id": toolCallId,
28058
+ "flue.tool_call_id": event.toolCallId,
28347
28059
  provider: "flue"
28348
28060
  };
28061
+ const parent = this.parentSpanForTool(event);
28349
28062
  const span = startFlueSpan(parent, {
28350
- name: `tool: ${event.toolName ?? "unknown"}`,
28351
- spanAttributes: { type: "tool" /* TOOL */ }
28352
- });
28353
- if (turnState) {
28354
- turnState.toolCalls.push({
28355
- args: event.args,
28356
- toolCallId,
28357
- toolName: event.toolName
28358
- });
28359
- }
28360
- safeLog3(span, {
28361
- input: event.args,
28362
- metadata
28363
- });
28364
- this.toolsById.set(toolKey(event), {
28365
- metadata,
28366
- span,
28367
- startTime: getCurrentUnixTimestamp()
28063
+ name: `tool:${event.toolName ?? "unknown"}`,
28064
+ spanAttributes: { type: "tool" /* TOOL */ },
28065
+ startTime: eventTime(event.timestamp),
28066
+ event: {
28067
+ input: event.args,
28068
+ metadata
28069
+ }
28368
28070
  });
28071
+ this.toolsByKey.set(toolKey(event), { metadata, span });
28369
28072
  }
28370
28073
  handleToolCall(event) {
28074
+ if (!event.toolCallId) {
28075
+ return;
28076
+ }
28371
28077
  const key = toolKey(event);
28372
- const state = this.toolsById.get(key) ?? this.startSyntheticToolState(event, event.toolName ?? "unknown");
28078
+ const state = this.toolsByKey.get(key) ?? this.startSyntheticTool(event);
28373
28079
  const metadata = {
28374
28080
  ...state.metadata,
28375
28081
  ...extractEventMetadata(event),
28376
28082
  ...event.toolName ? { "flue.tool_name": event.toolName } : {},
28377
- ...event.toolCallId ? { "flue.tool_call_id": event.toolCallId } : {},
28083
+ "flue.tool_call_id": event.toolCallId,
28378
28084
  ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
28379
28085
  };
28380
28086
  safeLog3(state.span, {
28381
28087
  ...event.isError ? { error: errorToString(event.result) } : {},
28382
28088
  metadata,
28383
- metrics: durationMsMetrics(event.durationMs),
28089
+ metrics: durationMetrics2(event.durationMs),
28384
28090
  output: event.result
28385
28091
  });
28386
- state.span.end();
28387
- this.toolsById.delete(key);
28092
+ safeEnd(state.span, eventTime(event.timestamp));
28093
+ this.toolsByKey.delete(key);
28388
28094
  }
28389
28095
  handleTaskStart(event) {
28390
- const parent = this.parentSpanForEvent(event);
28096
+ if (!event.taskId) {
28097
+ return;
28098
+ }
28391
28099
  const metadata = {
28392
28100
  ...extractEventMetadata(event),
28393
- ...event.role ? { "flue.role": event.role } : {},
28101
+ ...event.agent ? { "flue.agent": event.agent } : {},
28394
28102
  ...event.cwd ? { "flue.cwd": event.cwd } : {},
28395
28103
  "flue.task_id": event.taskId,
28396
28104
  provider: "flue"
28397
28105
  };
28106
+ const parent = this.parentSpanForEvent(event);
28398
28107
  const span = startFlueSpan(parent, {
28399
- name: "flue.task",
28400
- spanAttributes: { type: "task" /* TASK */ }
28401
- });
28402
- safeLog3(span, {
28403
- input: event.prompt,
28404
- metadata
28405
- });
28406
- this.tasksById.set(event.taskId, {
28407
- metadata,
28408
- span,
28409
- startTime: getCurrentUnixTimestamp()
28108
+ name: event.agent ? `task:${event.agent}` : "flue.task",
28109
+ spanAttributes: { type: "task" /* TASK */ },
28110
+ startTime: eventTime(event.timestamp),
28111
+ event: {
28112
+ input: event.prompt,
28113
+ metadata
28114
+ }
28410
28115
  });
28116
+ this.tasksById.set(event.taskId, { metadata, span });
28411
28117
  }
28412
28118
  handleTask(event) {
28413
28119
  const state = this.tasksById.get(event.taskId);
@@ -28419,426 +28125,372 @@ var FluePlugin = class extends BasePlugin {
28419
28125
  metadata: {
28420
28126
  ...state.metadata,
28421
28127
  ...extractEventMetadata(event),
28128
+ ...event.agent ? { "flue.agent": event.agent } : {},
28422
28129
  ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
28423
28130
  },
28424
- metrics: durationMsMetrics(event.durationMs),
28131
+ metrics: durationMetrics2(event.durationMs),
28425
28132
  output: event.result
28426
28133
  });
28427
- state.span.end();
28134
+ safeEnd(state.span, eventTime(event.timestamp));
28428
28135
  this.tasksById.delete(event.taskId);
28429
28136
  }
28430
28137
  handleCompactionStart(event) {
28431
- const operationState = this.operationStateForEvent(event);
28432
- const parent = operationState?.span ?? this.parentSpanForEvent(event);
28138
+ const key = compactionKey(event);
28139
+ const input = {
28140
+ ...event.estimatedTokens !== void 0 ? { estimatedTokens: event.estimatedTokens } : {},
28141
+ ...event.reason ? { reason: event.reason } : {}
28142
+ };
28433
28143
  const metadata = {
28434
28144
  ...extractEventMetadata(event),
28435
28145
  ...event.reason ? { "flue.compaction_reason": event.reason } : {},
28436
28146
  provider: "flue"
28437
28147
  };
28438
- const input = {
28439
- ...typeof event.estimatedTokens === "number" ? { estimatedTokens: event.estimatedTokens } : {},
28440
- ...event.reason ? { reason: event.reason } : {}
28441
- };
28148
+ const parent = this.parentSpanForEvent(event);
28442
28149
  const span = startFlueSpan(parent, {
28443
- name: "flue.compaction",
28444
- spanAttributes: { type: "task" /* TASK */ }
28445
- });
28446
- safeLog3(span, {
28447
- input,
28448
- metadata
28449
- });
28450
- this.compactionsByScope.set(scopeKey(event), {
28451
- input,
28452
- metadata,
28453
- operationState,
28454
- span,
28455
- startTime: getCurrentUnixTimestamp()
28456
- });
28457
- }
28458
- handleCompaction(event) {
28459
- const key = scopeKey(event);
28460
- const state = this.compactionsByScope.get(key) ?? this.findCompactionState(event);
28461
- if (!state) {
28462
- return;
28463
- }
28464
- safeLog3(state.span, {
28465
- metadata: {
28466
- ...state.metadata,
28467
- ...extractEventMetadata(event),
28468
- ...typeof event.messagesBefore === "number" ? { "flue.messages_before": event.messagesBefore } : {},
28469
- ...typeof event.messagesAfter === "number" ? { "flue.messages_after": event.messagesAfter } : {}
28470
- },
28471
- metrics: {
28472
- ...durationMsMetrics(event.durationMs),
28473
- ...metricsFromUsage(event.usage)
28474
- },
28475
- output: {
28476
- messagesAfter: event.messagesAfter,
28477
- messagesBefore: event.messagesBefore
28478
- }
28479
- });
28480
- state.span.end();
28481
- this.deleteCompactionState(state);
28482
- }
28483
- findCompactionState(event) {
28484
- const operationState = this.operationStateForEvent(event);
28485
- for (const state of this.compactionsByScope.values()) {
28486
- if (operationState && state.operationState === operationState) {
28487
- return state;
28488
- }
28489
- }
28490
- return void 0;
28491
- }
28492
- finishCompactionsForOperation(operationState) {
28493
- for (const state of [...this.compactionsByScope.values()]) {
28494
- if (state.operationState !== operationState) {
28495
- continue;
28496
- }
28497
- safeLog3(state.span, {
28498
- input: state.input,
28499
- metadata: state.metadata,
28500
- metrics: {
28501
- ...buildDurationMetrics3(state.startTime)
28502
- },
28503
- output: { completed: true }
28504
- });
28505
- state.span.end();
28506
- this.deleteCompactionState(state);
28507
- }
28508
- }
28509
- deleteCompactionState(state) {
28510
- for (const [key, candidate] of this.compactionsByScope) {
28511
- if (candidate !== state) {
28512
- continue;
28150
+ name: `compaction:${event.reason ?? "unknown"}`,
28151
+ spanAttributes: { type: "task" /* TASK */ },
28152
+ startTime: eventTime(event.timestamp),
28153
+ event: {
28154
+ input,
28155
+ metadata
28513
28156
  }
28514
- this.compactionsByScope.delete(key);
28515
- return;
28516
- }
28157
+ });
28158
+ this.logOperationInput(event.operationId, input);
28159
+ this.compactionsByKey.set(key, { metadata, span });
28517
28160
  }
28518
- startSyntheticToolState(event, toolName) {
28519
- const parent = this.parentSpanForEvent(event);
28161
+ handleCompaction(event) {
28162
+ const key = compactionKey(event);
28163
+ const state = this.compactionsByKey.get(key) ?? this.startSyntheticCompaction(event);
28520
28164
  const metadata = {
28165
+ ...state.metadata,
28521
28166
  ...extractEventMetadata(event),
28522
- ...event.toolCallId ? { "flue.tool_call_id": event.toolCallId } : {},
28523
- "flue.tool_name": toolName,
28524
- provider: "flue"
28167
+ ...event.usage ? { "flue.usage": event.usage } : {}
28525
28168
  };
28526
- const span = startFlueSpan(parent, {
28527
- name: `tool: ${toolName}`,
28528
- spanAttributes: { type: "tool" /* TOOL */ }
28169
+ safeLog3(state.span, {
28170
+ metadata,
28171
+ metrics: {
28172
+ ...durationMetrics2(event.durationMs),
28173
+ ...typeof event.messagesBefore === "number" ? { messages_before: event.messagesBefore } : {},
28174
+ ...typeof event.messagesAfter === "number" ? { messages_after: event.messagesAfter } : {}
28175
+ },
28176
+ output: {
28177
+ messagesAfter: event.messagesAfter,
28178
+ messagesBefore: event.messagesBefore
28179
+ }
28529
28180
  });
28530
- safeLog3(span, { metadata });
28531
- return { metadata, span, startTime: getCurrentUnixTimestamp() };
28181
+ safeEnd(state.span, eventTime(event.timestamp));
28182
+ this.compactionsByKey.delete(key);
28532
28183
  }
28533
- operationStateForEvent(event) {
28534
- if (event.operationId) {
28535
- const operation = this.activeOperationsById.get(event.operationId) ?? this.promotePendingOperationForEvent(event);
28536
- if (operation) {
28537
- return operation;
28184
+ parentSpanForTurn(event) {
28185
+ if (event.purpose === "compaction" || event.purpose === "compaction_prefix") {
28186
+ const compaction = this.compactionsByKey.get(compactionKey(event));
28187
+ if (compaction) {
28188
+ return compaction.span;
28538
28189
  }
28539
28190
  }
28540
- return this.activeOperationForEventScope(event) ?? this.pendingOperationForEventScope(event);
28191
+ return this.parentSpanForEvent(event);
28541
28192
  }
28542
28193
  parentSpanForEvent(event) {
28543
- if (event.operationId) {
28544
- const operation = this.operationStateForEvent(event);
28545
- if (operation) {
28546
- return operation.span;
28194
+ const turn = turnKey(event);
28195
+ if (turn) {
28196
+ const turnState = this.turnsByKey.get(turn);
28197
+ if (turnState) {
28198
+ return turnState.span;
28547
28199
  }
28548
28200
  }
28549
28201
  if (event.taskId) {
28550
- return this.tasksById.get(event.taskId)?.span;
28551
- }
28552
- return this.operationStateForEvent(event)?.span;
28553
- }
28554
- promotePendingOperationForEvent(event) {
28555
- if (!event.operationId) {
28556
- return void 0;
28557
- }
28558
- const scopePrefixes = operationScopePrefixes(event);
28559
- for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
28560
- if (!candidateQueue.length || !operationKeyMatchesScopes(candidateKey, scopePrefixes)) {
28561
- continue;
28562
- }
28563
- const state = candidateQueue.shift();
28564
- if (!state) {
28565
- return void 0;
28202
+ const task = this.tasksById.get(event.taskId);
28203
+ if (task) {
28204
+ return task.span;
28566
28205
  }
28567
- state.operationId = event.operationId;
28568
- this.activeOperationsById.set(event.operationId, state);
28569
- addScopedOperation(this.activeOperationsByScope, event, state);
28570
- state.metadata = {
28571
- ...state.metadata,
28572
- ...extractEventMetadata(event),
28573
- "flue.operation_id": event.operationId
28574
- };
28575
- safeLog3(state.span, { metadata: state.metadata });
28576
- return state;
28577
28206
  }
28578
- return void 0;
28579
- }
28580
- activeOperationForEventScope(event) {
28581
- for (const scope of operationScopeNames(event)) {
28582
- const operations = this.activeOperationsByScope.get(scope);
28583
- if (operations?.length) {
28584
- return operations[operations.length - 1];
28207
+ if (event.operationId) {
28208
+ const operation = this.operationsById.get(event.operationId);
28209
+ if (operation) {
28210
+ return operation.span;
28585
28211
  }
28586
28212
  }
28587
- return void 0;
28588
- }
28589
- pendingOperationForEventScope(event) {
28590
- const scopePrefixes = operationScopePrefixes(event);
28591
- for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
28592
- if (!candidateQueue.length || !operationKeyMatchesScopes(candidateKey, scopePrefixes)) {
28593
- continue;
28594
- }
28595
- return candidateQueue[0];
28213
+ if (event.runId) {
28214
+ return this.runsById.get(event.runId)?.span;
28596
28215
  }
28597
28216
  return void 0;
28598
28217
  }
28599
- takePendingOperationForEvent(event) {
28600
- const key = operationKey(event.session, event.operationKind);
28601
- const queue2 = this.pendingOperationsByKey.get(key);
28602
- if (queue2?.length) {
28603
- return queue2.shift();
28218
+ parentSpanForTool(event) {
28219
+ if (event.taskId) {
28220
+ const task = this.tasksById.get(event.taskId);
28221
+ if (task) {
28222
+ return task.span;
28223
+ }
28604
28224
  }
28605
- for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
28606
- if (candidateKey.endsWith(`::${event.operationKind}`) && candidateQueue.length) {
28607
- return candidateQueue.shift();
28225
+ if (event.operationId) {
28226
+ const operation = this.operationsById.get(event.operationId);
28227
+ if (operation) {
28228
+ return operation.span;
28608
28229
  }
28609
28230
  }
28231
+ if (event.runId) {
28232
+ return this.runsById.get(event.runId)?.span;
28233
+ }
28610
28234
  return void 0;
28611
28235
  }
28612
- pendingOperationQueue(key) {
28613
- const existing = this.pendingOperationsByKey.get(key);
28614
- if (existing) {
28615
- return existing;
28236
+ logOperationInput(operationId, input) {
28237
+ if (!operationId || input === void 0) {
28238
+ return;
28616
28239
  }
28617
- const queue2 = [];
28618
- this.pendingOperationsByKey.set(key, queue2);
28619
- return queue2;
28620
- }
28621
- };
28622
- function isInstrumentedOperation(operation) {
28623
- return operation === "prompt" || operation === "skill" || operation === "task" || operation === "compact";
28624
- }
28625
- function getSessionName(session) {
28626
- return typeof session?.name === "string" ? session.name : void 0;
28627
- }
28628
- function operationKey(sessionName, operation) {
28629
- return `${sessionName ?? "unknown"}::${operation}`;
28630
- }
28631
- function operationScopePrefixes(event) {
28632
- const scopes = /* @__PURE__ */ new Set();
28633
- for (const scope of operationScopeNames(event)) {
28634
- scopes.add(`${scope}::`);
28635
- }
28636
- return scopes;
28637
- }
28638
- function operationKeyMatchesScopes(key, scopes) {
28639
- for (const scope of scopes) {
28640
- if (key.startsWith(scope)) {
28641
- return true;
28240
+ const operation = this.operationsById.get(operationId);
28241
+ if (!operation || operation.loggedInput) {
28242
+ return;
28642
28243
  }
28244
+ safeLog3(operation.span, { input });
28245
+ operation.loggedInput = true;
28643
28246
  }
28644
- return false;
28645
- }
28646
- function operationScopeNames(event) {
28647
- const scopes = /* @__PURE__ */ new Set();
28648
- if (event.session) {
28649
- scopes.add(event.session);
28247
+ startSyntheticOperation(event) {
28248
+ const metadata = {
28249
+ ...extractEventMetadata(event),
28250
+ "flue.operation": event.operationKind,
28251
+ provider: "flue"
28252
+ };
28253
+ const span = startFlueSpan(this.parentSpanForEvent(event), {
28254
+ name: `flue.${event.operationKind}`,
28255
+ spanAttributes: { type: "task" /* TASK */ },
28256
+ startTime: eventTime(event.timestamp),
28257
+ event: { metadata }
28258
+ });
28259
+ return { metadata, span };
28650
28260
  }
28651
- if (event.parentSession) {
28652
- scopes.add(event.parentSession);
28261
+ startSyntheticTurn(event) {
28262
+ const metadata = {
28263
+ ...extractEventMetadata(event),
28264
+ ...event.api ? { "flue.api": event.api } : {},
28265
+ ...event.model ? { model: event.model, "flue.model": event.model } : {},
28266
+ ...event.provider ? { provider: event.provider } : { provider: "flue" },
28267
+ ...event.provider ? { "flue.provider": event.provider } : {},
28268
+ ...event.purpose ? { "flue.turn_purpose": event.purpose } : {}
28269
+ };
28270
+ const span = startFlueSpan(this.parentSpanForEvent(event), {
28271
+ name: `llm:${event.model ?? event.purpose ?? "unknown"}`,
28272
+ spanAttributes: { type: "llm" /* LLM */ },
28273
+ startTime: eventTime(event.timestamp),
28274
+ event: { metadata }
28275
+ });
28276
+ return { metadata, span };
28653
28277
  }
28654
- if (!scopes.size) {
28655
- scopes.add("unknown");
28278
+ startSyntheticTool(event) {
28279
+ const metadata = {
28280
+ ...extractEventMetadata(event),
28281
+ ...event.toolName ? { "flue.tool_name": event.toolName } : {},
28282
+ "flue.tool_call_id": event.toolCallId,
28283
+ provider: "flue"
28284
+ };
28285
+ const span = startFlueSpan(this.parentSpanForTool(event), {
28286
+ name: `tool:${event.toolName ?? "unknown"}`,
28287
+ spanAttributes: { type: "tool" /* TOOL */ },
28288
+ startTime: eventTime(event.timestamp),
28289
+ event: { metadata }
28290
+ });
28291
+ return { metadata, span };
28656
28292
  }
28657
- return scopes;
28658
- }
28659
- function addScopedOperation(operationsByScope, event, state) {
28660
- for (const scope of operationScopeNames(event)) {
28661
- addOperationToScope(operationsByScope, scope, state);
28293
+ startSyntheticCompaction(event) {
28294
+ const metadata = {
28295
+ ...extractEventMetadata(event),
28296
+ provider: "flue"
28297
+ };
28298
+ const span = startFlueSpan(this.parentSpanForEvent(event), {
28299
+ name: "compaction:unknown",
28300
+ spanAttributes: { type: "task" /* TASK */ },
28301
+ startTime: eventTime(event.timestamp),
28302
+ event: { metadata }
28303
+ });
28304
+ return { metadata, span };
28662
28305
  }
28663
- }
28664
- function addOperationToScope(operationsByScope, scope, state) {
28665
- const operations = operationsByScope.get(scope);
28666
- if (operations) {
28667
- if (!operations.includes(state)) {
28668
- operations.push(state);
28306
+ finishPendingChildrenForOperation(event, operationOutput2) {
28307
+ const endTime = eventTime(event.timestamp);
28308
+ const usage = event.usage ?? usageFromOperationResult(event.result);
28309
+ const turnEntries = [...this.turnsByKey].filter(
28310
+ ([, state]) => stateMatchesOperation(state, event.operationId)
28311
+ );
28312
+ turnEntries.forEach(([key, state], index) => {
28313
+ const shouldLogOperationOutput = (event.operationKind === "prompt" || event.operationKind === "skill") && index === turnEntries.length - 1 && operationOutput2 !== void 0;
28314
+ safeLog3(state.span, {
28315
+ metadata: state.metadata,
28316
+ metrics: metricsFromUsage(usage),
28317
+ ...shouldLogOperationOutput ? { output: operationOutput2 } : {}
28318
+ });
28319
+ safeEnd(state.span, endTime);
28320
+ this.turnsByKey.delete(key);
28321
+ });
28322
+ for (const [key, state] of this.toolsByKey) {
28323
+ if (!stateMatchesOperation(state, event.operationId)) {
28324
+ continue;
28325
+ }
28326
+ safeEnd(state.span, endTime);
28327
+ this.toolsByKey.delete(key);
28669
28328
  }
28670
- } else {
28671
- operationsByScope.set(scope, [state]);
28672
- }
28673
- }
28674
- function removeScopedOperation(operationsByScope, state) {
28675
- for (const [scope, operations] of operationsByScope) {
28676
- const index = operations.indexOf(state);
28677
- if (index === -1) {
28678
- continue;
28329
+ for (const [key, state] of this.tasksById) {
28330
+ if (!stateMatchesOperation(state, event.operationId)) {
28331
+ continue;
28332
+ }
28333
+ safeEnd(state.span, endTime);
28334
+ this.tasksById.delete(key);
28679
28335
  }
28680
- operations.splice(index, 1);
28681
- if (operations.length === 0) {
28682
- operationsByScope.delete(scope);
28336
+ for (const [key, state] of this.compactionsByKey) {
28337
+ if (!stateMatchesOperation(state, event.operationId)) {
28338
+ continue;
28339
+ }
28340
+ safeLog3(state.span, {
28341
+ metadata: state.metadata,
28342
+ metrics: durationMetrics2(event.durationMs),
28343
+ output: { completed: true }
28344
+ });
28345
+ safeEnd(state.span, eventTime(event.timestamp));
28346
+ this.compactionsByKey.delete(key);
28683
28347
  }
28684
28348
  }
28685
- }
28686
- function removePendingOperation(pendingOperationsByKey, state) {
28687
- for (const [key, queue2] of pendingOperationsByKey) {
28688
- const index = queue2.indexOf(state);
28689
- if (index === -1) {
28690
- continue;
28349
+ finishPendingSpansForRun(event) {
28350
+ const endTime = eventTime(event.timestamp);
28351
+ for (const [key, state] of this.toolsByKey) {
28352
+ if (!stateMatchesRun(state, event.runId)) {
28353
+ continue;
28354
+ }
28355
+ safeEnd(state.span, endTime);
28356
+ this.toolsByKey.delete(key);
28691
28357
  }
28692
- queue2.splice(index, 1);
28693
- if (queue2.length === 0) {
28694
- pendingOperationsByKey.delete(key);
28358
+ for (const [key, state] of this.turnsByKey) {
28359
+ if (!stateMatchesRun(state, event.runId)) {
28360
+ continue;
28361
+ }
28362
+ safeEnd(state.span, endTime);
28363
+ this.turnsByKey.delete(key);
28364
+ }
28365
+ for (const [key, state] of this.tasksById) {
28366
+ if (!stateMatchesRun(state, event.runId)) {
28367
+ continue;
28368
+ }
28369
+ safeEnd(state.span, endTime);
28370
+ this.tasksById.delete(key);
28371
+ }
28372
+ for (const [key, state] of this.compactionsByKey) {
28373
+ if (!stateMatchesRun(state, event.runId)) {
28374
+ continue;
28375
+ }
28376
+ safeLog3(state.span, {
28377
+ metadata: state.metadata,
28378
+ output: { completed: true }
28379
+ });
28380
+ safeEnd(state.span, endTime);
28381
+ this.compactionsByKey.delete(key);
28382
+ }
28383
+ for (const [key, state] of this.operationsById) {
28384
+ if (!stateMatchesRun(state, event.runId)) {
28385
+ continue;
28386
+ }
28387
+ safeLog3(state.span, {
28388
+ metadata: state.metadata,
28389
+ ...state.metadata["flue.operation"] === "compact" ? { output: { completed: true } } : {}
28390
+ });
28391
+ safeEnd(state.span, endTime);
28392
+ this.operationsById.delete(key);
28695
28393
  }
28696
- return;
28697
28394
  }
28395
+ };
28396
+ function isInstrumentedOperation(operation) {
28397
+ return operation === "prompt" || operation === "skill" || operation === "compact";
28698
28398
  }
28699
- function extractSessionMetadata(session) {
28700
- const sessionName = getSessionName(session);
28701
- return sessionName ? { "flue.session": sessionName } : {};
28702
- }
28703
- function extractEventMetadata(event) {
28399
+ function extractEventMetadata(event, ctx) {
28704
28400
  return {
28705
28401
  ...event.runId ? { "flue.run_id": event.runId } : {},
28402
+ ...event.instanceId ? { "flue.instance_id": event.instanceId } : {},
28403
+ ...event.dispatchId ? { "flue.dispatch_id": event.dispatchId } : {},
28706
28404
  ...typeof event.eventIndex === "number" ? { "flue.event_index": event.eventIndex } : {},
28707
28405
  ...event.session ? { "flue.session": event.session } : {},
28708
28406
  ...event.parentSession ? { "flue.parent_session": event.parentSession } : {},
28709
28407
  ...event.harness ? { "flue.harness": event.harness } : {},
28710
28408
  ...event.taskId ? { "flue.task_id": event.taskId } : {},
28711
- ...event.operationId ? { "flue.operation_id": event.operationId } : {}
28409
+ ...event.operationId ? { "flue.operation_id": event.operationId } : {},
28410
+ ...event.turnId ? { "flue.turn_id": event.turnId } : {},
28411
+ ...typeof ctx?.id === "string" ? { "flue.context_id": ctx.id } : {},
28412
+ ...typeof ctx?.runId === "string" ? { "flue.context_run_id": ctx.runId } : {}
28712
28413
  };
28713
28414
  }
28714
- function extractOperationInput(operation, args) {
28715
- switch (operation) {
28716
- case "prompt":
28717
- case "task":
28718
- return args[0];
28719
- case "skill":
28720
- return {
28721
- args: getOptionObject(args[1])?.args,
28722
- name: args[0]
28723
- };
28724
- case "compact":
28725
- return void 0;
28415
+ function extractPayloadMetadata(payload) {
28416
+ if (!isObjectLike(payload)) {
28417
+ return {};
28726
28418
  }
28419
+ const metadata = Reflect.get(payload, "metadata");
28420
+ if (!isObjectLike(metadata)) {
28421
+ return {};
28422
+ }
28423
+ return Object.fromEntries(Object.entries(metadata));
28727
28424
  }
28728
- function extractOperationInputMetadata(operation, args) {
28729
- const options = getOptionObject(args[1]);
28730
- return {
28731
- ...operation === "skill" && typeof args[0] === "string" ? { "flue.skill_name": args[0] } : {},
28732
- ...options?.model ? { model: options.model, "flue.model": options.model } : {},
28733
- ...options?.role ? { "flue.role": options.role } : {},
28734
- ...options?.thinkingLevel ? { "flue.thinking_level": options.thinkingLevel } : {},
28735
- ...typeof options?.cwd === "string" ? { "flue.cwd": options.cwd } : {},
28736
- ...Array.isArray(options?.tools) ? {
28737
- "flue.tools_count": options.tools.length,
28738
- tools: summarizeTools(options.tools)
28739
- } : {},
28740
- ...Array.isArray(options?.images) ? { "flue.images_count": options.images.length } : {},
28741
- ...options?.result || options?.schema ? { "flue.result_schema": true } : {}
28742
- };
28743
- }
28744
- function getOptionObject(value) {
28745
- return isObject(value) ? value : void 0;
28746
- }
28747
- function summarizeTools(tools) {
28748
- return tools.flatMap((tool) => {
28749
- if (!isObject(tool)) {
28750
- return [];
28751
- }
28752
- const name = typeof tool.name === "string" ? tool.name : void 0;
28753
- if (!name) {
28754
- return [];
28755
- }
28756
- return [
28757
- {
28758
- function: {
28759
- description: typeof tool.description === "string" ? tool.description : void 0,
28760
- name,
28761
- parameters: tool.parameters
28762
- },
28763
- type: "function"
28764
- }
28765
- ];
28766
- });
28425
+ function operationOutput(event) {
28426
+ if (event.operationKind === "prompt" || event.operationKind === "skill") {
28427
+ return llmResultFromOperationResult(event.result);
28428
+ }
28429
+ return event.result ?? (event.operationKind === "compact" ? { completed: true } : void 0);
28767
28430
  }
28768
- function extractPromptResponseMetadata(result) {
28769
- const modelId = result?.model && typeof result.model.id === "string" ? result.model.id : void 0;
28770
- return modelId ? {
28771
- model: modelId,
28772
- "flue.model": modelId
28773
- } : {};
28431
+ function llmResultFromOperationResult(result) {
28432
+ if (!isObjectLike(result)) {
28433
+ return result;
28434
+ }
28435
+ const text = Reflect.get(result, "text");
28436
+ return text === void 0 ? result : text;
28774
28437
  }
28775
- function extractOperationOutput(result) {
28776
- if (!result) {
28438
+ function usageFromOperationResult(result) {
28439
+ if (!isObjectLike(result)) {
28777
28440
  return void 0;
28778
28441
  }
28779
- if ("data" in result) {
28780
- return result.data;
28781
- }
28782
- if ("text" in result) {
28783
- return result.text;
28784
- }
28785
- return result;
28442
+ return Reflect.get(result, "usage");
28786
28443
  }
28787
28444
  function metricsFromUsage(usage) {
28445
+ if (!isObjectLike(usage)) {
28446
+ return {};
28447
+ }
28448
+ const cacheRead = Reflect.get(usage, "cacheRead");
28449
+ const cacheWrite = Reflect.get(usage, "cacheWrite");
28450
+ const cost = Reflect.get(usage, "cost");
28451
+ const input = Reflect.get(usage, "input");
28452
+ const output = Reflect.get(usage, "output");
28453
+ const totalTokens = Reflect.get(usage, "totalTokens");
28454
+ const totalCost = isObjectLike(cost) ? Reflect.get(cost, "total") : void 0;
28788
28455
  return {
28789
- ...typeof usage?.input === "number" ? { prompt_tokens: usage.input } : {},
28790
- ...typeof usage?.output === "number" ? { completion_tokens: usage.output } : {},
28791
- ...typeof usage?.cacheRead === "number" ? { prompt_cached_tokens: usage.cacheRead } : {},
28792
- ...typeof usage?.cacheWrite === "number" ? { prompt_cache_creation_tokens: usage.cacheWrite } : {},
28793
- ...typeof usage?.totalTokens === "number" ? { tokens: usage.totalTokens } : {},
28794
- ...typeof usage?.cost?.total === "number" ? { estimated_cost: usage.cost.total } : {}
28795
- };
28796
- }
28797
- function buildDurationMetrics3(startTime) {
28798
- return {
28799
- duration_ms: Math.max(0, (getCurrentUnixTimestamp() - startTime) * 1e3)
28456
+ ...typeof input === "number" ? { prompt_tokens: input } : {},
28457
+ ...typeof output === "number" ? { completion_tokens: output } : {},
28458
+ ...typeof cacheRead === "number" ? { prompt_cached_tokens: cacheRead } : {},
28459
+ ...typeof cacheWrite === "number" ? { prompt_cache_creation_tokens: cacheWrite } : {},
28460
+ ...typeof totalTokens === "number" ? { tokens: totalTokens } : {},
28461
+ ...typeof totalCost === "number" ? { estimated_cost: totalCost } : {}
28800
28462
  };
28801
28463
  }
28802
- function durationMsMetrics(durationMs) {
28464
+ function durationMetrics2(durationMs) {
28803
28465
  return typeof durationMs === "number" ? { duration_ms: durationMs } : {};
28804
28466
  }
28805
- function scopeKey(event) {
28806
- if (event.operationId) {
28807
- return `operation:${event.operationId}`;
28808
- }
28809
- if (event.taskId) {
28810
- return `task:${event.taskId}`;
28811
- }
28812
- if (event.session) {
28813
- return `session:${event.session}`;
28467
+ function eventTime(value) {
28468
+ if (typeof value !== "string") {
28469
+ return void 0;
28814
28470
  }
28815
- return "flue:unknown";
28471
+ const timestamp = Date.parse(value);
28472
+ return Number.isFinite(timestamp) ? timestamp / 1e3 : void 0;
28473
+ }
28474
+ function turnKey(event) {
28475
+ return event.turnId;
28816
28476
  }
28817
28477
  function toolKey(event) {
28818
- return `${scopeKey(event)}::tool:${event.toolCallId ?? "unknown"}`;
28478
+ return `${event.turnId ?? event.operationId ?? event.taskId ?? event.runId ?? "unknown"}:${event.toolCallId ?? "unknown"}`;
28819
28479
  }
28820
- function toAssistantOutput(text, finishReason, reasoning, toolCalls) {
28480
+ function compactionKey(event) {
28821
28481
  return [
28822
- {
28823
- finish_reason: finishReason ?? "stop",
28824
- index: 0,
28825
- message: {
28826
- content: text,
28827
- ...reasoning ? { reasoning } : {},
28828
- role: "assistant",
28829
- ...toolCalls?.length ? {
28830
- tool_calls: toolCalls.map((toolCall) => ({
28831
- function: {
28832
- arguments: toolCall.args === void 0 ? "{}" : JSON.stringify(toolCall.args),
28833
- name: toolCall.toolName ?? "unknown"
28834
- },
28835
- ...toolCall.toolCallId ? { id: toolCall.toolCallId } : {},
28836
- type: "function"
28837
- }))
28838
- } : {}
28839
- }
28840
- }
28841
- ];
28482
+ event.instanceId ?? "",
28483
+ event.runId ?? "",
28484
+ event.session ?? "",
28485
+ event.operationId ?? "",
28486
+ event.taskId ?? ""
28487
+ ].join(":");
28488
+ }
28489
+ function stateMatchesOperation(state, operationId) {
28490
+ return state.metadata["flue.operation_id"] === operationId;
28491
+ }
28492
+ function stateMatchesRun(state, runId) {
28493
+ return state.metadata["flue.run_id"] === runId;
28842
28494
  }
28843
28495
  function startFlueSpan(parent, args) {
28844
28496
  return parent ? withCurrent(parent, () => startSpan(args)) : startSpan(args);
@@ -28850,6 +28502,13 @@ function safeLog3(span, event) {
28850
28502
  logInstrumentationError3("Flue span log", error2);
28851
28503
  }
28852
28504
  }
28505
+ function safeEnd(span, endTime) {
28506
+ try {
28507
+ span.end(endTime === void 0 ? void 0 : { endTime });
28508
+ } catch (error2) {
28509
+ logInstrumentationError3("Flue span end", error2);
28510
+ }
28511
+ }
28853
28512
  function errorToString(error2) {
28854
28513
  if (error2 instanceof Error) {
28855
28514
  return error2.message;
@@ -29343,7 +29002,7 @@ var BraintrustPlugin = class extends BasePlugin {
29343
29002
  this.config = config3;
29344
29003
  }
29345
29004
  onEnable() {
29346
- const integrations = this.config.integrations || {};
29005
+ const integrations = this.config.integrations ?? {};
29347
29006
  if (integrations.openai !== false) {
29348
29007
  this.openaiPlugin = new OpenAIPlugin();
29349
29008
  this.openaiPlugin.enable();
@@ -29408,7 +29067,7 @@ var BraintrustPlugin = class extends BasePlugin {
29408
29067
  this.genkitPlugin = new GenkitPlugin();
29409
29068
  this.genkitPlugin.enable();
29410
29069
  }
29411
- if (getIntegrationConfig(integrations, "gitHubCopilot") !== false) {
29070
+ if (integrations.gitHubCopilot !== false) {
29412
29071
  this.gitHubCopilotPlugin = new GitHubCopilotPlugin();
29413
29072
  this.gitHubCopilotPlugin.enable();
29414
29073
  }
@@ -29521,6 +29180,7 @@ var envIntegrationAliases = {
29521
29180
  cursorsdk: "cursorSDK",
29522
29181
  flue: "flue",
29523
29182
  "flue-runtime": "flue",
29183
+ mastra: "mastra",
29524
29184
  "openai-agents": "openAIAgents",
29525
29185
  openaiagents: "openAIAgents",
29526
29186
  "openai-agents-core": "openAIAgents",
@@ -29563,6 +29223,7 @@ function getDefaultInstrumentationIntegrations() {
29563
29223
  cursor: true,
29564
29224
  cursorSDK: true,
29565
29225
  flue: true,
29226
+ mastra: true,
29566
29227
  openAIAgents: true,
29567
29228
  openrouter: true,
29568
29229
  openrouterAgent: true,
@@ -29587,6 +29248,9 @@ function readDisabledInstrumentationEnvConfig(disabledList) {
29587
29248
  }
29588
29249
  return { integrations };
29589
29250
  }
29251
+ function isInstrumentationIntegrationDisabled(integrations, ...names) {
29252
+ return names.some((name) => integrations?.[name] === false);
29253
+ }
29590
29254
 
29591
29255
  // src/instrumentation/registry.ts
29592
29256
  var REGISTRY_STATE_KEY = /* @__PURE__ */ Symbol.for("braintrust.registry");
@@ -29680,6 +29344,263 @@ var PluginRegistry = class {
29680
29344
  };
29681
29345
  var registry = new PluginRegistry();
29682
29346
 
29347
+ // src/auto-instrumentations/loader/mastra-observability-patch.ts
29348
+ var MASTRA_EXPORTER_FACTORY_GLOBAL = "__braintrustMastraExporterFactory";
29349
+ function installMastraExporterFactory(factory) {
29350
+ const globals = globalThis;
29351
+ globals[MASTRA_EXPORTER_FACTORY_GLOBAL] ??= factory;
29352
+ }
29353
+ var EXPORTER_FACTORY_KEY = JSON.stringify(MASTRA_EXPORTER_FACTORY_GLOBAL);
29354
+ var OBSERVABILITY_APPEND_BODY = `
29355
+ ;(function __braintrustWrapObservability() {
29356
+ // Top-level so we can both read and reassign the var binding the original
29357
+ // entry declared.
29358
+ if (typeof Observability === "undefined") return;
29359
+ if (Observability.__braintrustWrapped) return;
29360
+ function __braintrustEnsureExporter(rawConfig) {
29361
+ try {
29362
+ var factory = globalThis[${EXPORTER_FACTORY_KEY}];
29363
+ if (typeof factory !== "function") return rawConfig;
29364
+ var config = rawConfig && typeof rawConfig === "object" ? rawConfig : {};
29365
+ var configsIn = config.configs && typeof config.configs === "object" ? config.configs : null;
29366
+ var configsOut = {};
29367
+ var hadEntries = false;
29368
+ if (configsIn) {
29369
+ for (var name in configsIn) {
29370
+ if (!Object.prototype.hasOwnProperty.call(configsIn, name)) continue;
29371
+ hadEntries = true;
29372
+ var inst = configsIn[name] || {};
29373
+ var existing = Array.isArray(inst.exporters) ? inst.exporters : [];
29374
+ var hasOurs = existing.some(function (e) { return e && e.name === "braintrust"; });
29375
+ configsOut[name] = Object.assign({}, inst, {
29376
+ exporters: hasOurs ? existing : existing.concat([factory()]),
29377
+ });
29378
+ }
29379
+ }
29380
+ if (!hadEntries) {
29381
+ configsOut.default = {
29382
+ serviceName: "mastra",
29383
+ exporters: [factory()],
29384
+ };
29385
+ }
29386
+ return Object.assign({}, config, { configs: configsOut });
29387
+ } catch (e) {
29388
+ return rawConfig;
29389
+ }
29390
+ }
29391
+ var __OriginalObservability = Observability;
29392
+ Observability = new Proxy(__OriginalObservability, {
29393
+ construct: function (target, args, newTarget) {
29394
+ var nextArgs = args.slice();
29395
+ nextArgs[0] = __braintrustEnsureExporter(nextArgs[0]);
29396
+ return Reflect.construct(target, nextArgs, newTarget);
29397
+ },
29398
+ });
29399
+ Observability.__braintrustWrapped = true;
29400
+ if (typeof exports !== "undefined" && exports && typeof exports === "object") {
29401
+ try {
29402
+ Object.defineProperty(exports, "Observability", {
29403
+ enumerable: true,
29404
+ configurable: true,
29405
+ get: function () { return Observability; },
29406
+ });
29407
+ } catch (e) {}
29408
+ }
29409
+ })();
29410
+ `;
29411
+
29412
+ // src/wrappers/mastra.ts
29413
+ var MASTRA_BRAINTRUST_EXPORTER_NAME = "braintrust";
29414
+ var SPAN_TYPE_MAP = {
29415
+ agent_run: "task" /* TASK */,
29416
+ model_generation: "llm" /* LLM */,
29417
+ model_step: "llm" /* LLM */,
29418
+ model_chunk: "llm" /* LLM */,
29419
+ tool_call: "tool" /* TOOL */,
29420
+ mcp_tool_call: "tool" /* TOOL */,
29421
+ workflow_run: "task" /* TASK */,
29422
+ workflow_step: "function" /* FUNCTION */,
29423
+ workflow_conditional: "function" /* FUNCTION */,
29424
+ workflow_conditional_eval: "function" /* FUNCTION */,
29425
+ workflow_parallel: "function" /* FUNCTION */,
29426
+ workflow_loop: "function" /* FUNCTION */,
29427
+ workflow_sleep: "function" /* FUNCTION */,
29428
+ workflow_wait_event: "function" /* FUNCTION */,
29429
+ memory_operation: "function" /* FUNCTION */,
29430
+ workspace_action: "function" /* FUNCTION */,
29431
+ rag_ingestion: "task" /* TASK */,
29432
+ rag_embedding: "llm" /* LLM */,
29433
+ rag_vector_operation: "function" /* FUNCTION */,
29434
+ rag_action: "function" /* FUNCTION */,
29435
+ graph_action: "function" /* FUNCTION */,
29436
+ scorer_run: "score" /* SCORE */,
29437
+ scorer_step: "score" /* SCORE */,
29438
+ processor_run: "function" /* FUNCTION */,
29439
+ generic: "function" /* FUNCTION */
29440
+ };
29441
+ function spanTypeFor(mastraType) {
29442
+ return SPAN_TYPE_MAP[mastraType] ?? "function" /* FUNCTION */;
29443
+ }
29444
+ function epochSeconds(value) {
29445
+ if (value === void 0) return void 0;
29446
+ const ms = value instanceof Date ? value.getTime() : typeof value === "number" ? value : Date.parse(value);
29447
+ return Number.isFinite(ms) ? ms / 1e3 : void 0;
29448
+ }
29449
+ function modelMetrics(attributes) {
29450
+ if (!isObject(attributes)) return void 0;
29451
+ const usage = isObject(attributes.usage) ? attributes.usage : void 0;
29452
+ if (!usage) return void 0;
29453
+ const out = {};
29454
+ if (typeof usage.inputTokens === "number")
29455
+ out.prompt_tokens = usage.inputTokens;
29456
+ if (typeof usage.outputTokens === "number")
29457
+ out.completion_tokens = usage.outputTokens;
29458
+ if (typeof usage.inputTokens === "number" && typeof usage.outputTokens === "number") {
29459
+ out.tokens = usage.inputTokens + usage.outputTokens;
29460
+ }
29461
+ const inputDetails = isObject(usage.inputDetails) ? usage.inputDetails : void 0;
29462
+ const outputDetails = isObject(usage.outputDetails) ? usage.outputDetails : void 0;
29463
+ if (inputDetails && typeof inputDetails.cacheRead === "number") {
29464
+ out.prompt_cached_tokens = inputDetails.cacheRead;
29465
+ }
29466
+ if (inputDetails && typeof inputDetails.cacheWrite === "number") {
29467
+ out.prompt_cache_creation_tokens = inputDetails.cacheWrite;
29468
+ }
29469
+ if (outputDetails && typeof outputDetails.reasoning === "number") {
29470
+ out.completion_reasoning_tokens = outputDetails.reasoning;
29471
+ }
29472
+ return Object.keys(out).length > 0 ? out : void 0;
29473
+ }
29474
+ function buildMetadata(exported) {
29475
+ const out = {};
29476
+ if (exported.entityId !== void 0) out.entity_id = exported.entityId;
29477
+ if (exported.entityName !== void 0) out.entity_name = exported.entityName;
29478
+ if (exported.entityType !== void 0) out.entity_type = exported.entityType;
29479
+ if (exported.metadata && isObject(exported.metadata)) {
29480
+ Object.assign(out, exported.metadata);
29481
+ }
29482
+ if (exported.attributes && isObject(exported.attributes)) {
29483
+ for (const [key, value] of Object.entries(exported.attributes)) {
29484
+ if (key === "usage") continue;
29485
+ if (value !== void 0) out[key] = value;
29486
+ }
29487
+ }
29488
+ if (exported.tags && exported.tags.length > 0) {
29489
+ out.tags = exported.tags;
29490
+ }
29491
+ if (exported.requestContext && isObject(exported.requestContext)) {
29492
+ out.request_context = exported.requestContext;
29493
+ }
29494
+ return out;
29495
+ }
29496
+ var BraintrustObservabilityExporter = class {
29497
+ name = MASTRA_BRAINTRUST_EXPORTER_NAME;
29498
+ spans = /* @__PURE__ */ new Map();
29499
+ // Captured at the first SPAN_STARTED event. Mastra's observability bus may
29500
+ // dispatch later events outside the user's AsyncLocalStorage context, where
29501
+ // `currentSpan()` returns NOOP_SPAN — which would make our `startSpan()`
29502
+ // calls go to a no-op logger and silently drop. Anchoring on the parent
29503
+ // we observe while still in-context keeps the whole Mastra subtree under
29504
+ // the user's traced scenario.
29505
+ capturedParent;
29506
+ constructor() {
29507
+ _internalSetInitialState();
29508
+ }
29509
+ async exportTracingEvent(event) {
29510
+ const exported = event.exportedSpan;
29511
+ if (exported.isInternal === true) return;
29512
+ try {
29513
+ switch (event.type) {
29514
+ case "span_started":
29515
+ this.onStart(exported);
29516
+ break;
29517
+ case "span_updated":
29518
+ this.onUpdate(exported);
29519
+ break;
29520
+ case "span_ended":
29521
+ this.onEnd(exported);
29522
+ break;
29523
+ }
29524
+ } catch (err) {
29525
+ logExporterError(err);
29526
+ }
29527
+ }
29528
+ async flush() {
29529
+ const state = _internalGetGlobalState();
29530
+ if (state) {
29531
+ await state.bgLogger().flush();
29532
+ }
29533
+ }
29534
+ async shutdown() {
29535
+ await this.flush();
29536
+ this.spans.clear();
29537
+ }
29538
+ onStart(exported) {
29539
+ if (this.spans.has(exported.id)) return;
29540
+ const args = {
29541
+ name: exported.name,
29542
+ spanAttributes: { type: spanTypeFor(exported.type) },
29543
+ startTime: epochSeconds(exported.startTime)
29544
+ };
29545
+ const parentRecord = exported.parentSpanId ? this.spans.get(exported.parentSpanId) : void 0;
29546
+ if (!this.capturedParent) {
29547
+ const probe = currentSpan();
29548
+ if (probe && probe.spanId) {
29549
+ this.capturedParent = probe;
29550
+ }
29551
+ }
29552
+ const span = parentRecord ? parentRecord.span.startSpan(args) : this.capturedParent ? this.capturedParent.startSpan(args) : startSpan(args);
29553
+ const record = { span, hasLoggedInput: false };
29554
+ this.logPayload(record, exported);
29555
+ this.spans.set(exported.id, record);
29556
+ if (exported.isEvent === true) {
29557
+ span.end({ endTime: args.startTime });
29558
+ this.spans.delete(exported.id);
29559
+ }
29560
+ }
29561
+ onUpdate(exported) {
29562
+ const record = this.spans.get(exported.id);
29563
+ if (!record) return;
29564
+ this.logPayload(record, exported);
29565
+ }
29566
+ onEnd(exported) {
29567
+ const record = this.spans.get(exported.id);
29568
+ if (!record) return;
29569
+ this.logPayload(record, exported);
29570
+ if (exported.errorInfo) {
29571
+ record.span.log({
29572
+ error: exported.errorInfo.message || exported.errorInfo.name || "Unknown Mastra error"
29573
+ });
29574
+ }
29575
+ record.span.end({ endTime: epochSeconds(exported.endTime) });
29576
+ this.spans.delete(exported.id);
29577
+ }
29578
+ logPayload(record, exported) {
29579
+ const event = {};
29580
+ if (exported.input !== void 0) {
29581
+ event.input = exported.input;
29582
+ record.hasLoggedInput = true;
29583
+ }
29584
+ if (exported.output !== void 0) {
29585
+ event.output = exported.output;
29586
+ }
29587
+ const metadata = buildMetadata(exported);
29588
+ if (Object.keys(metadata).length > 0) {
29589
+ event.metadata = metadata;
29590
+ }
29591
+ const metrics = modelMetrics(exported.attributes);
29592
+ if (metrics) {
29593
+ event.metrics = metrics;
29594
+ }
29595
+ if (Object.keys(event).length > 0) {
29596
+ record.span.log(event);
29597
+ }
29598
+ }
29599
+ };
29600
+ function logExporterError(err) {
29601
+ debugLogger.warn("Mastra exporter failure:", err);
29602
+ }
29603
+
29683
29604
  // src/node/config.ts
29684
29605
  var BRAINTRUST_ENV_SEARCH_PARENT_LIMIT = 64;
29685
29606
  function configureNode() {
@@ -29765,6 +29686,12 @@ function configureNode() {
29765
29686
  isomorph_default.gunzip = (0, import_node_util3.promisify)(zlib.gunzip);
29766
29687
  isomorph_default.hash = (data) => crypto.createHash("sha256").update(data).digest("hex");
29767
29688
  _internalSetInitialState();
29689
+ const disabled = readDisabledInstrumentationEnvConfig(
29690
+ isomorph_default.getEnv("BRAINTRUST_DISABLE_INSTRUMENTATION")
29691
+ ).integrations;
29692
+ if (!isInstrumentationIntegrationDisabled(disabled, "mastra")) {
29693
+ installMastraExporterFactory(() => new BraintrustObservabilityExporter());
29694
+ }
29768
29695
  registry.enable();
29769
29696
  }
29770
29697
 
@@ -29772,7 +29699,7 @@ function configureNode() {
29772
29699
  var import_env2 = require("@next/env");
29773
29700
 
29774
29701
  // src/cli/functions/upload.ts
29775
- var import_node_fs = __toESM(require("node:fs"));
29702
+ var import_node_fs2 = __toESM(require("node:fs"));
29776
29703
  var import_node_path3 = __toESM(require("node:path"));
29777
29704
  var import_node_zlib = require("node:zlib");
29778
29705
  var import_v312 = require("zod/v3");
@@ -29783,21 +29710,7 @@ var fs2 = __toESM(require("node:fs/promises"));
29783
29710
 
29784
29711
  // src/cli/jest/nodeModulesPaths.ts
29785
29712
  var path2 = __toESM(require("node:path"));
29786
-
29787
- // src/cli/jest/tryRealpath.ts
29788
- var import_graceful_fs = require("graceful-fs");
29789
- function tryRealpath(path8) {
29790
- try {
29791
- path8 = import_graceful_fs.realpathSync.native(path8);
29792
- } catch (error2) {
29793
- if (error2.code !== "ENOENT" && error2.code !== "EISDIR") {
29794
- throw error2;
29795
- }
29796
- }
29797
- return path8;
29798
- }
29799
-
29800
- // src/cli/jest/nodeModulesPaths.ts
29713
+ var import_node_fs = require("node:fs");
29801
29714
  function nodeModulesPaths(basedir, options) {
29802
29715
  const modules = options && options.moduleDirectory ? Array.from(options.moduleDirectory) : ["node_modules"];
29803
29716
  const basedirAbs = path2.resolve(basedir);
@@ -29809,7 +29722,7 @@ function nodeModulesPaths(basedir, options) {
29809
29722
  }
29810
29723
  let physicalBasedir;
29811
29724
  try {
29812
- physicalBasedir = tryRealpath(basedirAbs);
29725
+ physicalBasedir = import_node_fs.realpathSync.native(basedirAbs);
29813
29726
  } catch {
29814
29727
  physicalBasedir = basedirAbs;
29815
29728
  }
@@ -30786,7 +30699,7 @@ async function uploadBundles({
30786
30699
  if (!pathInfo) {
30787
30700
  return true;
30788
30701
  }
30789
- const bundleStream = import_node_fs.default.createReadStream(bundleFile).pipe((0, import_node_zlib.createGzip)());
30702
+ const bundleStream = import_node_fs2.default.createReadStream(bundleFile).pipe((0, import_node_zlib.createGzip)());
30790
30703
  const bundleData = await new Promise((resolve2, reject2) => {
30791
30704
  const chunks = [];
30792
30705
  bundleStream.on("data", (chunk) => {
@@ -32299,7 +32212,7 @@ function checkMatch(pathInput, include_patterns, exclude_patterns) {
32299
32212
  async function collectFiles(inputPath, mode) {
32300
32213
  let pathStat = null;
32301
32214
  try {
32302
- pathStat = import_node_fs2.default.lstatSync(inputPath);
32215
+ pathStat = import_node_fs3.default.lstatSync(inputPath);
32303
32216
  } catch (e) {
32304
32217
  console.error(error(`Error reading ${inputPath}: ${e}`));
32305
32218
  process.exit(1);