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