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/workerd.js CHANGED
@@ -38,6 +38,7 @@ __export(workerd_exports, {
38
38
  BaseExperiment: () => BaseExperiment,
39
39
  BraintrustLangChainCallbackHandler: () => BraintrustLangChainCallbackHandler,
40
40
  BraintrustMiddleware: () => BraintrustMiddleware,
41
+ BraintrustObservabilityExporter: () => BraintrustObservabilityExporter,
41
42
  BraintrustState: () => BraintrustState,
42
43
  BraintrustStream: () => BraintrustStream,
43
44
  CachedSpanFetcher: () => CachedSpanFetcher,
@@ -84,6 +85,7 @@ __export(workerd_exports, {
84
85
  _internalIso: () => isomorph_default,
85
86
  _internalSetInitialState: () => _internalSetInitialState,
86
87
  addAzureBlobHeaders: () => addAzureBlobHeaders,
88
+ braintrustFlueObserver: () => braintrustFlueObserver,
87
89
  braintrustStreamChunkSchema: () => braintrustStreamChunkSchema,
88
90
  buildLocalSummary: () => buildLocalSummary,
89
91
  configureInstrumentation: () => configureInstrumentation,
@@ -164,8 +166,6 @@ __export(workerd_exports, {
164
166
  wrapCohere: () => wrapCohere,
165
167
  wrapCopilotClient: () => wrapCopilotClient,
166
168
  wrapCursorSDK: () => wrapCursorSDK,
167
- wrapFlueContext: () => wrapFlueContext,
168
- wrapFlueSession: () => wrapFlueSession,
169
169
  wrapGenkit: () => wrapGenkit,
170
170
  wrapGoogleADK: () => wrapGoogleADK,
171
171
  wrapGoogleGenAI: () => wrapGoogleGenAI,
@@ -4112,6 +4112,76 @@ var LRUCache = class {
4112
4112
  }
4113
4113
  };
4114
4114
 
4115
+ // src/prompt-cache/cache-config.ts
4116
+ var CACHE_LOCATION_ENV_VAR = "BRAINTRUST_CACHE_LOCATION";
4117
+ var DEFAULT_CACHE_MEMORY_MAX = 1 << 10;
4118
+ var DEFAULT_CACHE_DISK_MAX = 1 << 20;
4119
+ var warnedInvalidCacheModeEnvValue = false;
4120
+ var warnedUnavailableDiskCacheMode = false;
4121
+ function warnInvalidCacheMode(value) {
4122
+ if (warnedInvalidCacheModeEnvValue) {
4123
+ return;
4124
+ }
4125
+ warnedInvalidCacheModeEnvValue = true;
4126
+ debugLogger.warn(
4127
+ `Invalid ${CACHE_LOCATION_ENV_VAR} value "${value}". Expected "mixed", "memory", "disk", or "none". Falling back to "mixed".`
4128
+ );
4129
+ }
4130
+ function warnUnavailableDiskCache() {
4131
+ if (warnedUnavailableDiskCacheMode) {
4132
+ return;
4133
+ }
4134
+ warnedUnavailableDiskCacheMode = true;
4135
+ debugLogger.warn(
4136
+ `Disk cache is not supported on this platform, so ${CACHE_LOCATION_ENV_VAR}="disk" disables prompt and parameters caching.`
4137
+ );
4138
+ }
4139
+ function parseCacheMode() {
4140
+ const value = isomorph_default.getEnv(CACHE_LOCATION_ENV_VAR);
4141
+ const normalized = value?.trim().toLowerCase();
4142
+ if (!normalized) {
4143
+ return "mixed";
4144
+ }
4145
+ if (normalized === "mixed" || normalized === "memory" || normalized === "disk" || normalized === "none") {
4146
+ return normalized;
4147
+ }
4148
+ warnInvalidCacheMode(value ?? "");
4149
+ return "mixed";
4150
+ }
4151
+ function parsePositiveIntegerEnv(envVar, defaultValue) {
4152
+ const value = Number(isomorph_default.getEnv(envVar));
4153
+ return Number.isInteger(value) && value > 0 ? value : defaultValue;
4154
+ }
4155
+ function createCacheLayers({
4156
+ memoryMaxEnvVar,
4157
+ diskCacheDirEnvVar,
4158
+ diskMaxEnvVar,
4159
+ getDefaultDiskCacheDir
4160
+ }) {
4161
+ const mode = parseCacheMode();
4162
+ const memoryCache = mode === "mixed" || mode === "memory" ? new LRUCache({
4163
+ max: parsePositiveIntegerEnv(
4164
+ memoryMaxEnvVar,
4165
+ DEFAULT_CACHE_MEMORY_MAX
4166
+ )
4167
+ }) : void 0;
4168
+ let diskCache;
4169
+ if (mode === "mixed" || mode === "disk") {
4170
+ if (canUseDiskCache()) {
4171
+ diskCache = new DiskCache({
4172
+ cacheDir: isomorph_default.getEnv(diskCacheDirEnvVar) ?? getDefaultDiskCacheDir(),
4173
+ max: parsePositiveIntegerEnv(diskMaxEnvVar, DEFAULT_CACHE_DISK_MAX)
4174
+ });
4175
+ } else if (mode === "disk") {
4176
+ warnUnavailableDiskCache();
4177
+ }
4178
+ }
4179
+ if (diskCache) {
4180
+ return { memoryCache, diskCache };
4181
+ }
4182
+ return { memoryCache };
4183
+ }
4184
+
4115
4185
  // src/prompt-cache/prompt-cache.ts
4116
4186
  function createCacheKey(key) {
4117
4187
  if (key.id) {
@@ -4139,16 +4209,18 @@ var PromptCache = class {
4139
4209
  */
4140
4210
  async get(key) {
4141
4211
  const cacheKey = createCacheKey(key);
4142
- const memoryPrompt = this.memoryCache.get(cacheKey);
4143
- if (memoryPrompt !== void 0) {
4144
- return memoryPrompt;
4212
+ if (this.memoryCache) {
4213
+ const memoryPrompt = this.memoryCache.get(cacheKey);
4214
+ if (memoryPrompt !== void 0) {
4215
+ return memoryPrompt;
4216
+ }
4145
4217
  }
4146
4218
  if (this.diskCache) {
4147
4219
  const diskPrompt = await this.diskCache.get(cacheKey);
4148
4220
  if (!diskPrompt) {
4149
4221
  return void 0;
4150
4222
  }
4151
- this.memoryCache.set(cacheKey, diskPrompt);
4223
+ this.memoryCache?.set(cacheKey, diskPrompt);
4152
4224
  return diskPrompt;
4153
4225
  }
4154
4226
  return void 0;
@@ -4163,7 +4235,7 @@ var PromptCache = class {
4163
4235
  */
4164
4236
  async set(key, value) {
4165
4237
  const cacheKey = createCacheKey(key);
4166
- this.memoryCache.set(cacheKey, value);
4238
+ this.memoryCache?.set(cacheKey, value);
4167
4239
  if (this.diskCache) {
4168
4240
  await this.diskCache.set(cacheKey, value);
4169
4241
  }
@@ -4193,23 +4265,25 @@ var ParametersCache = class {
4193
4265
  }
4194
4266
  async get(key) {
4195
4267
  const cacheKey = createCacheKey2(key);
4196
- const memoryParams = this.memoryCache.get(cacheKey);
4197
- if (memoryParams !== void 0) {
4198
- return memoryParams;
4268
+ if (this.memoryCache) {
4269
+ const memoryParams = this.memoryCache.get(cacheKey);
4270
+ if (memoryParams !== void 0) {
4271
+ return memoryParams;
4272
+ }
4199
4273
  }
4200
4274
  if (this.diskCache) {
4201
4275
  const diskParams = await this.diskCache.get(cacheKey);
4202
4276
  if (!diskParams) {
4203
4277
  return void 0;
4204
4278
  }
4205
- this.memoryCache.set(cacheKey, diskParams);
4279
+ this.memoryCache?.set(cacheKey, diskParams);
4206
4280
  return diskParams;
4207
4281
  }
4208
4282
  return void 0;
4209
4283
  }
4210
4284
  async set(key, value) {
4211
4285
  const cacheKey = createCacheKey2(key);
4212
- this.memoryCache.set(cacheKey, value);
4286
+ this.memoryCache?.set(cacheKey, value);
4213
4287
  if (this.diskCache) {
4214
4288
  await this.diskCache.set(cacheKey, value);
4215
4289
  }
@@ -4741,21 +4815,22 @@ var BraintrustState = class _BraintrustState {
4741
4815
  setGlobalDebugLogLevel(void 0);
4742
4816
  }
4743
4817
  this.resetLoginInfo();
4744
- const memoryCache = new LRUCache({
4745
- max: Number(isomorph_default.getEnv("BRAINTRUST_PROMPT_CACHE_MEMORY_MAX")) ?? 1 << 10
4818
+ const { memoryCache, diskCache } = createCacheLayers({
4819
+ memoryMaxEnvVar: "BRAINTRUST_PROMPT_CACHE_MEMORY_MAX",
4820
+ diskCacheDirEnvVar: "BRAINTRUST_PROMPT_CACHE_DIR",
4821
+ diskMaxEnvVar: "BRAINTRUST_PROMPT_CACHE_DISK_MAX",
4822
+ getDefaultDiskCacheDir: () => `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/prompt_cache`
4746
4823
  });
4747
- const diskCache = canUseDiskCache() ? new DiskCache({
4748
- cacheDir: isomorph_default.getEnv("BRAINTRUST_PROMPT_CACHE_DIR") ?? `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/prompt_cache`,
4749
- max: Number(isomorph_default.getEnv("BRAINTRUST_PROMPT_CACHE_DISK_MAX")) ?? 1 << 20
4750
- }) : void 0;
4751
4824
  this.promptCache = new PromptCache({ memoryCache, diskCache });
4752
- const parametersMemoryCache = new LRUCache({
4753
- max: Number(isomorph_default.getEnv("BRAINTRUST_PARAMETERS_CACHE_MEMORY_MAX")) ?? 1 << 10
4825
+ const {
4826
+ memoryCache: parametersMemoryCache,
4827
+ diskCache: parametersDiskCache
4828
+ } = createCacheLayers({
4829
+ memoryMaxEnvVar: "BRAINTRUST_PARAMETERS_CACHE_MEMORY_MAX",
4830
+ diskCacheDirEnvVar: "BRAINTRUST_PARAMETERS_CACHE_DIR",
4831
+ diskMaxEnvVar: "BRAINTRUST_PARAMETERS_CACHE_DISK_MAX",
4832
+ getDefaultDiskCacheDir: () => `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/parameters_cache`
4754
4833
  });
4755
- const parametersDiskCache = canUseDiskCache() ? new DiskCache({
4756
- cacheDir: isomorph_default.getEnv("BRAINTRUST_PARAMETERS_CACHE_DIR") ?? `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/parameters_cache`,
4757
- max: Number(isomorph_default.getEnv("BRAINTRUST_PARAMETERS_CACHE_DISK_MAX")) ?? 1 << 20
4758
- }) : void 0;
4759
4834
  this.parametersCache = new ParametersCache({
4760
4835
  memoryCache: parametersMemoryCache,
4761
4836
  diskCache: parametersDiskCache
@@ -24141,825 +24216,440 @@ var flueChannels = defineChannels("@flue/runtime", {
24141
24216
  createContext: channel({
24142
24217
  channelName: "createFlueContext",
24143
24218
  kind: "sync-stream"
24144
- }),
24145
- openSession: channel({
24146
- channelName: "Harness.openSession",
24147
- kind: "async"
24148
- }),
24149
- contextEvent: channel({
24150
- channelName: "context.event",
24151
- kind: "sync-stream"
24152
- }),
24153
- prompt: channel({
24154
- channelName: "session.prompt",
24155
- kind: "async"
24156
- }),
24157
- skill: channel({
24158
- channelName: "session.skill",
24159
- kind: "async"
24160
- }),
24161
- task: channel({
24162
- channelName: "session.task",
24163
- kind: "async"
24164
- }),
24165
- compact: channel({
24166
- channelName: "session.compact",
24167
- kind: "async"
24168
24219
  })
24169
24220
  });
24170
24221
 
24171
- // src/wrappers/flue.ts
24172
- var WRAPPED_FLUE_CONTEXT = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-context");
24173
- var WRAPPED_FLUE_HARNESS = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-harness");
24174
- var WRAPPED_FLUE_SESSION = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-session");
24175
- var SUBSCRIBED_FLUE_CONTEXT_EVENTS = /* @__PURE__ */ Symbol.for(
24176
- "braintrust.flue.subscribed-context-events"
24177
- );
24178
- function wrapFlueContext(ctx) {
24179
- if (!isPlausibleFlueContext(ctx)) {
24180
- console.warn("Unsupported Flue context. Not wrapping.");
24181
- return ctx;
24222
+ // src/instrumentation/plugins/flue-plugin.ts
24223
+ var FLUE_AUTO_STATE = /* @__PURE__ */ Symbol.for("braintrust.flue.auto-state");
24224
+ var FLUE_OBSERVE_BRIDGE = /* @__PURE__ */ Symbol.for("braintrust.flue.observe-bridge");
24225
+ var braintrustFlueObserver = (event, ctx) => {
24226
+ getObserveBridge().handle(event, ctx);
24227
+ };
24228
+ var FluePlugin = class extends BasePlugin {
24229
+ onEnable() {
24230
+ this.unsubscribers.push(enableFlueAutoInstrumentation());
24182
24231
  }
24183
- const context = ctx;
24184
- subscribeFlueContextEvents(context, { captureTurnSpans: true });
24185
- return patchFlueContextInPlace(context);
24186
- }
24187
- function patchFlueContextInPlace(ctx) {
24188
- const context = ctx;
24189
- if (context[WRAPPED_FLUE_CONTEXT]) {
24190
- return ctx;
24232
+ onDisable() {
24233
+ for (const unsubscribe of this.unsubscribers) {
24234
+ unsubscribe();
24235
+ }
24236
+ this.unsubscribers = [];
24191
24237
  }
24192
- const originalInit = context.init.bind(context);
24193
- try {
24194
- Object.defineProperty(context, WRAPPED_FLUE_CONTEXT, {
24195
- configurable: false,
24196
- enumerable: false,
24197
- value: true
24198
- });
24199
- Object.defineProperty(context, "init", {
24200
- configurable: true,
24201
- value: async function wrappedFlueInit(options) {
24202
- const harness = await originalInit(options);
24203
- return wrapFlueHarness(harness);
24204
- },
24205
- writable: true
24206
- });
24207
- } catch {
24238
+ };
24239
+ function enableFlueAutoInstrumentation() {
24240
+ const state = getAutoState();
24241
+ state.refCount += 1;
24242
+ if (!state.handlers) {
24243
+ const channel2 = flueChannels.createContext.tracingChannel();
24244
+ const handlers = {
24245
+ end: (event) => {
24246
+ subscribeToFlueContext(event.result, state);
24247
+ }
24248
+ };
24249
+ channel2.subscribe(handlers);
24250
+ state.channel = channel2;
24251
+ state.handlers = handlers;
24208
24252
  }
24209
- return ctx;
24253
+ let released = false;
24254
+ return () => {
24255
+ if (released) {
24256
+ return;
24257
+ }
24258
+ released = true;
24259
+ releaseAutoState(state);
24260
+ };
24210
24261
  }
24211
- function wrapFlueSession(session) {
24212
- if (!isPlausibleFlueSession(session)) {
24213
- console.warn("Unsupported Flue session. Not wrapping.");
24214
- return session;
24262
+ function getAutoState() {
24263
+ const existing = Reflect.get(globalThis, FLUE_AUTO_STATE);
24264
+ if (isAutoState(existing)) {
24265
+ return existing;
24215
24266
  }
24216
- return patchFlueSessionInPlace(session);
24267
+ const state = {
24268
+ contexts: /* @__PURE__ */ new WeakSet(),
24269
+ refCount: 0
24270
+ };
24271
+ Reflect.set(globalThis, FLUE_AUTO_STATE, state);
24272
+ return state;
24217
24273
  }
24218
- function subscribeFlueContextEvents(ctx, options = {}) {
24219
- if (!ctx || typeof ctx !== "object" || typeof ctx.subscribeEvent !== "function") {
24220
- return void 0;
24221
- }
24222
- const context = ctx;
24223
- const captureTurnSpans = options.captureTurnSpans ?? true;
24224
- const existingSubscription = context[SUBSCRIBED_FLUE_CONTEXT_EVENTS];
24225
- if (existingSubscription) {
24226
- if (existingSubscription.captureTurnSpans || !captureTurnSpans) {
24227
- return void 0;
24228
- }
24229
- try {
24230
- existingSubscription.unsubscribe();
24231
- } catch {
24232
- }
24233
- }
24234
- try {
24235
- const unsubscribe = ctx.subscribeEvent((event) => {
24236
- flueChannels.contextEvent.traceSync(() => void 0, {
24237
- arguments: [event],
24238
- captureTurnSpans,
24239
- context: ctx
24240
- });
24241
- });
24242
- if (existingSubscription) {
24243
- existingSubscription.captureTurnSpans = captureTurnSpans;
24244
- existingSubscription.unsubscribe = unsubscribe;
24245
- } else {
24246
- Object.defineProperty(context, SUBSCRIBED_FLUE_CONTEXT_EVENTS, {
24247
- configurable: false,
24248
- enumerable: false,
24249
- value: {
24250
- captureTurnSpans,
24251
- unsubscribe
24252
- }
24253
- });
24254
- }
24255
- return unsubscribe;
24256
- } catch {
24257
- return void 0;
24274
+ function getObserveBridge() {
24275
+ const existing = Reflect.get(globalThis, FLUE_OBSERVE_BRIDGE);
24276
+ if (isFlueObserveBridge(existing)) {
24277
+ return existing;
24258
24278
  }
24279
+ const bridge = new FlueObserveBridge();
24280
+ Reflect.set(globalThis, FLUE_OBSERVE_BRIDGE, bridge);
24281
+ return bridge;
24259
24282
  }
24260
- function wrapFlueHarness(harness) {
24261
- if (!isPlausibleFlueHarness(harness)) {
24262
- return harness;
24263
- }
24264
- const target = harness;
24265
- if (target[WRAPPED_FLUE_HARNESS]) {
24266
- return harness;
24267
- }
24268
- const originalSession = target.session.bind(target);
24269
- try {
24270
- Object.defineProperty(target, WRAPPED_FLUE_HARNESS, {
24271
- configurable: false,
24272
- enumerable: false,
24273
- value: true
24274
- });
24275
- Object.defineProperty(target, "session", {
24276
- configurable: true,
24277
- value: async function wrappedFlueHarnessSession(name, options) {
24278
- const session = await originalSession(name, options);
24279
- return patchFlueSessionInPlace(session);
24280
- },
24281
- writable: true
24282
- });
24283
- const sessions = target.sessions;
24284
- if (sessions && typeof sessions === "object") {
24285
- patchFlueSessionFactory(sessions, "get");
24286
- patchFlueSessionFactory(sessions, "create");
24287
- }
24288
- } catch {
24289
- }
24290
- return harness;
24283
+ function isFlueObserveBridge(value) {
24284
+ return isObjectLike(value) && typeof Reflect.get(value, "handle") === "function" && typeof Reflect.get(value, "reset") === "function";
24291
24285
  }
24292
- function patchFlueSessionInPlace(session) {
24293
- if (session[WRAPPED_FLUE_SESSION]) {
24294
- return session;
24295
- }
24296
- try {
24297
- Object.defineProperty(session, WRAPPED_FLUE_SESSION, {
24298
- configurable: false,
24299
- enumerable: false,
24300
- value: true
24301
- });
24302
- patchCallHandleMethod(session, "prompt", flueChannels.prompt);
24303
- patchCallHandleMethod(session, "skill", flueChannels.skill);
24304
- patchCallHandleMethod(session, "task", flueChannels.task);
24305
- patchCompact(session);
24306
- } catch {
24307
- }
24308
- return session;
24286
+ function isAutoState(value) {
24287
+ return isObjectLike(value) && Reflect.get(value, "contexts") instanceof WeakSet && typeof Reflect.get(value, "refCount") === "number";
24309
24288
  }
24310
- function patchFlueSessionFactory(sessions, method) {
24311
- const original = sessions[method];
24312
- if (typeof original !== "function") {
24289
+ function releaseAutoState(state) {
24290
+ state.refCount -= 1;
24291
+ if (state.refCount > 0) {
24313
24292
  return;
24314
24293
  }
24315
- const bound = original.bind(sessions);
24316
- Object.defineProperty(sessions, method, {
24317
- configurable: true,
24318
- value: async function wrappedFlueSessionFactory(name, options) {
24319
- const session = await bound(name, options);
24320
- return patchFlueSessionInPlace(session);
24321
- },
24322
- writable: true
24323
- });
24324
- }
24325
- function patchCallHandleMethod(session, method, channel2) {
24326
- const original = session[method];
24327
- if (typeof original !== "function") {
24328
- return;
24294
+ try {
24295
+ if (state.channel && state.handlers) {
24296
+ state.channel.unsubscribe(state.handlers);
24297
+ }
24298
+ } finally {
24299
+ Reflect.deleteProperty(globalThis, FLUE_AUTO_STATE);
24329
24300
  }
24330
- const bound = original.bind(session);
24331
- Object.defineProperty(session, method, {
24332
- configurable: true,
24333
- value(input, options) {
24334
- const args = [input, options];
24335
- const { originalResult, traced: traced2 } = traceFlueOperation(channel2, {
24336
- context: {
24337
- arguments: args,
24338
- operation: method,
24339
- session
24340
- },
24341
- run: () => bound(input, options)
24342
- });
24343
- return preserveCallHandle(originalResult, traced2);
24344
- },
24345
- writable: true
24346
- });
24347
24301
  }
24348
- function patchCompact(session) {
24349
- const original = session.compact;
24350
- if (typeof original !== "function") {
24302
+ function subscribeToFlueContext(value, state) {
24303
+ if (!isObservableFlueContext(value) || state.contexts.has(value)) {
24351
24304
  return;
24352
24305
  }
24353
- const bound = original.bind(session);
24354
- Object.defineProperty(session, "compact", {
24355
- configurable: true,
24356
- value() {
24357
- const context = {
24358
- arguments: [],
24359
- operation: "compact",
24360
- session
24361
- };
24362
- return flueChannels.compact.tracePromise(() => bound(), context);
24363
- },
24364
- writable: true
24365
- });
24366
- }
24367
- function traceFlueOperation(channel2, args) {
24368
- const tracingChannel2 = channel2.tracingChannel();
24369
- const context = args.context;
24370
- let originalResult;
24371
- let traced2;
24372
- const run = () => {
24306
+ const ctx = flueContextFromUnknown(value);
24307
+ let released = false;
24308
+ let unsubscribe;
24309
+ const release = () => {
24310
+ if (released) {
24311
+ return;
24312
+ }
24313
+ released = true;
24373
24314
  try {
24374
- originalResult = args.run();
24375
- tracingChannel2.end?.publish(context);
24315
+ unsubscribe?.();
24376
24316
  } catch (error) {
24377
- context.error = normalizeError3(error);
24378
- tracingChannel2.error?.publish(context);
24379
- tracingChannel2.end?.publish(context);
24380
- throw error;
24317
+ logInstrumentationError3("Flue context unsubscribe", error);
24381
24318
  }
24382
- traced2 = Promise.resolve(originalResult).then(
24383
- (result) => {
24384
- context.result = result;
24385
- tracingChannel2.asyncStart?.publish(context);
24386
- tracingChannel2.asyncEnd?.publish(context);
24387
- return result;
24388
- },
24389
- (error) => {
24390
- context.error = normalizeError3(error);
24391
- tracingChannel2.error?.publish(context);
24392
- tracingChannel2.asyncStart?.publish(context);
24393
- tracingChannel2.asyncEnd?.publish(context);
24394
- throw error;
24395
- }
24396
- );
24397
24319
  };
24398
- if (tracingChannel2.start?.runStores) {
24399
- tracingChannel2.start.runStores(context, run);
24400
- } else {
24401
- tracingChannel2.start?.publish(context);
24402
- run();
24320
+ try {
24321
+ unsubscribe = value.subscribeEvent((event) => {
24322
+ if (state.refCount <= 0) {
24323
+ release();
24324
+ return;
24325
+ }
24326
+ braintrustFlueObserver(event, ctx);
24327
+ if (isAutoContextTerminalEvent(event, ctx)) {
24328
+ release();
24329
+ }
24330
+ });
24331
+ state.contexts.add(value);
24332
+ } catch (error) {
24333
+ logInstrumentationError3("Flue context subscription", error);
24403
24334
  }
24404
- return { originalResult, traced: traced2 };
24405
- }
24406
- function normalizeError3(error) {
24407
- return error instanceof Error ? error : new Error(String(error));
24408
24335
  }
24409
- function preserveCallHandle(originalHandle, traced2) {
24410
- if (!isFlueCallHandle(originalHandle)) {
24411
- return traced2;
24336
+ function isAutoContextTerminalEvent(event, ctx) {
24337
+ if (!isObjectLike(event)) {
24338
+ return false;
24412
24339
  }
24413
- const handle = originalHandle;
24414
- const wrapped = {
24415
- get signal() {
24416
- return handle.signal;
24417
- },
24418
- abort(reason) {
24419
- return handle.abort(reason);
24420
- },
24421
- then(onfulfilled, onrejected) {
24422
- return traced2.then(onfulfilled, onrejected);
24423
- }
24424
- };
24425
- return wrapped;
24340
+ const type = Reflect.get(event, "type");
24341
+ if (type === "run_end") {
24342
+ return true;
24343
+ }
24344
+ if (type !== "operation") {
24345
+ return false;
24346
+ }
24347
+ return !ctx?.runId && typeof Reflect.get(event, "runId") !== "string";
24426
24348
  }
24427
- function isPlausibleFlueContext(value) {
24428
- return !!value && typeof value === "object" && typeof value.init === "function";
24349
+ function isObservableFlueContext(value) {
24350
+ return isObjectLike(value) && typeof Reflect.get(value, "subscribeEvent") === "function";
24429
24351
  }
24430
- function isPlausibleFlueHarness(value) {
24431
- return !!value && typeof value === "object" && typeof value.session === "function";
24352
+ function isFlueEvent(event) {
24353
+ const type = Reflect.get(event, "type");
24354
+ 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";
24432
24355
  }
24433
- function isPlausibleFlueSession(value) {
24434
- return !!value && typeof value === "object" && typeof value.prompt === "function" && typeof value.skill === "function" && typeof value.task === "function" && typeof value.compact === "function";
24356
+ function flueContextFromUnknown(ctx) {
24357
+ if (!isObjectLike(ctx)) {
24358
+ return void 0;
24359
+ }
24360
+ const id = Reflect.get(ctx, "id");
24361
+ const runId = Reflect.get(ctx, "runId");
24362
+ return {
24363
+ ...typeof id === "string" ? { id } : {},
24364
+ ...typeof runId === "string" ? { runId } : {}
24365
+ };
24435
24366
  }
24436
- function isFlueCallHandle(value) {
24437
- return !!value && typeof value === "object" && typeof value.then === "function" && typeof value.abort === "function" && "signal" in value;
24367
+ function isObjectLike(value) {
24368
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24438
24369
  }
24439
-
24440
- // src/instrumentation/plugins/flue-plugin.ts
24441
- var FluePlugin = class extends BasePlugin {
24442
- activeOperationsById = /* @__PURE__ */ new Map();
24443
- activeOperationsByScope = /* @__PURE__ */ new Map();
24444
- compactionsByScope = /* @__PURE__ */ new Map();
24445
- pendingOperationsByKey = /* @__PURE__ */ new Map();
24370
+ var FlueObserveBridge = class {
24371
+ compactionsByKey = /* @__PURE__ */ new Map();
24372
+ operationsById = /* @__PURE__ */ new Map();
24373
+ runsById = /* @__PURE__ */ new Map();
24374
+ seenEvents = /* @__PURE__ */ new WeakSet();
24446
24375
  tasksById = /* @__PURE__ */ new Map();
24447
- toolsById = /* @__PURE__ */ new Map();
24448
- turnsByScope = /* @__PURE__ */ new Map();
24449
- onEnable() {
24450
- this.subscribeToContextCreation();
24451
- this.subscribeToSessionCreation();
24452
- this.subscribeToContextEvents();
24453
- this.subscribeToSessionOperations();
24454
- }
24455
- onDisable() {
24456
- for (const unsubscribe of this.unsubscribers) {
24457
- unsubscribe();
24376
+ toolsByKey = /* @__PURE__ */ new Map();
24377
+ turnsByKey = /* @__PURE__ */ new Map();
24378
+ handle(event, ctx) {
24379
+ if (!isObjectLike(event) || !isFlueEvent(event)) {
24380
+ return;
24458
24381
  }
24459
- this.unsubscribers = [];
24460
- this.activeOperationsById.clear();
24461
- this.activeOperationsByScope.clear();
24462
- this.compactionsByScope.clear();
24463
- this.pendingOperationsByKey.clear();
24382
+ if (this.seenEvents.has(event)) {
24383
+ return;
24384
+ }
24385
+ this.seenEvents.add(event);
24386
+ try {
24387
+ this.handleEvent(event, flueContextFromUnknown(ctx));
24388
+ } catch (error) {
24389
+ logInstrumentationError3("Flue observe", error);
24390
+ }
24391
+ }
24392
+ reset() {
24393
+ this.compactionsByKey.clear();
24394
+ this.operationsById.clear();
24395
+ this.runsById.clear();
24396
+ this.seenEvents = /* @__PURE__ */ new WeakSet();
24464
24397
  this.tasksById.clear();
24465
- this.toolsById.clear();
24466
- this.turnsByScope.clear();
24398
+ this.toolsByKey.clear();
24399
+ this.turnsByKey.clear();
24467
24400
  }
24468
- subscribeToContextCreation() {
24469
- const channel2 = flueChannels.createContext.tracingChannel();
24470
- const handlers = {
24471
- end: (event) => {
24472
- const ctx = event.result;
24473
- if (!ctx) {
24474
- return;
24475
- }
24476
- subscribeFlueContextEvents(ctx, { captureTurnSpans: false });
24477
- patchFlueContextInPlace(ctx);
24478
- },
24479
- error: () => {
24480
- }
24481
- };
24482
- channel2.subscribe(handlers);
24483
- this.unsubscribers.push(() => {
24484
- channel2.unsubscribe(handlers);
24485
- });
24401
+ handleEvent(event, ctx) {
24402
+ switch (event.type) {
24403
+ case "run_start":
24404
+ this.handleRunStart(event, ctx);
24405
+ return;
24406
+ case "run_end":
24407
+ this.handleRunEnd(event);
24408
+ return;
24409
+ case "operation_start":
24410
+ this.handleOperationStart(event);
24411
+ return;
24412
+ case "operation":
24413
+ this.handleOperation(event);
24414
+ return;
24415
+ case "turn_request":
24416
+ this.handleTurnRequest(event);
24417
+ return;
24418
+ case "turn":
24419
+ this.handleTurn(event);
24420
+ return;
24421
+ case "tool_start":
24422
+ this.handleToolStart(event);
24423
+ return;
24424
+ case "tool_call":
24425
+ this.handleToolCall(event);
24426
+ return;
24427
+ case "task_start":
24428
+ this.handleTaskStart(event);
24429
+ return;
24430
+ case "task":
24431
+ this.handleTask(event);
24432
+ return;
24433
+ case "compaction_start":
24434
+ this.handleCompactionStart(event);
24435
+ return;
24436
+ case "compaction":
24437
+ this.handleCompaction(event);
24438
+ return;
24439
+ default:
24440
+ return;
24441
+ }
24486
24442
  }
24487
- subscribeToSessionCreation() {
24488
- const channel2 = flueChannels.openSession.tracingChannel();
24489
- const handlers = {
24490
- asyncEnd: (event) => {
24491
- if (event.result) {
24492
- patchFlueSessionInPlace(
24493
- event.result
24494
- );
24495
- }
24496
- if (event.harness) {
24497
- wrapFlueHarness(event.harness);
24498
- }
24499
- },
24500
- error: () => {
24501
- }
24443
+ handleRunStart(event, ctx) {
24444
+ if (!event.runId) {
24445
+ return;
24446
+ }
24447
+ const workflowName = event.workflowName ?? event.owner?.workflowName ?? (typeof ctx?.id === "string" ? ctx.id : "unknown");
24448
+ const metadata = {
24449
+ ...extractPayloadMetadata(event.payload),
24450
+ ...extractEventMetadata(event, ctx),
24451
+ ...workflowName ? { "flue.workflow_name": workflowName } : {},
24452
+ provider: "flue"
24502
24453
  };
24503
- channel2.subscribe(handlers);
24504
- this.unsubscribers.push(() => {
24505
- channel2.unsubscribe(handlers);
24454
+ const span = startSpan({
24455
+ name: `workflow:${workflowName}`,
24456
+ spanAttributes: { type: "task" /* TASK */ },
24457
+ startTime: eventTime(event.startedAt ?? event.timestamp),
24458
+ event: {
24459
+ input: event.payload,
24460
+ metadata
24461
+ }
24506
24462
  });
24463
+ this.runsById.set(event.runId, { metadata, span });
24507
24464
  }
24508
- subscribeToSessionOperations() {
24509
- this.subscribeToSessionOperation(flueChannels.prompt);
24510
- this.subscribeToSessionOperation(flueChannels.skill);
24511
- this.subscribeToSessionOperation(flueChannels.task);
24512
- this.subscribeToCompact();
24513
- }
24514
- subscribeToSessionOperation(channel2) {
24515
- const tracingChannel2 = channel2.tracingChannel();
24516
- const states = /* @__PURE__ */ new WeakMap();
24517
- const ensureState2 = (event) => {
24518
- const existing = states.get(event);
24519
- if (existing) {
24520
- return existing;
24521
- }
24522
- const state = this.startOperationState({
24523
- args: event.arguments,
24524
- moduleVersion: typeof event.moduleVersion === "string" ? event.moduleVersion : void 0,
24525
- operation: event.operation,
24526
- session: event.session
24527
- });
24528
- states.set(event, state);
24529
- return state;
24530
- };
24531
- const unbindCurrentSpanStore = this.bindCurrentSpanStoreToOperationStart(
24532
- tracingChannel2,
24533
- ensureState2
24534
- );
24535
- const handlers = {
24536
- start: (event) => {
24537
- ensureState2(event);
24538
- },
24539
- asyncEnd: (event) => {
24540
- this.endOperationState(states.get(event), event.result);
24541
- states.delete(event);
24542
- },
24543
- error: (event) => {
24544
- const state = states.get(event);
24545
- if (state && event.error) {
24546
- safeLog3(state.span, { error: errorToString(event.error) });
24547
- this.finishOperationState(state);
24548
- }
24549
- states.delete(event);
24550
- }
24551
- };
24552
- tracingChannel2.subscribe(handlers);
24553
- this.unsubscribers.push(() => {
24554
- unbindCurrentSpanStore?.();
24555
- tracingChannel2.unsubscribe(handlers);
24556
- });
24557
- }
24558
- subscribeToCompact() {
24559
- const tracingChannel2 = flueChannels.compact.tracingChannel();
24560
- const states = /* @__PURE__ */ new WeakMap();
24561
- const ensureState2 = (event) => {
24562
- const existing = states.get(event);
24563
- if (existing) {
24564
- return existing;
24565
- }
24566
- const state = this.startOperationState({
24567
- args: [],
24568
- moduleVersion: typeof event.moduleVersion === "string" ? event.moduleVersion : void 0,
24569
- operation: event.operation,
24570
- session: event.session
24465
+ handleRunEnd(event) {
24466
+ const state = this.runsById.get(event.runId);
24467
+ this.finishPendingSpansForRun(event);
24468
+ if (state) {
24469
+ safeLog3(state.span, {
24470
+ ...event.isError ? { error: errorToString(event.error) } : {},
24471
+ metadata: {
24472
+ ...state.metadata,
24473
+ ...extractEventMetadata(event),
24474
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
24475
+ },
24476
+ metrics: durationMetrics2(event.durationMs),
24477
+ output: event.result
24571
24478
  });
24572
- states.set(event, state);
24573
- return state;
24574
- };
24575
- const unbindCurrentSpanStore = this.bindCurrentSpanStoreToOperationStart(
24576
- tracingChannel2,
24577
- ensureState2
24578
- );
24579
- const handlers = {
24580
- start: (event) => {
24581
- ensureState2(event);
24582
- },
24583
- asyncEnd: (event) => {
24584
- this.endOperationState(states.get(event), void 0);
24585
- states.delete(event);
24586
- },
24587
- error: (event) => {
24588
- const state = states.get(event);
24589
- if (state && event.error) {
24590
- safeLog3(state.span, { error: errorToString(event.error) });
24591
- this.finishOperationState(state);
24592
- }
24593
- states.delete(event);
24594
- }
24595
- };
24596
- tracingChannel2.subscribe(handlers);
24597
- this.unsubscribers.push(() => {
24598
- unbindCurrentSpanStore?.();
24599
- tracingChannel2.unsubscribe(handlers);
24600
- });
24601
- }
24602
- subscribeToContextEvents() {
24603
- const channel2 = flueChannels.contextEvent.tracingChannel();
24604
- const handlers = {
24605
- start: (event) => {
24606
- const flueEvent = event.arguments[0];
24607
- if (!flueEvent) {
24608
- return;
24609
- }
24610
- try {
24611
- this.handleFlueEvent(flueEvent, {
24612
- captureTurnSpans: event.captureTurnSpans !== false
24613
- });
24614
- } catch (error) {
24615
- logInstrumentationError3("Flue event", error);
24616
- }
24617
- },
24618
- error: () => {
24619
- }
24620
- };
24621
- channel2.subscribe(handlers);
24622
- this.unsubscribers.push(() => {
24623
- channel2.unsubscribe(handlers);
24624
- });
24625
- }
24626
- bindCurrentSpanStoreToOperationStart(tracingChannel2, ensureState2) {
24627
- const state = _internalGetGlobalState();
24628
- const startChannel = tracingChannel2.start;
24629
- const contextManager = state?.contextManager;
24630
- const currentSpanStore = contextManager ? contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
24631
- if (!currentSpanStore || !startChannel) {
24632
- return void 0;
24479
+ safeEnd(state.span, eventTime(event.timestamp));
24480
+ this.runsById.delete(event.runId);
24633
24481
  }
24634
- startChannel.bindStore(currentSpanStore, (event) => {
24635
- const operationState = ensureState2(event);
24636
- return contextManager.wrapSpanForStore(operationState.span);
24482
+ void flush().catch((error) => {
24483
+ logInstrumentationError3("Flue flush", error);
24637
24484
  });
24638
- return () => {
24639
- startChannel.unbindStore(currentSpanStore);
24640
- };
24641
- }
24642
- startOperationState(args) {
24643
- const sessionName = getSessionName(args.session);
24644
- const metadata = {
24645
- ...extractOperationInputMetadata(args.operation, args.args),
24646
- ...extractSessionMetadata(args.session),
24647
- "flue.operation": args.operation,
24648
- provider: "flue",
24649
- ...args.moduleVersion ? { "flue.version": args.moduleVersion } : {}
24650
- };
24651
- const span = startSpan({
24652
- name: `flue.session.${args.operation}`,
24653
- spanAttributes: { type: "task" /* TASK */ }
24654
- });
24655
- const state = {
24656
- metadata,
24657
- operation: args.operation,
24658
- sessionName,
24659
- span,
24660
- startTime: getCurrentUnixTimestamp()
24661
- };
24662
- safeLog3(span, {
24663
- input: extractOperationInput(args.operation, args.args),
24664
- metadata
24665
- });
24666
- this.pendingOperationQueue(operationKey(sessionName, args.operation)).push(
24667
- state
24668
- );
24669
- addOperationToScope(
24670
- this.activeOperationsByScope,
24671
- sessionName ?? "unknown",
24672
- state
24673
- );
24674
- return state;
24675
- }
24676
- endOperationState(state, result) {
24677
- if (!state) {
24678
- return;
24679
- }
24680
- const metadata = {
24681
- ...state.metadata,
24682
- ...extractPromptResponseMetadata(result)
24683
- };
24684
- const metrics = {
24685
- ...buildDurationMetrics3(state.startTime),
24686
- ...metricsFromUsage(result?.usage)
24687
- };
24688
- safeLog3(state.span, {
24689
- metadata,
24690
- metrics,
24691
- output: extractOperationOutput(result)
24692
- });
24693
- this.finishCompactionsForOperation(state);
24694
- this.finishOperationState(state);
24695
- }
24696
- finishOperationState(state) {
24697
- removePendingOperation(this.pendingOperationsByKey, state);
24698
- if (state.operationId) {
24699
- this.activeOperationsById.delete(state.operationId);
24700
- }
24701
- removeScopedOperation(this.activeOperationsByScope, state);
24702
- state.span.end();
24703
- }
24704
- handleFlueEvent(event, options) {
24705
- switch (event.type) {
24706
- case "operation_start":
24707
- this.handleOperationStart(event);
24708
- return;
24709
- case "operation":
24710
- this.handleOperation(event);
24711
- return;
24712
- case "text_delta":
24713
- if (!options.captureTurnSpans) {
24714
- return;
24715
- }
24716
- this.ensureTurnState(event).text.push(
24717
- typeof event.text === "string" ? event.text : ""
24718
- );
24719
- return;
24720
- case "thinking_start":
24721
- if (!options.captureTurnSpans) {
24722
- return;
24723
- }
24724
- this.handleThinkingStart(event);
24725
- return;
24726
- case "thinking_delta":
24727
- if (!options.captureTurnSpans) {
24728
- return;
24729
- }
24730
- this.handleThinkingDelta(event);
24731
- return;
24732
- case "thinking_end":
24733
- if (!options.captureTurnSpans) {
24734
- return;
24735
- }
24736
- this.handleThinkingEnd(event);
24737
- return;
24738
- case "turn":
24739
- if (!options.captureTurnSpans) {
24740
- return;
24741
- }
24742
- this.handleTurn(event);
24743
- return;
24744
- case "tool_start":
24745
- this.handleToolStart(event, options);
24746
- return;
24747
- case "tool_call":
24748
- this.handleToolCall(event);
24749
- return;
24750
- case "task_start":
24751
- this.handleTaskStart(event);
24752
- return;
24753
- case "task":
24754
- this.handleTask(event);
24755
- return;
24756
- case "compaction_start":
24757
- this.handleCompactionStart(event);
24758
- return;
24759
- case "compaction":
24760
- this.handleCompaction(event);
24761
- return;
24762
- default:
24763
- return;
24764
- }
24765
24485
  }
24766
24486
  handleOperationStart(event) {
24767
- if (!isInstrumentedOperation(event.operationKind)) {
24487
+ if (!event.operationId || !isInstrumentedOperation(event.operationKind)) {
24768
24488
  return;
24769
24489
  }
24770
- const state = this.takePendingOperationForEvent(event);
24771
- if (!state) {
24772
- return;
24773
- }
24774
- state.operationId = event.operationId;
24775
- this.activeOperationsById.set(event.operationId, state);
24776
- addScopedOperation(this.activeOperationsByScope, event, state);
24777
- state.metadata = {
24778
- ...state.metadata,
24490
+ const metadata = {
24779
24491
  ...extractEventMetadata(event),
24780
- "flue.operation_id": event.operationId
24492
+ "flue.operation": event.operationKind,
24493
+ provider: "flue"
24781
24494
  };
24782
- safeLog3(state.span, { metadata: state.metadata });
24495
+ const parent = this.parentSpanForEvent(event);
24496
+ const span = startFlueSpan(parent, {
24497
+ name: `flue.${event.operationKind}`,
24498
+ spanAttributes: { type: "task" /* TASK */ },
24499
+ startTime: eventTime(event.timestamp),
24500
+ event: { metadata }
24501
+ });
24502
+ this.operationsById.set(event.operationId, { metadata, span });
24783
24503
  }
24784
24504
  handleOperation(event) {
24785
- const state = event.operationId ? this.activeOperationsById.get(event.operationId) : void 0;
24786
- if (!state) {
24505
+ if (!isInstrumentedOperation(event.operationKind)) {
24787
24506
  return;
24788
24507
  }
24508
+ const state = this.operationsById.get(event.operationId) ?? this.startSyntheticOperation(event);
24509
+ const output = operationOutput(event);
24789
24510
  const metadata = {
24790
24511
  ...state.metadata,
24791
24512
  ...extractEventMetadata(event),
24792
- ...typeof event.durationMs === "number" ? { "flue.duration_ms": event.durationMs } : {},
24793
- ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
24513
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {},
24514
+ ...event.usage ? { "flue.usage": event.usage } : {}
24794
24515
  };
24795
- const metrics = metricsFromUsage(event.usage);
24516
+ this.finishPendingChildrenForOperation(event, output);
24796
24517
  safeLog3(state.span, {
24797
- ...event.error ? { error: errorToString(event.error) } : {},
24518
+ ...event.isError ? { error: errorToString(event.error) } : {},
24798
24519
  metadata,
24799
- ...Object.keys(metrics).length ? { metrics } : {}
24520
+ metrics: durationMetrics2(event.durationMs),
24521
+ output
24800
24522
  });
24523
+ safeEnd(state.span, eventTime(event.timestamp));
24524
+ this.operationsById.delete(event.operationId);
24801
24525
  }
24802
- ensureTurnState(event) {
24803
- const scope = scopeKey(event);
24804
- const existing = this.turnsByScope.get(scope);
24805
- if (existing) {
24806
- return existing;
24526
+ handleTurnRequest(event) {
24527
+ const key = turnKey(event);
24528
+ if (!key) {
24529
+ return;
24807
24530
  }
24808
- const parent = this.parentSpanForEvent(event);
24809
24531
  const metadata = {
24810
24532
  ...extractEventMetadata(event),
24811
- provider: "flue"
24533
+ ...event.api ? { "flue.api": event.api } : {},
24534
+ ...event.model ? { model: event.model, "flue.model": event.model } : {},
24535
+ ...event.provider ? { provider: event.provider } : { provider: "flue" },
24536
+ ...event.provider ? { "flue.provider": event.provider } : {},
24537
+ ...event.purpose ? { "flue.turn_purpose": event.purpose } : {},
24538
+ ...event.reasoning ? { reasoning: event.reasoning } : {},
24539
+ ...event.input?.systemPrompt ? { "flue.system_prompt": event.input.systemPrompt } : {},
24540
+ ...event.input?.tools ? { tools: event.input.tools } : {}
24812
24541
  };
24542
+ const parent = this.parentSpanForTurn(event);
24813
24543
  const span = startFlueSpan(parent, {
24814
- name: "flue.turn",
24815
- spanAttributes: { type: "llm" /* LLM */ }
24544
+ name: `llm:${event.model ?? event.purpose ?? "unknown"}`,
24545
+ spanAttributes: { type: "llm" /* LLM */ },
24546
+ startTime: eventTime(event.timestamp),
24547
+ event: {
24548
+ input: event.input?.messages,
24549
+ metadata
24550
+ }
24816
24551
  });
24817
- const state = {
24818
- metadata,
24819
- span,
24820
- hasThinking: false,
24821
- startTime: getCurrentUnixTimestamp(),
24822
- text: [],
24823
- thinking: [],
24824
- toolCalls: []
24825
- };
24826
- safeLog3(span, { metadata });
24827
- this.turnsByScope.set(scope, state);
24828
- return state;
24552
+ this.logOperationInput(
24553
+ event.operationId,
24554
+ event.input?.messages ?? event.input
24555
+ );
24556
+ this.turnsByKey.set(key, { metadata, span });
24829
24557
  }
24830
24558
  handleTurn(event) {
24831
- const scope = scopeKey(event);
24832
- const state = this.ensureTurnState(event);
24833
- const text = state.text.join("");
24834
- const reasoning = state.finalThinking ?? state.thinking.join("");
24835
- const outputReasoning = reasoning || (state.hasThinking ? "[reasoning stream present; content unavailable]" : void 0);
24559
+ const key = turnKey(event);
24560
+ if (!key) {
24561
+ return;
24562
+ }
24563
+ const state = this.turnsByKey.get(key) ?? this.startSyntheticTurn(event);
24836
24564
  const metadata = {
24837
24565
  ...state.metadata,
24838
24566
  ...extractEventMetadata(event),
24567
+ ...event.api ? { "flue.api": event.api } : {},
24839
24568
  ...event.model ? { model: event.model, "flue.model": event.model } : {},
24569
+ ...event.provider ? { provider: event.provider } : {},
24570
+ ...event.provider ? { "flue.provider": event.provider } : {},
24571
+ ...event.purpose ? { "flue.turn_purpose": event.purpose } : {},
24840
24572
  ...event.stopReason ? { "flue.stop_reason": event.stopReason } : {},
24841
- ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {},
24842
- provider: "flue"
24573
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
24843
24574
  };
24844
24575
  safeLog3(state.span, {
24845
- ...event.error ? { error: errorToString(event.error) } : {},
24576
+ ...event.isError ? { error: errorToString(event.error) } : {},
24846
24577
  metadata,
24847
24578
  metrics: {
24848
- ...durationMsMetrics(event.durationMs),
24579
+ ...durationMetrics2(event.durationMs),
24849
24580
  ...metricsFromUsage(event.usage)
24850
24581
  },
24851
- output: toAssistantOutput(
24852
- text,
24853
- event.stopReason,
24854
- outputReasoning,
24855
- state.toolCalls
24856
- )
24582
+ output: event.output
24857
24583
  });
24858
- state.span.end();
24859
- this.turnsByScope.delete(scope);
24860
- }
24861
- handleThinkingDelta(event) {
24862
- const delta = event.delta;
24863
- if (typeof delta !== "string" || !delta) {
24864
- return;
24865
- }
24866
- const state = this.ensureTurnState(event);
24867
- state.hasThinking = true;
24868
- state.metadata["flue.thinking"] = true;
24869
- state.thinking.push(delta);
24870
- }
24871
- handleThinkingStart(event) {
24872
- const state = this.ensureTurnState(event);
24873
- state.hasThinking = true;
24874
- state.metadata["flue.thinking"] = true;
24875
- }
24876
- handleThinkingEnd(event) {
24877
- const state = this.ensureTurnState(event);
24878
- state.hasThinking = true;
24879
- state.metadata["flue.thinking"] = true;
24880
- if (typeof event.content === "string" && event.content) {
24881
- state.finalThinking = event.content;
24882
- }
24584
+ safeEnd(state.span, eventTime(event.timestamp));
24585
+ this.turnsByKey.delete(key);
24883
24586
  }
24884
- handleToolStart(event, options) {
24885
- const toolCallId = event.toolCallId;
24886
- if (!toolCallId) {
24587
+ handleToolStart(event) {
24588
+ if (!event.toolCallId) {
24887
24589
  return;
24888
24590
  }
24889
- const parent = this.parentSpanForEvent(event);
24890
- const scope = scopeKey(event);
24891
- let turnState = this.turnsByScope.get(scope);
24892
- if (!turnState && parent && options.captureTurnSpans) {
24893
- turnState = this.ensureTurnState(event);
24894
- }
24895
24591
  const metadata = {
24896
24592
  ...extractEventMetadata(event),
24897
24593
  ...event.toolName ? { "flue.tool_name": event.toolName } : {},
24898
- "flue.tool_call_id": toolCallId,
24594
+ "flue.tool_call_id": event.toolCallId,
24899
24595
  provider: "flue"
24900
24596
  };
24597
+ const parent = this.parentSpanForTool(event);
24901
24598
  const span = startFlueSpan(parent, {
24902
- name: `tool: ${event.toolName ?? "unknown"}`,
24903
- spanAttributes: { type: "tool" /* TOOL */ }
24904
- });
24905
- if (turnState) {
24906
- turnState.toolCalls.push({
24907
- args: event.args,
24908
- toolCallId,
24909
- toolName: event.toolName
24910
- });
24911
- }
24912
- safeLog3(span, {
24913
- input: event.args,
24914
- metadata
24915
- });
24916
- this.toolsById.set(toolKey(event), {
24917
- metadata,
24918
- span,
24919
- startTime: getCurrentUnixTimestamp()
24599
+ name: `tool:${event.toolName ?? "unknown"}`,
24600
+ spanAttributes: { type: "tool" /* TOOL */ },
24601
+ startTime: eventTime(event.timestamp),
24602
+ event: {
24603
+ input: event.args,
24604
+ metadata
24605
+ }
24920
24606
  });
24607
+ this.toolsByKey.set(toolKey(event), { metadata, span });
24921
24608
  }
24922
24609
  handleToolCall(event) {
24610
+ if (!event.toolCallId) {
24611
+ return;
24612
+ }
24923
24613
  const key = toolKey(event);
24924
- const state = this.toolsById.get(key) ?? this.startSyntheticToolState(event, event.toolName ?? "unknown");
24614
+ const state = this.toolsByKey.get(key) ?? this.startSyntheticTool(event);
24925
24615
  const metadata = {
24926
24616
  ...state.metadata,
24927
24617
  ...extractEventMetadata(event),
24928
24618
  ...event.toolName ? { "flue.tool_name": event.toolName } : {},
24929
- ...event.toolCallId ? { "flue.tool_call_id": event.toolCallId } : {},
24619
+ "flue.tool_call_id": event.toolCallId,
24930
24620
  ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
24931
24621
  };
24932
24622
  safeLog3(state.span, {
24933
24623
  ...event.isError ? { error: errorToString(event.result) } : {},
24934
24624
  metadata,
24935
- metrics: durationMsMetrics(event.durationMs),
24625
+ metrics: durationMetrics2(event.durationMs),
24936
24626
  output: event.result
24937
24627
  });
24938
- state.span.end();
24939
- this.toolsById.delete(key);
24628
+ safeEnd(state.span, eventTime(event.timestamp));
24629
+ this.toolsByKey.delete(key);
24940
24630
  }
24941
24631
  handleTaskStart(event) {
24942
- const parent = this.parentSpanForEvent(event);
24632
+ if (!event.taskId) {
24633
+ return;
24634
+ }
24943
24635
  const metadata = {
24944
24636
  ...extractEventMetadata(event),
24945
- ...event.role ? { "flue.role": event.role } : {},
24637
+ ...event.agent ? { "flue.agent": event.agent } : {},
24946
24638
  ...event.cwd ? { "flue.cwd": event.cwd } : {},
24947
24639
  "flue.task_id": event.taskId,
24948
24640
  provider: "flue"
24949
24641
  };
24642
+ const parent = this.parentSpanForEvent(event);
24950
24643
  const span = startFlueSpan(parent, {
24951
- name: "flue.task",
24952
- spanAttributes: { type: "task" /* TASK */ }
24953
- });
24954
- safeLog3(span, {
24955
- input: event.prompt,
24956
- metadata
24957
- });
24958
- this.tasksById.set(event.taskId, {
24959
- metadata,
24960
- span,
24961
- startTime: getCurrentUnixTimestamp()
24644
+ name: event.agent ? `task:${event.agent}` : "flue.task",
24645
+ spanAttributes: { type: "task" /* TASK */ },
24646
+ startTime: eventTime(event.timestamp),
24647
+ event: {
24648
+ input: event.prompt,
24649
+ metadata
24650
+ }
24962
24651
  });
24652
+ this.tasksById.set(event.taskId, { metadata, span });
24963
24653
  }
24964
24654
  handleTask(event) {
24965
24655
  const state = this.tasksById.get(event.taskId);
@@ -24971,426 +24661,372 @@ var FluePlugin = class extends BasePlugin {
24971
24661
  metadata: {
24972
24662
  ...state.metadata,
24973
24663
  ...extractEventMetadata(event),
24664
+ ...event.agent ? { "flue.agent": event.agent } : {},
24974
24665
  ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
24975
24666
  },
24976
- metrics: durationMsMetrics(event.durationMs),
24667
+ metrics: durationMetrics2(event.durationMs),
24977
24668
  output: event.result
24978
24669
  });
24979
- state.span.end();
24670
+ safeEnd(state.span, eventTime(event.timestamp));
24980
24671
  this.tasksById.delete(event.taskId);
24981
24672
  }
24982
24673
  handleCompactionStart(event) {
24983
- const operationState = this.operationStateForEvent(event);
24984
- const parent = operationState?.span ?? this.parentSpanForEvent(event);
24674
+ const key = compactionKey(event);
24675
+ const input = {
24676
+ ...event.estimatedTokens !== void 0 ? { estimatedTokens: event.estimatedTokens } : {},
24677
+ ...event.reason ? { reason: event.reason } : {}
24678
+ };
24985
24679
  const metadata = {
24986
24680
  ...extractEventMetadata(event),
24987
24681
  ...event.reason ? { "flue.compaction_reason": event.reason } : {},
24988
24682
  provider: "flue"
24989
24683
  };
24990
- const input = {
24991
- ...typeof event.estimatedTokens === "number" ? { estimatedTokens: event.estimatedTokens } : {},
24992
- ...event.reason ? { reason: event.reason } : {}
24993
- };
24684
+ const parent = this.parentSpanForEvent(event);
24994
24685
  const span = startFlueSpan(parent, {
24995
- name: "flue.compaction",
24996
- spanAttributes: { type: "task" /* TASK */ }
24997
- });
24998
- safeLog3(span, {
24999
- input,
25000
- metadata
25001
- });
25002
- this.compactionsByScope.set(scopeKey(event), {
25003
- input,
25004
- metadata,
25005
- operationState,
25006
- span,
25007
- startTime: getCurrentUnixTimestamp()
24686
+ name: `compaction:${event.reason ?? "unknown"}`,
24687
+ spanAttributes: { type: "task" /* TASK */ },
24688
+ startTime: eventTime(event.timestamp),
24689
+ event: {
24690
+ input,
24691
+ metadata
24692
+ }
25008
24693
  });
24694
+ this.logOperationInput(event.operationId, input);
24695
+ this.compactionsByKey.set(key, { metadata, span });
25009
24696
  }
25010
24697
  handleCompaction(event) {
25011
- const key = scopeKey(event);
25012
- const state = this.compactionsByScope.get(key) ?? this.findCompactionState(event);
25013
- if (!state) {
25014
- return;
25015
- }
24698
+ const key = compactionKey(event);
24699
+ const state = this.compactionsByKey.get(key) ?? this.startSyntheticCompaction(event);
24700
+ const metadata = {
24701
+ ...state.metadata,
24702
+ ...extractEventMetadata(event),
24703
+ ...event.usage ? { "flue.usage": event.usage } : {}
24704
+ };
25016
24705
  safeLog3(state.span, {
25017
- metadata: {
25018
- ...state.metadata,
25019
- ...extractEventMetadata(event),
25020
- ...typeof event.messagesBefore === "number" ? { "flue.messages_before": event.messagesBefore } : {},
25021
- ...typeof event.messagesAfter === "number" ? { "flue.messages_after": event.messagesAfter } : {}
25022
- },
24706
+ metadata,
25023
24707
  metrics: {
25024
- ...durationMsMetrics(event.durationMs),
25025
- ...metricsFromUsage(event.usage)
24708
+ ...durationMetrics2(event.durationMs),
24709
+ ...typeof event.messagesBefore === "number" ? { messages_before: event.messagesBefore } : {},
24710
+ ...typeof event.messagesAfter === "number" ? { messages_after: event.messagesAfter } : {}
25026
24711
  },
25027
24712
  output: {
25028
24713
  messagesAfter: event.messagesAfter,
25029
24714
  messagesBefore: event.messagesBefore
25030
24715
  }
25031
24716
  });
25032
- state.span.end();
25033
- this.deleteCompactionState(state);
24717
+ safeEnd(state.span, eventTime(event.timestamp));
24718
+ this.compactionsByKey.delete(key);
25034
24719
  }
25035
- findCompactionState(event) {
25036
- const operationState = this.operationStateForEvent(event);
25037
- for (const state of this.compactionsByScope.values()) {
25038
- if (operationState && state.operationState === operationState) {
25039
- return state;
24720
+ parentSpanForTurn(event) {
24721
+ if (event.purpose === "compaction" || event.purpose === "compaction_prefix") {
24722
+ const compaction = this.compactionsByKey.get(compactionKey(event));
24723
+ if (compaction) {
24724
+ return compaction.span;
25040
24725
  }
25041
24726
  }
25042
- return void 0;
24727
+ return this.parentSpanForEvent(event);
25043
24728
  }
25044
- finishCompactionsForOperation(operationState) {
25045
- for (const state of [...this.compactionsByScope.values()]) {
25046
- if (state.operationState !== operationState) {
25047
- continue;
24729
+ parentSpanForEvent(event) {
24730
+ const turn = turnKey(event);
24731
+ if (turn) {
24732
+ const turnState = this.turnsByKey.get(turn);
24733
+ if (turnState) {
24734
+ return turnState.span;
25048
24735
  }
25049
- safeLog3(state.span, {
25050
- input: state.input,
25051
- metadata: state.metadata,
25052
- metrics: {
25053
- ...buildDurationMetrics3(state.startTime)
25054
- },
25055
- output: { completed: true }
25056
- });
25057
- state.span.end();
25058
- this.deleteCompactionState(state);
25059
24736
  }
25060
- }
25061
- deleteCompactionState(state) {
25062
- for (const [key, candidate] of this.compactionsByScope) {
25063
- if (candidate !== state) {
25064
- continue;
24737
+ if (event.taskId) {
24738
+ const task = this.tasksById.get(event.taskId);
24739
+ if (task) {
24740
+ return task.span;
25065
24741
  }
25066
- this.compactionsByScope.delete(key);
25067
- return;
25068
24742
  }
25069
- }
25070
- startSyntheticToolState(event, toolName) {
25071
- const parent = this.parentSpanForEvent(event);
25072
- const metadata = {
25073
- ...extractEventMetadata(event),
25074
- ...event.toolCallId ? { "flue.tool_call_id": event.toolCallId } : {},
25075
- "flue.tool_name": toolName,
25076
- provider: "flue"
25077
- };
25078
- const span = startFlueSpan(parent, {
25079
- name: `tool: ${toolName}`,
25080
- spanAttributes: { type: "tool" /* TOOL */ }
25081
- });
25082
- safeLog3(span, { metadata });
25083
- return { metadata, span, startTime: getCurrentUnixTimestamp() };
25084
- }
25085
- operationStateForEvent(event) {
25086
24743
  if (event.operationId) {
25087
- const operation = this.activeOperationsById.get(event.operationId) ?? this.promotePendingOperationForEvent(event);
24744
+ const operation = this.operationsById.get(event.operationId);
25088
24745
  if (operation) {
25089
- return operation;
24746
+ return operation.span;
25090
24747
  }
25091
24748
  }
25092
- return this.activeOperationForEventScope(event) ?? this.pendingOperationForEventScope(event);
24749
+ if (event.runId) {
24750
+ return this.runsById.get(event.runId)?.span;
24751
+ }
24752
+ return void 0;
25093
24753
  }
25094
- parentSpanForEvent(event) {
24754
+ parentSpanForTool(event) {
24755
+ if (event.taskId) {
24756
+ const task = this.tasksById.get(event.taskId);
24757
+ if (task) {
24758
+ return task.span;
24759
+ }
24760
+ }
25095
24761
  if (event.operationId) {
25096
- const operation = this.operationStateForEvent(event);
24762
+ const operation = this.operationsById.get(event.operationId);
25097
24763
  if (operation) {
25098
24764
  return operation.span;
25099
24765
  }
25100
24766
  }
25101
- if (event.taskId) {
25102
- return this.tasksById.get(event.taskId)?.span;
24767
+ if (event.runId) {
24768
+ return this.runsById.get(event.runId)?.span;
25103
24769
  }
25104
- return this.operationStateForEvent(event)?.span;
24770
+ return void 0;
25105
24771
  }
25106
- promotePendingOperationForEvent(event) {
25107
- if (!event.operationId) {
25108
- return void 0;
24772
+ logOperationInput(operationId, input) {
24773
+ if (!operationId || input === void 0) {
24774
+ return;
24775
+ }
24776
+ const operation = this.operationsById.get(operationId);
24777
+ if (!operation || operation.loggedInput) {
24778
+ return;
24779
+ }
24780
+ safeLog3(operation.span, { input });
24781
+ operation.loggedInput = true;
24782
+ }
24783
+ startSyntheticOperation(event) {
24784
+ const metadata = {
24785
+ ...extractEventMetadata(event),
24786
+ "flue.operation": event.operationKind,
24787
+ provider: "flue"
24788
+ };
24789
+ const span = startFlueSpan(this.parentSpanForEvent(event), {
24790
+ name: `flue.${event.operationKind}`,
24791
+ spanAttributes: { type: "task" /* TASK */ },
24792
+ startTime: eventTime(event.timestamp),
24793
+ event: { metadata }
24794
+ });
24795
+ return { metadata, span };
24796
+ }
24797
+ startSyntheticTurn(event) {
24798
+ const metadata = {
24799
+ ...extractEventMetadata(event),
24800
+ ...event.api ? { "flue.api": event.api } : {},
24801
+ ...event.model ? { model: event.model, "flue.model": event.model } : {},
24802
+ ...event.provider ? { provider: event.provider } : { provider: "flue" },
24803
+ ...event.provider ? { "flue.provider": event.provider } : {},
24804
+ ...event.purpose ? { "flue.turn_purpose": event.purpose } : {}
24805
+ };
24806
+ const span = startFlueSpan(this.parentSpanForEvent(event), {
24807
+ name: `llm:${event.model ?? event.purpose ?? "unknown"}`,
24808
+ spanAttributes: { type: "llm" /* LLM */ },
24809
+ startTime: eventTime(event.timestamp),
24810
+ event: { metadata }
24811
+ });
24812
+ return { metadata, span };
24813
+ }
24814
+ startSyntheticTool(event) {
24815
+ const metadata = {
24816
+ ...extractEventMetadata(event),
24817
+ ...event.toolName ? { "flue.tool_name": event.toolName } : {},
24818
+ "flue.tool_call_id": event.toolCallId,
24819
+ provider: "flue"
24820
+ };
24821
+ const span = startFlueSpan(this.parentSpanForTool(event), {
24822
+ name: `tool:${event.toolName ?? "unknown"}`,
24823
+ spanAttributes: { type: "tool" /* TOOL */ },
24824
+ startTime: eventTime(event.timestamp),
24825
+ event: { metadata }
24826
+ });
24827
+ return { metadata, span };
24828
+ }
24829
+ startSyntheticCompaction(event) {
24830
+ const metadata = {
24831
+ ...extractEventMetadata(event),
24832
+ provider: "flue"
24833
+ };
24834
+ const span = startFlueSpan(this.parentSpanForEvent(event), {
24835
+ name: "compaction:unknown",
24836
+ spanAttributes: { type: "task" /* TASK */ },
24837
+ startTime: eventTime(event.timestamp),
24838
+ event: { metadata }
24839
+ });
24840
+ return { metadata, span };
24841
+ }
24842
+ finishPendingChildrenForOperation(event, operationOutput2) {
24843
+ const endTime = eventTime(event.timestamp);
24844
+ const usage = event.usage ?? usageFromOperationResult(event.result);
24845
+ const turnEntries = [...this.turnsByKey].filter(
24846
+ ([, state]) => stateMatchesOperation(state, event.operationId)
24847
+ );
24848
+ turnEntries.forEach(([key, state], index) => {
24849
+ const shouldLogOperationOutput = (event.operationKind === "prompt" || event.operationKind === "skill") && index === turnEntries.length - 1 && operationOutput2 !== void 0;
24850
+ safeLog3(state.span, {
24851
+ metadata: state.metadata,
24852
+ metrics: metricsFromUsage(usage),
24853
+ ...shouldLogOperationOutput ? { output: operationOutput2 } : {}
24854
+ });
24855
+ safeEnd(state.span, endTime);
24856
+ this.turnsByKey.delete(key);
24857
+ });
24858
+ for (const [key, state] of this.toolsByKey) {
24859
+ if (!stateMatchesOperation(state, event.operationId)) {
24860
+ continue;
24861
+ }
24862
+ safeEnd(state.span, endTime);
24863
+ this.toolsByKey.delete(key);
25109
24864
  }
25110
- const scopePrefixes = operationScopePrefixes(event);
25111
- for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
25112
- if (!candidateQueue.length || !operationKeyMatchesScopes(candidateKey, scopePrefixes)) {
24865
+ for (const [key, state] of this.tasksById) {
24866
+ if (!stateMatchesOperation(state, event.operationId)) {
25113
24867
  continue;
25114
24868
  }
25115
- const state = candidateQueue.shift();
25116
- if (!state) {
25117
- return void 0;
24869
+ safeEnd(state.span, endTime);
24870
+ this.tasksById.delete(key);
24871
+ }
24872
+ for (const [key, state] of this.compactionsByKey) {
24873
+ if (!stateMatchesOperation(state, event.operationId)) {
24874
+ continue;
25118
24875
  }
25119
- state.operationId = event.operationId;
25120
- this.activeOperationsById.set(event.operationId, state);
25121
- addScopedOperation(this.activeOperationsByScope, event, state);
25122
- state.metadata = {
25123
- ...state.metadata,
25124
- ...extractEventMetadata(event),
25125
- "flue.operation_id": event.operationId
25126
- };
25127
- safeLog3(state.span, { metadata: state.metadata });
25128
- return state;
24876
+ safeLog3(state.span, {
24877
+ metadata: state.metadata,
24878
+ metrics: durationMetrics2(event.durationMs),
24879
+ output: { completed: true }
24880
+ });
24881
+ safeEnd(state.span, eventTime(event.timestamp));
24882
+ this.compactionsByKey.delete(key);
25129
24883
  }
25130
- return void 0;
25131
24884
  }
25132
- activeOperationForEventScope(event) {
25133
- for (const scope of operationScopeNames(event)) {
25134
- const operations = this.activeOperationsByScope.get(scope);
25135
- if (operations?.length) {
25136
- return operations[operations.length - 1];
24885
+ finishPendingSpansForRun(event) {
24886
+ const endTime = eventTime(event.timestamp);
24887
+ for (const [key, state] of this.toolsByKey) {
24888
+ if (!stateMatchesRun(state, event.runId)) {
24889
+ continue;
25137
24890
  }
24891
+ safeEnd(state.span, endTime);
24892
+ this.toolsByKey.delete(key);
25138
24893
  }
25139
- return void 0;
25140
- }
25141
- pendingOperationForEventScope(event) {
25142
- const scopePrefixes = operationScopePrefixes(event);
25143
- for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
25144
- if (!candidateQueue.length || !operationKeyMatchesScopes(candidateKey, scopePrefixes)) {
24894
+ for (const [key, state] of this.turnsByKey) {
24895
+ if (!stateMatchesRun(state, event.runId)) {
25145
24896
  continue;
25146
24897
  }
25147
- return candidateQueue[0];
24898
+ safeEnd(state.span, endTime);
24899
+ this.turnsByKey.delete(key);
25148
24900
  }
25149
- return void 0;
25150
- }
25151
- takePendingOperationForEvent(event) {
25152
- const key = operationKey(event.session, event.operationKind);
25153
- const queue2 = this.pendingOperationsByKey.get(key);
25154
- if (queue2?.length) {
25155
- return queue2.shift();
24901
+ for (const [key, state] of this.tasksById) {
24902
+ if (!stateMatchesRun(state, event.runId)) {
24903
+ continue;
24904
+ }
24905
+ safeEnd(state.span, endTime);
24906
+ this.tasksById.delete(key);
25156
24907
  }
25157
- for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
25158
- if (candidateKey.endsWith(`::${event.operationKind}`) && candidateQueue.length) {
25159
- return candidateQueue.shift();
24908
+ for (const [key, state] of this.compactionsByKey) {
24909
+ if (!stateMatchesRun(state, event.runId)) {
24910
+ continue;
25160
24911
  }
24912
+ safeLog3(state.span, {
24913
+ metadata: state.metadata,
24914
+ output: { completed: true }
24915
+ });
24916
+ safeEnd(state.span, endTime);
24917
+ this.compactionsByKey.delete(key);
25161
24918
  }
25162
- return void 0;
25163
- }
25164
- pendingOperationQueue(key) {
25165
- const existing = this.pendingOperationsByKey.get(key);
25166
- if (existing) {
25167
- return existing;
24919
+ for (const [key, state] of this.operationsById) {
24920
+ if (!stateMatchesRun(state, event.runId)) {
24921
+ continue;
24922
+ }
24923
+ safeLog3(state.span, {
24924
+ metadata: state.metadata,
24925
+ ...state.metadata["flue.operation"] === "compact" ? { output: { completed: true } } : {}
24926
+ });
24927
+ safeEnd(state.span, endTime);
24928
+ this.operationsById.delete(key);
25168
24929
  }
25169
- const queue2 = [];
25170
- this.pendingOperationsByKey.set(key, queue2);
25171
- return queue2;
25172
24930
  }
25173
24931
  };
25174
24932
  function isInstrumentedOperation(operation) {
25175
- return operation === "prompt" || operation === "skill" || operation === "task" || operation === "compact";
25176
- }
25177
- function getSessionName(session) {
25178
- return typeof session?.name === "string" ? session.name : void 0;
25179
- }
25180
- function operationKey(sessionName, operation) {
25181
- return `${sessionName ?? "unknown"}::${operation}`;
25182
- }
25183
- function operationScopePrefixes(event) {
25184
- const scopes = /* @__PURE__ */ new Set();
25185
- for (const scope of operationScopeNames(event)) {
25186
- scopes.add(`${scope}::`);
25187
- }
25188
- return scopes;
25189
- }
25190
- function operationKeyMatchesScopes(key, scopes) {
25191
- for (const scope of scopes) {
25192
- if (key.startsWith(scope)) {
25193
- return true;
25194
- }
25195
- }
25196
- return false;
24933
+ return operation === "prompt" || operation === "skill" || operation === "compact";
25197
24934
  }
25198
- function operationScopeNames(event) {
25199
- const scopes = /* @__PURE__ */ new Set();
25200
- if (event.session) {
25201
- scopes.add(event.session);
25202
- }
25203
- if (event.parentSession) {
25204
- scopes.add(event.parentSession);
25205
- }
25206
- if (!scopes.size) {
25207
- scopes.add("unknown");
25208
- }
25209
- return scopes;
25210
- }
25211
- function addScopedOperation(operationsByScope, event, state) {
25212
- for (const scope of operationScopeNames(event)) {
25213
- addOperationToScope(operationsByScope, scope, state);
25214
- }
25215
- }
25216
- function addOperationToScope(operationsByScope, scope, state) {
25217
- const operations = operationsByScope.get(scope);
25218
- if (operations) {
25219
- if (!operations.includes(state)) {
25220
- operations.push(state);
25221
- }
25222
- } else {
25223
- operationsByScope.set(scope, [state]);
25224
- }
25225
- }
25226
- function removeScopedOperation(operationsByScope, state) {
25227
- for (const [scope, operations] of operationsByScope) {
25228
- const index = operations.indexOf(state);
25229
- if (index === -1) {
25230
- continue;
25231
- }
25232
- operations.splice(index, 1);
25233
- if (operations.length === 0) {
25234
- operationsByScope.delete(scope);
25235
- }
25236
- }
25237
- }
25238
- function removePendingOperation(pendingOperationsByKey, state) {
25239
- for (const [key, queue2] of pendingOperationsByKey) {
25240
- const index = queue2.indexOf(state);
25241
- if (index === -1) {
25242
- continue;
25243
- }
25244
- queue2.splice(index, 1);
25245
- if (queue2.length === 0) {
25246
- pendingOperationsByKey.delete(key);
25247
- }
25248
- return;
25249
- }
25250
- }
25251
- function extractSessionMetadata(session) {
25252
- const sessionName = getSessionName(session);
25253
- return sessionName ? { "flue.session": sessionName } : {};
25254
- }
25255
- function extractEventMetadata(event) {
24935
+ function extractEventMetadata(event, ctx) {
25256
24936
  return {
25257
24937
  ...event.runId ? { "flue.run_id": event.runId } : {},
24938
+ ...event.instanceId ? { "flue.instance_id": event.instanceId } : {},
24939
+ ...event.dispatchId ? { "flue.dispatch_id": event.dispatchId } : {},
25258
24940
  ...typeof event.eventIndex === "number" ? { "flue.event_index": event.eventIndex } : {},
25259
24941
  ...event.session ? { "flue.session": event.session } : {},
25260
24942
  ...event.parentSession ? { "flue.parent_session": event.parentSession } : {},
25261
24943
  ...event.harness ? { "flue.harness": event.harness } : {},
25262
24944
  ...event.taskId ? { "flue.task_id": event.taskId } : {},
25263
- ...event.operationId ? { "flue.operation_id": event.operationId } : {}
24945
+ ...event.operationId ? { "flue.operation_id": event.operationId } : {},
24946
+ ...event.turnId ? { "flue.turn_id": event.turnId } : {},
24947
+ ...typeof ctx?.id === "string" ? { "flue.context_id": ctx.id } : {},
24948
+ ...typeof ctx?.runId === "string" ? { "flue.context_run_id": ctx.runId } : {}
25264
24949
  };
25265
24950
  }
25266
- function extractOperationInput(operation, args) {
25267
- switch (operation) {
25268
- case "prompt":
25269
- case "task":
25270
- return args[0];
25271
- case "skill":
25272
- return {
25273
- args: getOptionObject(args[1])?.args,
25274
- name: args[0]
25275
- };
25276
- case "compact":
25277
- return void 0;
24951
+ function extractPayloadMetadata(payload) {
24952
+ if (!isObjectLike(payload)) {
24953
+ return {};
25278
24954
  }
24955
+ const metadata = Reflect.get(payload, "metadata");
24956
+ if (!isObjectLike(metadata)) {
24957
+ return {};
24958
+ }
24959
+ return Object.fromEntries(Object.entries(metadata));
25279
24960
  }
25280
- function extractOperationInputMetadata(operation, args) {
25281
- const options = getOptionObject(args[1]);
25282
- return {
25283
- ...operation === "skill" && typeof args[0] === "string" ? { "flue.skill_name": args[0] } : {},
25284
- ...options?.model ? { model: options.model, "flue.model": options.model } : {},
25285
- ...options?.role ? { "flue.role": options.role } : {},
25286
- ...options?.thinkingLevel ? { "flue.thinking_level": options.thinkingLevel } : {},
25287
- ...typeof options?.cwd === "string" ? { "flue.cwd": options.cwd } : {},
25288
- ...Array.isArray(options?.tools) ? {
25289
- "flue.tools_count": options.tools.length,
25290
- tools: summarizeTools(options.tools)
25291
- } : {},
25292
- ...Array.isArray(options?.images) ? { "flue.images_count": options.images.length } : {},
25293
- ...options?.result || options?.schema ? { "flue.result_schema": true } : {}
25294
- };
25295
- }
25296
- function getOptionObject(value) {
25297
- return isObject(value) ? value : void 0;
25298
- }
25299
- function summarizeTools(tools) {
25300
- return tools.flatMap((tool) => {
25301
- if (!isObject(tool)) {
25302
- return [];
25303
- }
25304
- const name = typeof tool.name === "string" ? tool.name : void 0;
25305
- if (!name) {
25306
- return [];
25307
- }
25308
- return [
25309
- {
25310
- function: {
25311
- description: typeof tool.description === "string" ? tool.description : void 0,
25312
- name,
25313
- parameters: tool.parameters
25314
- },
25315
- type: "function"
25316
- }
25317
- ];
25318
- });
24961
+ function operationOutput(event) {
24962
+ if (event.operationKind === "prompt" || event.operationKind === "skill") {
24963
+ return llmResultFromOperationResult(event.result);
24964
+ }
24965
+ return event.result ?? (event.operationKind === "compact" ? { completed: true } : void 0);
25319
24966
  }
25320
- function extractPromptResponseMetadata(result) {
25321
- const modelId = result?.model && typeof result.model.id === "string" ? result.model.id : void 0;
25322
- return modelId ? {
25323
- model: modelId,
25324
- "flue.model": modelId
25325
- } : {};
24967
+ function llmResultFromOperationResult(result) {
24968
+ if (!isObjectLike(result)) {
24969
+ return result;
24970
+ }
24971
+ const text = Reflect.get(result, "text");
24972
+ return text === void 0 ? result : text;
25326
24973
  }
25327
- function extractOperationOutput(result) {
25328
- if (!result) {
24974
+ function usageFromOperationResult(result) {
24975
+ if (!isObjectLike(result)) {
25329
24976
  return void 0;
25330
24977
  }
25331
- if ("data" in result) {
25332
- return result.data;
25333
- }
25334
- if ("text" in result) {
25335
- return result.text;
25336
- }
25337
- return result;
24978
+ return Reflect.get(result, "usage");
25338
24979
  }
25339
24980
  function metricsFromUsage(usage) {
24981
+ if (!isObjectLike(usage)) {
24982
+ return {};
24983
+ }
24984
+ const cacheRead = Reflect.get(usage, "cacheRead");
24985
+ const cacheWrite = Reflect.get(usage, "cacheWrite");
24986
+ const cost = Reflect.get(usage, "cost");
24987
+ const input = Reflect.get(usage, "input");
24988
+ const output = Reflect.get(usage, "output");
24989
+ const totalTokens = Reflect.get(usage, "totalTokens");
24990
+ const totalCost = isObjectLike(cost) ? Reflect.get(cost, "total") : void 0;
25340
24991
  return {
25341
- ...typeof usage?.input === "number" ? { prompt_tokens: usage.input } : {},
25342
- ...typeof usage?.output === "number" ? { completion_tokens: usage.output } : {},
25343
- ...typeof usage?.cacheRead === "number" ? { prompt_cached_tokens: usage.cacheRead } : {},
25344
- ...typeof usage?.cacheWrite === "number" ? { prompt_cache_creation_tokens: usage.cacheWrite } : {},
25345
- ...typeof usage?.totalTokens === "number" ? { tokens: usage.totalTokens } : {},
25346
- ...typeof usage?.cost?.total === "number" ? { estimated_cost: usage.cost.total } : {}
25347
- };
25348
- }
25349
- function buildDurationMetrics3(startTime) {
25350
- return {
25351
- duration_ms: Math.max(0, (getCurrentUnixTimestamp() - startTime) * 1e3)
24992
+ ...typeof input === "number" ? { prompt_tokens: input } : {},
24993
+ ...typeof output === "number" ? { completion_tokens: output } : {},
24994
+ ...typeof cacheRead === "number" ? { prompt_cached_tokens: cacheRead } : {},
24995
+ ...typeof cacheWrite === "number" ? { prompt_cache_creation_tokens: cacheWrite } : {},
24996
+ ...typeof totalTokens === "number" ? { tokens: totalTokens } : {},
24997
+ ...typeof totalCost === "number" ? { estimated_cost: totalCost } : {}
25352
24998
  };
25353
24999
  }
25354
- function durationMsMetrics(durationMs) {
25000
+ function durationMetrics2(durationMs) {
25355
25001
  return typeof durationMs === "number" ? { duration_ms: durationMs } : {};
25356
25002
  }
25357
- function scopeKey(event) {
25358
- if (event.operationId) {
25359
- return `operation:${event.operationId}`;
25360
- }
25361
- if (event.taskId) {
25362
- return `task:${event.taskId}`;
25363
- }
25364
- if (event.session) {
25365
- return `session:${event.session}`;
25003
+ function eventTime(value) {
25004
+ if (typeof value !== "string") {
25005
+ return void 0;
25366
25006
  }
25367
- return "flue:unknown";
25007
+ const timestamp = Date.parse(value);
25008
+ return Number.isFinite(timestamp) ? timestamp / 1e3 : void 0;
25009
+ }
25010
+ function turnKey(event) {
25011
+ return event.turnId;
25368
25012
  }
25369
25013
  function toolKey(event) {
25370
- return `${scopeKey(event)}::tool:${event.toolCallId ?? "unknown"}`;
25014
+ return `${event.turnId ?? event.operationId ?? event.taskId ?? event.runId ?? "unknown"}:${event.toolCallId ?? "unknown"}`;
25371
25015
  }
25372
- function toAssistantOutput(text, finishReason, reasoning, toolCalls) {
25016
+ function compactionKey(event) {
25373
25017
  return [
25374
- {
25375
- finish_reason: finishReason ?? "stop",
25376
- index: 0,
25377
- message: {
25378
- content: text,
25379
- ...reasoning ? { reasoning } : {},
25380
- role: "assistant",
25381
- ...toolCalls?.length ? {
25382
- tool_calls: toolCalls.map((toolCall) => ({
25383
- function: {
25384
- arguments: toolCall.args === void 0 ? "{}" : JSON.stringify(toolCall.args),
25385
- name: toolCall.toolName ?? "unknown"
25386
- },
25387
- ...toolCall.toolCallId ? { id: toolCall.toolCallId } : {},
25388
- type: "function"
25389
- }))
25390
- } : {}
25391
- }
25392
- }
25393
- ];
25018
+ event.instanceId ?? "",
25019
+ event.runId ?? "",
25020
+ event.session ?? "",
25021
+ event.operationId ?? "",
25022
+ event.taskId ?? ""
25023
+ ].join(":");
25024
+ }
25025
+ function stateMatchesOperation(state, operationId) {
25026
+ return state.metadata["flue.operation_id"] === operationId;
25027
+ }
25028
+ function stateMatchesRun(state, runId) {
25029
+ return state.metadata["flue.run_id"] === runId;
25394
25030
  }
25395
25031
  function startFlueSpan(parent, args) {
25396
25032
  return parent ? withCurrent(parent, () => startSpan(args)) : startSpan(args);
@@ -25402,6 +25038,13 @@ function safeLog3(span, event) {
25402
25038
  logInstrumentationError3("Flue span log", error);
25403
25039
  }
25404
25040
  }
25041
+ function safeEnd(span, endTime) {
25042
+ try {
25043
+ span.end(endTime === void 0 ? void 0 : { endTime });
25044
+ } catch (error) {
25045
+ logInstrumentationError3("Flue span end", error);
25046
+ }
25047
+ }
25405
25048
  function errorToString(error) {
25406
25049
  if (error instanceof Error) {
25407
25050
  return error.message;
@@ -25895,7 +25538,7 @@ var BraintrustPlugin = class extends BasePlugin {
25895
25538
  this.config = config;
25896
25539
  }
25897
25540
  onEnable() {
25898
- const integrations = this.config.integrations || {};
25541
+ const integrations = this.config.integrations ?? {};
25899
25542
  if (integrations.openai !== false) {
25900
25543
  this.openaiPlugin = new OpenAIPlugin();
25901
25544
  this.openaiPlugin.enable();
@@ -25960,7 +25603,7 @@ var BraintrustPlugin = class extends BasePlugin {
25960
25603
  this.genkitPlugin = new GenkitPlugin();
25961
25604
  this.genkitPlugin.enable();
25962
25605
  }
25963
- if (getIntegrationConfig(integrations, "gitHubCopilot") !== false) {
25606
+ if (integrations.gitHubCopilot !== false) {
25964
25607
  this.gitHubCopilotPlugin = new GitHubCopilotPlugin();
25965
25608
  this.gitHubCopilotPlugin.enable();
25966
25609
  }
@@ -26073,6 +25716,7 @@ var envIntegrationAliases = {
26073
25716
  cursorsdk: "cursorSDK",
26074
25717
  flue: "flue",
26075
25718
  "flue-runtime": "flue",
25719
+ mastra: "mastra",
26076
25720
  "openai-agents": "openAIAgents",
26077
25721
  openaiagents: "openAIAgents",
26078
25722
  "openai-agents-core": "openAIAgents",
@@ -26115,6 +25759,7 @@ function getDefaultInstrumentationIntegrations() {
26115
25759
  cursor: true,
26116
25760
  cursorSDK: true,
26117
25761
  flue: true,
25762
+ mastra: true,
26118
25763
  openAIAgents: true,
26119
25764
  openrouter: true,
26120
25765
  openrouterAgent: true,
@@ -26287,6 +25932,7 @@ __export(exports_exports, {
26287
25932
  BaseExperiment: () => BaseExperiment,
26288
25933
  BraintrustLangChainCallbackHandler: () => BraintrustLangChainCallbackHandler,
26289
25934
  BraintrustMiddleware: () => BraintrustMiddleware,
25935
+ BraintrustObservabilityExporter: () => BraintrustObservabilityExporter,
26290
25936
  BraintrustState: () => BraintrustState,
26291
25937
  BraintrustStream: () => BraintrustStream,
26292
25938
  CachedSpanFetcher: () => CachedSpanFetcher,
@@ -26333,6 +25979,7 @@ __export(exports_exports, {
26333
25979
  _internalIso: () => isomorph_default,
26334
25980
  _internalSetInitialState: () => _internalSetInitialState,
26335
25981
  addAzureBlobHeaders: () => addAzureBlobHeaders,
25982
+ braintrustFlueObserver: () => braintrustFlueObserver,
26336
25983
  braintrustStreamChunkSchema: () => braintrustStreamChunkSchema,
26337
25984
  buildLocalSummary: () => buildLocalSummary,
26338
25985
  configureInstrumentation: () => configureInstrumentation,
@@ -26412,8 +26059,6 @@ __export(exports_exports, {
26412
26059
  wrapCohere: () => wrapCohere,
26413
26060
  wrapCopilotClient: () => wrapCopilotClient,
26414
26061
  wrapCursorSDK: () => wrapCursorSDK,
26415
- wrapFlueContext: () => wrapFlueContext,
26416
- wrapFlueSession: () => wrapFlueSession,
26417
26062
  wrapGenkit: () => wrapGenkit,
26418
26063
  wrapGoogleADK: () => wrapGoogleADK,
26419
26064
  wrapGoogleGenAI: () => wrapGoogleGenAI,
@@ -27903,6 +27548,196 @@ function toolRunnerProxy(toolRunner, anthropic, channel2) {
27903
27548
  }
27904
27549
 
27905
27550
  // src/wrappers/mastra.ts
27551
+ var MASTRA_BRAINTRUST_EXPORTER_NAME = "braintrust";
27552
+ var SPAN_TYPE_MAP = {
27553
+ agent_run: "task" /* TASK */,
27554
+ model_generation: "llm" /* LLM */,
27555
+ model_step: "llm" /* LLM */,
27556
+ model_chunk: "llm" /* LLM */,
27557
+ tool_call: "tool" /* TOOL */,
27558
+ mcp_tool_call: "tool" /* TOOL */,
27559
+ workflow_run: "task" /* TASK */,
27560
+ workflow_step: "function" /* FUNCTION */,
27561
+ workflow_conditional: "function" /* FUNCTION */,
27562
+ workflow_conditional_eval: "function" /* FUNCTION */,
27563
+ workflow_parallel: "function" /* FUNCTION */,
27564
+ workflow_loop: "function" /* FUNCTION */,
27565
+ workflow_sleep: "function" /* FUNCTION */,
27566
+ workflow_wait_event: "function" /* FUNCTION */,
27567
+ memory_operation: "function" /* FUNCTION */,
27568
+ workspace_action: "function" /* FUNCTION */,
27569
+ rag_ingestion: "task" /* TASK */,
27570
+ rag_embedding: "llm" /* LLM */,
27571
+ rag_vector_operation: "function" /* FUNCTION */,
27572
+ rag_action: "function" /* FUNCTION */,
27573
+ graph_action: "function" /* FUNCTION */,
27574
+ scorer_run: "score" /* SCORE */,
27575
+ scorer_step: "score" /* SCORE */,
27576
+ processor_run: "function" /* FUNCTION */,
27577
+ generic: "function" /* FUNCTION */
27578
+ };
27579
+ function spanTypeFor(mastraType) {
27580
+ return SPAN_TYPE_MAP[mastraType] ?? "function" /* FUNCTION */;
27581
+ }
27582
+ function epochSeconds(value) {
27583
+ if (value === void 0) return void 0;
27584
+ const ms = value instanceof Date ? value.getTime() : typeof value === "number" ? value : Date.parse(value);
27585
+ return Number.isFinite(ms) ? ms / 1e3 : void 0;
27586
+ }
27587
+ function modelMetrics(attributes) {
27588
+ if (!isObject(attributes)) return void 0;
27589
+ const usage = isObject(attributes.usage) ? attributes.usage : void 0;
27590
+ if (!usage) return void 0;
27591
+ const out = {};
27592
+ if (typeof usage.inputTokens === "number")
27593
+ out.prompt_tokens = usage.inputTokens;
27594
+ if (typeof usage.outputTokens === "number")
27595
+ out.completion_tokens = usage.outputTokens;
27596
+ if (typeof usage.inputTokens === "number" && typeof usage.outputTokens === "number") {
27597
+ out.tokens = usage.inputTokens + usage.outputTokens;
27598
+ }
27599
+ const inputDetails = isObject(usage.inputDetails) ? usage.inputDetails : void 0;
27600
+ const outputDetails = isObject(usage.outputDetails) ? usage.outputDetails : void 0;
27601
+ if (inputDetails && typeof inputDetails.cacheRead === "number") {
27602
+ out.prompt_cached_tokens = inputDetails.cacheRead;
27603
+ }
27604
+ if (inputDetails && typeof inputDetails.cacheWrite === "number") {
27605
+ out.prompt_cache_creation_tokens = inputDetails.cacheWrite;
27606
+ }
27607
+ if (outputDetails && typeof outputDetails.reasoning === "number") {
27608
+ out.completion_reasoning_tokens = outputDetails.reasoning;
27609
+ }
27610
+ return Object.keys(out).length > 0 ? out : void 0;
27611
+ }
27612
+ function buildMetadata(exported) {
27613
+ const out = {};
27614
+ if (exported.entityId !== void 0) out.entity_id = exported.entityId;
27615
+ if (exported.entityName !== void 0) out.entity_name = exported.entityName;
27616
+ if (exported.entityType !== void 0) out.entity_type = exported.entityType;
27617
+ if (exported.metadata && isObject(exported.metadata)) {
27618
+ Object.assign(out, exported.metadata);
27619
+ }
27620
+ if (exported.attributes && isObject(exported.attributes)) {
27621
+ for (const [key, value] of Object.entries(exported.attributes)) {
27622
+ if (key === "usage") continue;
27623
+ if (value !== void 0) out[key] = value;
27624
+ }
27625
+ }
27626
+ if (exported.tags && exported.tags.length > 0) {
27627
+ out.tags = exported.tags;
27628
+ }
27629
+ if (exported.requestContext && isObject(exported.requestContext)) {
27630
+ out.request_context = exported.requestContext;
27631
+ }
27632
+ return out;
27633
+ }
27634
+ var BraintrustObservabilityExporter = class {
27635
+ name = MASTRA_BRAINTRUST_EXPORTER_NAME;
27636
+ spans = /* @__PURE__ */ new Map();
27637
+ // Captured at the first SPAN_STARTED event. Mastra's observability bus may
27638
+ // dispatch later events outside the user's AsyncLocalStorage context, where
27639
+ // `currentSpan()` returns NOOP_SPAN — which would make our `startSpan()`
27640
+ // calls go to a no-op logger and silently drop. Anchoring on the parent
27641
+ // we observe while still in-context keeps the whole Mastra subtree under
27642
+ // the user's traced scenario.
27643
+ capturedParent;
27644
+ constructor() {
27645
+ _internalSetInitialState();
27646
+ }
27647
+ async exportTracingEvent(event) {
27648
+ const exported = event.exportedSpan;
27649
+ if (exported.isInternal === true) return;
27650
+ try {
27651
+ switch (event.type) {
27652
+ case "span_started":
27653
+ this.onStart(exported);
27654
+ break;
27655
+ case "span_updated":
27656
+ this.onUpdate(exported);
27657
+ break;
27658
+ case "span_ended":
27659
+ this.onEnd(exported);
27660
+ break;
27661
+ }
27662
+ } catch (err) {
27663
+ logExporterError(err);
27664
+ }
27665
+ }
27666
+ async flush() {
27667
+ const state = _internalGetGlobalState();
27668
+ if (state) {
27669
+ await state.bgLogger().flush();
27670
+ }
27671
+ }
27672
+ async shutdown() {
27673
+ await this.flush();
27674
+ this.spans.clear();
27675
+ }
27676
+ onStart(exported) {
27677
+ if (this.spans.has(exported.id)) return;
27678
+ const args = {
27679
+ name: exported.name,
27680
+ spanAttributes: { type: spanTypeFor(exported.type) },
27681
+ startTime: epochSeconds(exported.startTime)
27682
+ };
27683
+ const parentRecord = exported.parentSpanId ? this.spans.get(exported.parentSpanId) : void 0;
27684
+ if (!this.capturedParent) {
27685
+ const probe = currentSpan();
27686
+ if (probe && probe.spanId) {
27687
+ this.capturedParent = probe;
27688
+ }
27689
+ }
27690
+ const span = parentRecord ? parentRecord.span.startSpan(args) : this.capturedParent ? this.capturedParent.startSpan(args) : startSpan(args);
27691
+ const record = { span, hasLoggedInput: false };
27692
+ this.logPayload(record, exported);
27693
+ this.spans.set(exported.id, record);
27694
+ if (exported.isEvent === true) {
27695
+ span.end({ endTime: args.startTime });
27696
+ this.spans.delete(exported.id);
27697
+ }
27698
+ }
27699
+ onUpdate(exported) {
27700
+ const record = this.spans.get(exported.id);
27701
+ if (!record) return;
27702
+ this.logPayload(record, exported);
27703
+ }
27704
+ onEnd(exported) {
27705
+ const record = this.spans.get(exported.id);
27706
+ if (!record) return;
27707
+ this.logPayload(record, exported);
27708
+ if (exported.errorInfo) {
27709
+ record.span.log({
27710
+ error: exported.errorInfo.message || exported.errorInfo.name || "Unknown Mastra error"
27711
+ });
27712
+ }
27713
+ record.span.end({ endTime: epochSeconds(exported.endTime) });
27714
+ this.spans.delete(exported.id);
27715
+ }
27716
+ logPayload(record, exported) {
27717
+ const event = {};
27718
+ if (exported.input !== void 0) {
27719
+ event.input = exported.input;
27720
+ record.hasLoggedInput = true;
27721
+ }
27722
+ if (exported.output !== void 0) {
27723
+ event.output = exported.output;
27724
+ }
27725
+ const metadata = buildMetadata(exported);
27726
+ if (Object.keys(metadata).length > 0) {
27727
+ event.metadata = metadata;
27728
+ }
27729
+ const metrics = modelMetrics(exported.attributes);
27730
+ if (metrics) {
27731
+ event.metrics = metrics;
27732
+ }
27733
+ if (Object.keys(event).length > 0) {
27734
+ record.span.log(event);
27735
+ }
27736
+ }
27737
+ };
27738
+ function logExporterError(err) {
27739
+ debugLogger.warn("Mastra exporter failure:", err);
27740
+ }
27906
27741
  function wrapMastraAgent(agent, _options) {
27907
27742
  return agent;
27908
27743
  }