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
@@ -3935,6 +3935,76 @@ var LRUCache = class {
3935
3935
  }
3936
3936
  };
3937
3937
 
3938
+ // src/prompt-cache/cache-config.ts
3939
+ var CACHE_LOCATION_ENV_VAR = "BRAINTRUST_CACHE_LOCATION";
3940
+ var DEFAULT_CACHE_MEMORY_MAX = 1 << 10;
3941
+ var DEFAULT_CACHE_DISK_MAX = 1 << 20;
3942
+ var warnedInvalidCacheModeEnvValue = false;
3943
+ var warnedUnavailableDiskCacheMode = false;
3944
+ function warnInvalidCacheMode(value) {
3945
+ if (warnedInvalidCacheModeEnvValue) {
3946
+ return;
3947
+ }
3948
+ warnedInvalidCacheModeEnvValue = true;
3949
+ debugLogger.warn(
3950
+ `Invalid ${CACHE_LOCATION_ENV_VAR} value "${value}". Expected "mixed", "memory", "disk", or "none". Falling back to "mixed".`
3951
+ );
3952
+ }
3953
+ function warnUnavailableDiskCache() {
3954
+ if (warnedUnavailableDiskCacheMode) {
3955
+ return;
3956
+ }
3957
+ warnedUnavailableDiskCacheMode = true;
3958
+ debugLogger.warn(
3959
+ `Disk cache is not supported on this platform, so ${CACHE_LOCATION_ENV_VAR}="disk" disables prompt and parameters caching.`
3960
+ );
3961
+ }
3962
+ function parseCacheMode() {
3963
+ const value = isomorph_default.getEnv(CACHE_LOCATION_ENV_VAR);
3964
+ const normalized = value?.trim().toLowerCase();
3965
+ if (!normalized) {
3966
+ return "mixed";
3967
+ }
3968
+ if (normalized === "mixed" || normalized === "memory" || normalized === "disk" || normalized === "none") {
3969
+ return normalized;
3970
+ }
3971
+ warnInvalidCacheMode(value ?? "");
3972
+ return "mixed";
3973
+ }
3974
+ function parsePositiveIntegerEnv(envVar, defaultValue) {
3975
+ const value = Number(isomorph_default.getEnv(envVar));
3976
+ return Number.isInteger(value) && value > 0 ? value : defaultValue;
3977
+ }
3978
+ function createCacheLayers({
3979
+ memoryMaxEnvVar,
3980
+ diskCacheDirEnvVar,
3981
+ diskMaxEnvVar,
3982
+ getDefaultDiskCacheDir
3983
+ }) {
3984
+ const mode = parseCacheMode();
3985
+ const memoryCache = mode === "mixed" || mode === "memory" ? new LRUCache({
3986
+ max: parsePositiveIntegerEnv(
3987
+ memoryMaxEnvVar,
3988
+ DEFAULT_CACHE_MEMORY_MAX
3989
+ )
3990
+ }) : void 0;
3991
+ let diskCache;
3992
+ if (mode === "mixed" || mode === "disk") {
3993
+ if (canUseDiskCache()) {
3994
+ diskCache = new DiskCache({
3995
+ cacheDir: isomorph_default.getEnv(diskCacheDirEnvVar) ?? getDefaultDiskCacheDir(),
3996
+ max: parsePositiveIntegerEnv(diskMaxEnvVar, DEFAULT_CACHE_DISK_MAX)
3997
+ });
3998
+ } else if (mode === "disk") {
3999
+ warnUnavailableDiskCache();
4000
+ }
4001
+ }
4002
+ if (diskCache) {
4003
+ return { memoryCache, diskCache };
4004
+ }
4005
+ return { memoryCache };
4006
+ }
4007
+
3938
4008
  // src/prompt-cache/prompt-cache.ts
3939
4009
  function createCacheKey(key) {
3940
4010
  if (key.id) {
@@ -3962,16 +4032,18 @@ var PromptCache = class {
3962
4032
  */
3963
4033
  async get(key) {
3964
4034
  const cacheKey = createCacheKey(key);
3965
- const memoryPrompt = this.memoryCache.get(cacheKey);
3966
- if (memoryPrompt !== void 0) {
3967
- return memoryPrompt;
4035
+ if (this.memoryCache) {
4036
+ const memoryPrompt = this.memoryCache.get(cacheKey);
4037
+ if (memoryPrompt !== void 0) {
4038
+ return memoryPrompt;
4039
+ }
3968
4040
  }
3969
4041
  if (this.diskCache) {
3970
4042
  const diskPrompt = await this.diskCache.get(cacheKey);
3971
4043
  if (!diskPrompt) {
3972
4044
  return void 0;
3973
4045
  }
3974
- this.memoryCache.set(cacheKey, diskPrompt);
4046
+ this.memoryCache?.set(cacheKey, diskPrompt);
3975
4047
  return diskPrompt;
3976
4048
  }
3977
4049
  return void 0;
@@ -3986,7 +4058,7 @@ var PromptCache = class {
3986
4058
  */
3987
4059
  async set(key, value) {
3988
4060
  const cacheKey = createCacheKey(key);
3989
- this.memoryCache.set(cacheKey, value);
4061
+ this.memoryCache?.set(cacheKey, value);
3990
4062
  if (this.diskCache) {
3991
4063
  await this.diskCache.set(cacheKey, value);
3992
4064
  }
@@ -4016,23 +4088,25 @@ var ParametersCache = class {
4016
4088
  }
4017
4089
  async get(key) {
4018
4090
  const cacheKey = createCacheKey2(key);
4019
- const memoryParams = this.memoryCache.get(cacheKey);
4020
- if (memoryParams !== void 0) {
4021
- return memoryParams;
4091
+ if (this.memoryCache) {
4092
+ const memoryParams = this.memoryCache.get(cacheKey);
4093
+ if (memoryParams !== void 0) {
4094
+ return memoryParams;
4095
+ }
4022
4096
  }
4023
4097
  if (this.diskCache) {
4024
4098
  const diskParams = await this.diskCache.get(cacheKey);
4025
4099
  if (!diskParams) {
4026
4100
  return void 0;
4027
4101
  }
4028
- this.memoryCache.set(cacheKey, diskParams);
4102
+ this.memoryCache?.set(cacheKey, diskParams);
4029
4103
  return diskParams;
4030
4104
  }
4031
4105
  return void 0;
4032
4106
  }
4033
4107
  async set(key, value) {
4034
4108
  const cacheKey = createCacheKey2(key);
4035
- this.memoryCache.set(cacheKey, value);
4109
+ this.memoryCache?.set(cacheKey, value);
4036
4110
  if (this.diskCache) {
4037
4111
  await this.diskCache.set(cacheKey, value);
4038
4112
  }
@@ -4564,21 +4638,22 @@ var BraintrustState = class _BraintrustState {
4564
4638
  setGlobalDebugLogLevel(void 0);
4565
4639
  }
4566
4640
  this.resetLoginInfo();
4567
- const memoryCache = new LRUCache({
4568
- max: Number(isomorph_default.getEnv("BRAINTRUST_PROMPT_CACHE_MEMORY_MAX")) ?? 1 << 10
4641
+ const { memoryCache, diskCache } = createCacheLayers({
4642
+ memoryMaxEnvVar: "BRAINTRUST_PROMPT_CACHE_MEMORY_MAX",
4643
+ diskCacheDirEnvVar: "BRAINTRUST_PROMPT_CACHE_DIR",
4644
+ diskMaxEnvVar: "BRAINTRUST_PROMPT_CACHE_DISK_MAX",
4645
+ getDefaultDiskCacheDir: () => `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/prompt_cache`
4569
4646
  });
4570
- const diskCache = canUseDiskCache() ? new DiskCache({
4571
- cacheDir: isomorph_default.getEnv("BRAINTRUST_PROMPT_CACHE_DIR") ?? `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/prompt_cache`,
4572
- max: Number(isomorph_default.getEnv("BRAINTRUST_PROMPT_CACHE_DISK_MAX")) ?? 1 << 20
4573
- }) : void 0;
4574
4647
  this.promptCache = new PromptCache({ memoryCache, diskCache });
4575
- const parametersMemoryCache = new LRUCache({
4576
- max: Number(isomorph_default.getEnv("BRAINTRUST_PARAMETERS_CACHE_MEMORY_MAX")) ?? 1 << 10
4648
+ const {
4649
+ memoryCache: parametersMemoryCache,
4650
+ diskCache: parametersDiskCache
4651
+ } = createCacheLayers({
4652
+ memoryMaxEnvVar: "BRAINTRUST_PARAMETERS_CACHE_MEMORY_MAX",
4653
+ diskCacheDirEnvVar: "BRAINTRUST_PARAMETERS_CACHE_DIR",
4654
+ diskMaxEnvVar: "BRAINTRUST_PARAMETERS_CACHE_DISK_MAX",
4655
+ getDefaultDiskCacheDir: () => `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/parameters_cache`
4577
4656
  });
4578
- const parametersDiskCache = canUseDiskCache() ? new DiskCache({
4579
- cacheDir: isomorph_default.getEnv("BRAINTRUST_PARAMETERS_CACHE_DIR") ?? `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/parameters_cache`,
4580
- max: Number(isomorph_default.getEnv("BRAINTRUST_PARAMETERS_CACHE_DISK_MAX")) ?? 1 << 20
4581
- }) : void 0;
4582
4657
  this.parametersCache = new ParametersCache({
4583
4658
  memoryCache: parametersMemoryCache,
4584
4659
  diskCache: parametersDiskCache
@@ -23964,825 +24039,440 @@ var flueChannels = defineChannels("@flue/runtime", {
23964
24039
  createContext: channel({
23965
24040
  channelName: "createFlueContext",
23966
24041
  kind: "sync-stream"
23967
- }),
23968
- openSession: channel({
23969
- channelName: "Harness.openSession",
23970
- kind: "async"
23971
- }),
23972
- contextEvent: channel({
23973
- channelName: "context.event",
23974
- kind: "sync-stream"
23975
- }),
23976
- prompt: channel({
23977
- channelName: "session.prompt",
23978
- kind: "async"
23979
- }),
23980
- skill: channel({
23981
- channelName: "session.skill",
23982
- kind: "async"
23983
- }),
23984
- task: channel({
23985
- channelName: "session.task",
23986
- kind: "async"
23987
- }),
23988
- compact: channel({
23989
- channelName: "session.compact",
23990
- kind: "async"
23991
24042
  })
23992
24043
  });
23993
24044
 
23994
- // src/wrappers/flue.ts
23995
- var WRAPPED_FLUE_CONTEXT = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-context");
23996
- var WRAPPED_FLUE_HARNESS = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-harness");
23997
- var WRAPPED_FLUE_SESSION = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-session");
23998
- var SUBSCRIBED_FLUE_CONTEXT_EVENTS = /* @__PURE__ */ Symbol.for(
23999
- "braintrust.flue.subscribed-context-events"
24000
- );
24001
- function wrapFlueContext(ctx) {
24002
- if (!isPlausibleFlueContext(ctx)) {
24003
- console.warn("Unsupported Flue context. Not wrapping.");
24004
- return ctx;
24045
+ // src/instrumentation/plugins/flue-plugin.ts
24046
+ var FLUE_AUTO_STATE = /* @__PURE__ */ Symbol.for("braintrust.flue.auto-state");
24047
+ var FLUE_OBSERVE_BRIDGE = /* @__PURE__ */ Symbol.for("braintrust.flue.observe-bridge");
24048
+ var braintrustFlueObserver = (event, ctx) => {
24049
+ getObserveBridge().handle(event, ctx);
24050
+ };
24051
+ var FluePlugin = class extends BasePlugin {
24052
+ onEnable() {
24053
+ this.unsubscribers.push(enableFlueAutoInstrumentation());
24005
24054
  }
24006
- const context = ctx;
24007
- subscribeFlueContextEvents(context, { captureTurnSpans: true });
24008
- return patchFlueContextInPlace(context);
24009
- }
24010
- function patchFlueContextInPlace(ctx) {
24011
- const context = ctx;
24012
- if (context[WRAPPED_FLUE_CONTEXT]) {
24013
- return ctx;
24055
+ onDisable() {
24056
+ for (const unsubscribe of this.unsubscribers) {
24057
+ unsubscribe();
24058
+ }
24059
+ this.unsubscribers = [];
24014
24060
  }
24015
- const originalInit = context.init.bind(context);
24016
- try {
24017
- Object.defineProperty(context, WRAPPED_FLUE_CONTEXT, {
24018
- configurable: false,
24019
- enumerable: false,
24020
- value: true
24021
- });
24022
- Object.defineProperty(context, "init", {
24023
- configurable: true,
24024
- value: async function wrappedFlueInit(options) {
24025
- const harness = await originalInit(options);
24026
- return wrapFlueHarness(harness);
24027
- },
24028
- writable: true
24029
- });
24030
- } catch {
24061
+ };
24062
+ function enableFlueAutoInstrumentation() {
24063
+ const state = getAutoState();
24064
+ state.refCount += 1;
24065
+ if (!state.handlers) {
24066
+ const channel2 = flueChannels.createContext.tracingChannel();
24067
+ const handlers = {
24068
+ end: (event) => {
24069
+ subscribeToFlueContext(event.result, state);
24070
+ }
24071
+ };
24072
+ channel2.subscribe(handlers);
24073
+ state.channel = channel2;
24074
+ state.handlers = handlers;
24031
24075
  }
24032
- return ctx;
24076
+ let released = false;
24077
+ return () => {
24078
+ if (released) {
24079
+ return;
24080
+ }
24081
+ released = true;
24082
+ releaseAutoState(state);
24083
+ };
24033
24084
  }
24034
- function wrapFlueSession(session) {
24035
- if (!isPlausibleFlueSession(session)) {
24036
- console.warn("Unsupported Flue session. Not wrapping.");
24037
- return session;
24085
+ function getAutoState() {
24086
+ const existing = Reflect.get(globalThis, FLUE_AUTO_STATE);
24087
+ if (isAutoState(existing)) {
24088
+ return existing;
24038
24089
  }
24039
- return patchFlueSessionInPlace(session);
24090
+ const state = {
24091
+ contexts: /* @__PURE__ */ new WeakSet(),
24092
+ refCount: 0
24093
+ };
24094
+ Reflect.set(globalThis, FLUE_AUTO_STATE, state);
24095
+ return state;
24040
24096
  }
24041
- function subscribeFlueContextEvents(ctx, options = {}) {
24042
- if (!ctx || typeof ctx !== "object" || typeof ctx.subscribeEvent !== "function") {
24043
- return void 0;
24044
- }
24045
- const context = ctx;
24046
- const captureTurnSpans = options.captureTurnSpans ?? true;
24047
- const existingSubscription = context[SUBSCRIBED_FLUE_CONTEXT_EVENTS];
24048
- if (existingSubscription) {
24049
- if (existingSubscription.captureTurnSpans || !captureTurnSpans) {
24050
- return void 0;
24051
- }
24052
- try {
24053
- existingSubscription.unsubscribe();
24054
- } catch {
24055
- }
24056
- }
24057
- try {
24058
- const unsubscribe = ctx.subscribeEvent((event) => {
24059
- flueChannels.contextEvent.traceSync(() => void 0, {
24060
- arguments: [event],
24061
- captureTurnSpans,
24062
- context: ctx
24063
- });
24064
- });
24065
- if (existingSubscription) {
24066
- existingSubscription.captureTurnSpans = captureTurnSpans;
24067
- existingSubscription.unsubscribe = unsubscribe;
24068
- } else {
24069
- Object.defineProperty(context, SUBSCRIBED_FLUE_CONTEXT_EVENTS, {
24070
- configurable: false,
24071
- enumerable: false,
24072
- value: {
24073
- captureTurnSpans,
24074
- unsubscribe
24075
- }
24076
- });
24077
- }
24078
- return unsubscribe;
24079
- } catch {
24080
- return void 0;
24097
+ function getObserveBridge() {
24098
+ const existing = Reflect.get(globalThis, FLUE_OBSERVE_BRIDGE);
24099
+ if (isFlueObserveBridge(existing)) {
24100
+ return existing;
24081
24101
  }
24102
+ const bridge = new FlueObserveBridge();
24103
+ Reflect.set(globalThis, FLUE_OBSERVE_BRIDGE, bridge);
24104
+ return bridge;
24082
24105
  }
24083
- function wrapFlueHarness(harness) {
24084
- if (!isPlausibleFlueHarness(harness)) {
24085
- return harness;
24086
- }
24087
- const target = harness;
24088
- if (target[WRAPPED_FLUE_HARNESS]) {
24089
- return harness;
24090
- }
24091
- const originalSession = target.session.bind(target);
24092
- try {
24093
- Object.defineProperty(target, WRAPPED_FLUE_HARNESS, {
24094
- configurable: false,
24095
- enumerable: false,
24096
- value: true
24097
- });
24098
- Object.defineProperty(target, "session", {
24099
- configurable: true,
24100
- value: async function wrappedFlueHarnessSession(name, options) {
24101
- const session = await originalSession(name, options);
24102
- return patchFlueSessionInPlace(session);
24103
- },
24104
- writable: true
24105
- });
24106
- const sessions = target.sessions;
24107
- if (sessions && typeof sessions === "object") {
24108
- patchFlueSessionFactory(sessions, "get");
24109
- patchFlueSessionFactory(sessions, "create");
24110
- }
24111
- } catch {
24112
- }
24113
- return harness;
24106
+ function isFlueObserveBridge(value) {
24107
+ return isObjectLike(value) && typeof Reflect.get(value, "handle") === "function" && typeof Reflect.get(value, "reset") === "function";
24114
24108
  }
24115
- function patchFlueSessionInPlace(session) {
24116
- if (session[WRAPPED_FLUE_SESSION]) {
24117
- return session;
24118
- }
24119
- try {
24120
- Object.defineProperty(session, WRAPPED_FLUE_SESSION, {
24121
- configurable: false,
24122
- enumerable: false,
24123
- value: true
24124
- });
24125
- patchCallHandleMethod(session, "prompt", flueChannels.prompt);
24126
- patchCallHandleMethod(session, "skill", flueChannels.skill);
24127
- patchCallHandleMethod(session, "task", flueChannels.task);
24128
- patchCompact(session);
24129
- } catch {
24130
- }
24131
- return session;
24109
+ function isAutoState(value) {
24110
+ return isObjectLike(value) && Reflect.get(value, "contexts") instanceof WeakSet && typeof Reflect.get(value, "refCount") === "number";
24132
24111
  }
24133
- function patchFlueSessionFactory(sessions, method) {
24134
- const original = sessions[method];
24135
- if (typeof original !== "function") {
24112
+ function releaseAutoState(state) {
24113
+ state.refCount -= 1;
24114
+ if (state.refCount > 0) {
24136
24115
  return;
24137
24116
  }
24138
- const bound = original.bind(sessions);
24139
- Object.defineProperty(sessions, method, {
24140
- configurable: true,
24141
- value: async function wrappedFlueSessionFactory(name, options) {
24142
- const session = await bound(name, options);
24143
- return patchFlueSessionInPlace(session);
24144
- },
24145
- writable: true
24146
- });
24147
- }
24148
- function patchCallHandleMethod(session, method, channel2) {
24149
- const original = session[method];
24150
- if (typeof original !== "function") {
24151
- return;
24117
+ try {
24118
+ if (state.channel && state.handlers) {
24119
+ state.channel.unsubscribe(state.handlers);
24120
+ }
24121
+ } finally {
24122
+ Reflect.deleteProperty(globalThis, FLUE_AUTO_STATE);
24152
24123
  }
24153
- const bound = original.bind(session);
24154
- Object.defineProperty(session, method, {
24155
- configurable: true,
24156
- value(input, options) {
24157
- const args = [input, options];
24158
- const { originalResult, traced: traced2 } = traceFlueOperation(channel2, {
24159
- context: {
24160
- arguments: args,
24161
- operation: method,
24162
- session
24163
- },
24164
- run: () => bound(input, options)
24165
- });
24166
- return preserveCallHandle(originalResult, traced2);
24167
- },
24168
- writable: true
24169
- });
24170
24124
  }
24171
- function patchCompact(session) {
24172
- const original = session.compact;
24173
- if (typeof original !== "function") {
24125
+ function subscribeToFlueContext(value, state) {
24126
+ if (!isObservableFlueContext(value) || state.contexts.has(value)) {
24174
24127
  return;
24175
24128
  }
24176
- const bound = original.bind(session);
24177
- Object.defineProperty(session, "compact", {
24178
- configurable: true,
24179
- value() {
24180
- const context = {
24181
- arguments: [],
24182
- operation: "compact",
24183
- session
24184
- };
24185
- return flueChannels.compact.tracePromise(() => bound(), context);
24186
- },
24187
- writable: true
24188
- });
24189
- }
24190
- function traceFlueOperation(channel2, args) {
24191
- const tracingChannel2 = channel2.tracingChannel();
24192
- const context = args.context;
24193
- let originalResult;
24194
- let traced2;
24195
- const run = () => {
24129
+ const ctx = flueContextFromUnknown(value);
24130
+ let released = false;
24131
+ let unsubscribe;
24132
+ const release = () => {
24133
+ if (released) {
24134
+ return;
24135
+ }
24136
+ released = true;
24196
24137
  try {
24197
- originalResult = args.run();
24198
- tracingChannel2.end?.publish(context);
24138
+ unsubscribe?.();
24199
24139
  } catch (error) {
24200
- context.error = normalizeError3(error);
24201
- tracingChannel2.error?.publish(context);
24202
- tracingChannel2.end?.publish(context);
24203
- throw error;
24140
+ logInstrumentationError3("Flue context unsubscribe", error);
24204
24141
  }
24205
- traced2 = Promise.resolve(originalResult).then(
24206
- (result) => {
24207
- context.result = result;
24208
- tracingChannel2.asyncStart?.publish(context);
24209
- tracingChannel2.asyncEnd?.publish(context);
24210
- return result;
24211
- },
24212
- (error) => {
24213
- context.error = normalizeError3(error);
24214
- tracingChannel2.error?.publish(context);
24215
- tracingChannel2.asyncStart?.publish(context);
24216
- tracingChannel2.asyncEnd?.publish(context);
24217
- throw error;
24218
- }
24219
- );
24220
24142
  };
24221
- if (tracingChannel2.start?.runStores) {
24222
- tracingChannel2.start.runStores(context, run);
24223
- } else {
24224
- tracingChannel2.start?.publish(context);
24225
- run();
24143
+ try {
24144
+ unsubscribe = value.subscribeEvent((event) => {
24145
+ if (state.refCount <= 0) {
24146
+ release();
24147
+ return;
24148
+ }
24149
+ braintrustFlueObserver(event, ctx);
24150
+ if (isAutoContextTerminalEvent(event, ctx)) {
24151
+ release();
24152
+ }
24153
+ });
24154
+ state.contexts.add(value);
24155
+ } catch (error) {
24156
+ logInstrumentationError3("Flue context subscription", error);
24226
24157
  }
24227
- return { originalResult, traced: traced2 };
24228
- }
24229
- function normalizeError3(error) {
24230
- return error instanceof Error ? error : new Error(String(error));
24231
24158
  }
24232
- function preserveCallHandle(originalHandle, traced2) {
24233
- if (!isFlueCallHandle(originalHandle)) {
24234
- return traced2;
24159
+ function isAutoContextTerminalEvent(event, ctx) {
24160
+ if (!isObjectLike(event)) {
24161
+ return false;
24235
24162
  }
24236
- const handle = originalHandle;
24237
- const wrapped = {
24238
- get signal() {
24239
- return handle.signal;
24240
- },
24241
- abort(reason) {
24242
- return handle.abort(reason);
24243
- },
24244
- then(onfulfilled, onrejected) {
24245
- return traced2.then(onfulfilled, onrejected);
24246
- }
24247
- };
24248
- return wrapped;
24163
+ const type = Reflect.get(event, "type");
24164
+ if (type === "run_end") {
24165
+ return true;
24166
+ }
24167
+ if (type !== "operation") {
24168
+ return false;
24169
+ }
24170
+ return !ctx?.runId && typeof Reflect.get(event, "runId") !== "string";
24249
24171
  }
24250
- function isPlausibleFlueContext(value) {
24251
- return !!value && typeof value === "object" && typeof value.init === "function";
24172
+ function isObservableFlueContext(value) {
24173
+ return isObjectLike(value) && typeof Reflect.get(value, "subscribeEvent") === "function";
24252
24174
  }
24253
- function isPlausibleFlueHarness(value) {
24254
- return !!value && typeof value === "object" && typeof value.session === "function";
24175
+ function isFlueEvent(event) {
24176
+ const type = Reflect.get(event, "type");
24177
+ 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";
24255
24178
  }
24256
- function isPlausibleFlueSession(value) {
24257
- return !!value && typeof value === "object" && typeof value.prompt === "function" && typeof value.skill === "function" && typeof value.task === "function" && typeof value.compact === "function";
24179
+ function flueContextFromUnknown(ctx) {
24180
+ if (!isObjectLike(ctx)) {
24181
+ return void 0;
24182
+ }
24183
+ const id = Reflect.get(ctx, "id");
24184
+ const runId = Reflect.get(ctx, "runId");
24185
+ return {
24186
+ ...typeof id === "string" ? { id } : {},
24187
+ ...typeof runId === "string" ? { runId } : {}
24188
+ };
24258
24189
  }
24259
- function isFlueCallHandle(value) {
24260
- return !!value && typeof value === "object" && typeof value.then === "function" && typeof value.abort === "function" && "signal" in value;
24190
+ function isObjectLike(value) {
24191
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24261
24192
  }
24262
-
24263
- // src/instrumentation/plugins/flue-plugin.ts
24264
- var FluePlugin = class extends BasePlugin {
24265
- activeOperationsById = /* @__PURE__ */ new Map();
24266
- activeOperationsByScope = /* @__PURE__ */ new Map();
24267
- compactionsByScope = /* @__PURE__ */ new Map();
24268
- pendingOperationsByKey = /* @__PURE__ */ new Map();
24193
+ var FlueObserveBridge = class {
24194
+ compactionsByKey = /* @__PURE__ */ new Map();
24195
+ operationsById = /* @__PURE__ */ new Map();
24196
+ runsById = /* @__PURE__ */ new Map();
24197
+ seenEvents = /* @__PURE__ */ new WeakSet();
24269
24198
  tasksById = /* @__PURE__ */ new Map();
24270
- toolsById = /* @__PURE__ */ new Map();
24271
- turnsByScope = /* @__PURE__ */ new Map();
24272
- onEnable() {
24273
- this.subscribeToContextCreation();
24274
- this.subscribeToSessionCreation();
24275
- this.subscribeToContextEvents();
24276
- this.subscribeToSessionOperations();
24277
- }
24278
- onDisable() {
24279
- for (const unsubscribe of this.unsubscribers) {
24280
- unsubscribe();
24199
+ toolsByKey = /* @__PURE__ */ new Map();
24200
+ turnsByKey = /* @__PURE__ */ new Map();
24201
+ handle(event, ctx) {
24202
+ if (!isObjectLike(event) || !isFlueEvent(event)) {
24203
+ return;
24281
24204
  }
24282
- this.unsubscribers = [];
24283
- this.activeOperationsById.clear();
24284
- this.activeOperationsByScope.clear();
24285
- this.compactionsByScope.clear();
24286
- this.pendingOperationsByKey.clear();
24205
+ if (this.seenEvents.has(event)) {
24206
+ return;
24207
+ }
24208
+ this.seenEvents.add(event);
24209
+ try {
24210
+ this.handleEvent(event, flueContextFromUnknown(ctx));
24211
+ } catch (error) {
24212
+ logInstrumentationError3("Flue observe", error);
24213
+ }
24214
+ }
24215
+ reset() {
24216
+ this.compactionsByKey.clear();
24217
+ this.operationsById.clear();
24218
+ this.runsById.clear();
24219
+ this.seenEvents = /* @__PURE__ */ new WeakSet();
24287
24220
  this.tasksById.clear();
24288
- this.toolsById.clear();
24289
- this.turnsByScope.clear();
24221
+ this.toolsByKey.clear();
24222
+ this.turnsByKey.clear();
24290
24223
  }
24291
- subscribeToContextCreation() {
24292
- const channel2 = flueChannels.createContext.tracingChannel();
24293
- const handlers = {
24294
- end: (event) => {
24295
- const ctx = event.result;
24296
- if (!ctx) {
24297
- return;
24298
- }
24299
- subscribeFlueContextEvents(ctx, { captureTurnSpans: false });
24300
- patchFlueContextInPlace(ctx);
24301
- },
24302
- error: () => {
24303
- }
24304
- };
24305
- channel2.subscribe(handlers);
24306
- this.unsubscribers.push(() => {
24307
- channel2.unsubscribe(handlers);
24308
- });
24224
+ handleEvent(event, ctx) {
24225
+ switch (event.type) {
24226
+ case "run_start":
24227
+ this.handleRunStart(event, ctx);
24228
+ return;
24229
+ case "run_end":
24230
+ this.handleRunEnd(event);
24231
+ return;
24232
+ case "operation_start":
24233
+ this.handleOperationStart(event);
24234
+ return;
24235
+ case "operation":
24236
+ this.handleOperation(event);
24237
+ return;
24238
+ case "turn_request":
24239
+ this.handleTurnRequest(event);
24240
+ return;
24241
+ case "turn":
24242
+ this.handleTurn(event);
24243
+ return;
24244
+ case "tool_start":
24245
+ this.handleToolStart(event);
24246
+ return;
24247
+ case "tool_call":
24248
+ this.handleToolCall(event);
24249
+ return;
24250
+ case "task_start":
24251
+ this.handleTaskStart(event);
24252
+ return;
24253
+ case "task":
24254
+ this.handleTask(event);
24255
+ return;
24256
+ case "compaction_start":
24257
+ this.handleCompactionStart(event);
24258
+ return;
24259
+ case "compaction":
24260
+ this.handleCompaction(event);
24261
+ return;
24262
+ default:
24263
+ return;
24264
+ }
24309
24265
  }
24310
- subscribeToSessionCreation() {
24311
- const channel2 = flueChannels.openSession.tracingChannel();
24312
- const handlers = {
24313
- asyncEnd: (event) => {
24314
- if (event.result) {
24315
- patchFlueSessionInPlace(
24316
- event.result
24317
- );
24318
- }
24319
- if (event.harness) {
24320
- wrapFlueHarness(event.harness);
24321
- }
24322
- },
24323
- error: () => {
24324
- }
24266
+ handleRunStart(event, ctx) {
24267
+ if (!event.runId) {
24268
+ return;
24269
+ }
24270
+ const workflowName = event.workflowName ?? event.owner?.workflowName ?? (typeof ctx?.id === "string" ? ctx.id : "unknown");
24271
+ const metadata = {
24272
+ ...extractPayloadMetadata(event.payload),
24273
+ ...extractEventMetadata(event, ctx),
24274
+ ...workflowName ? { "flue.workflow_name": workflowName } : {},
24275
+ provider: "flue"
24325
24276
  };
24326
- channel2.subscribe(handlers);
24327
- this.unsubscribers.push(() => {
24328
- channel2.unsubscribe(handlers);
24277
+ const span = startSpan({
24278
+ name: `workflow:${workflowName}`,
24279
+ spanAttributes: { type: "task" /* TASK */ },
24280
+ startTime: eventTime(event.startedAt ?? event.timestamp),
24281
+ event: {
24282
+ input: event.payload,
24283
+ metadata
24284
+ }
24329
24285
  });
24286
+ this.runsById.set(event.runId, { metadata, span });
24330
24287
  }
24331
- subscribeToSessionOperations() {
24332
- this.subscribeToSessionOperation(flueChannels.prompt);
24333
- this.subscribeToSessionOperation(flueChannels.skill);
24334
- this.subscribeToSessionOperation(flueChannels.task);
24335
- this.subscribeToCompact();
24336
- }
24337
- subscribeToSessionOperation(channel2) {
24338
- const tracingChannel2 = channel2.tracingChannel();
24339
- const states = /* @__PURE__ */ new WeakMap();
24340
- const ensureState2 = (event) => {
24341
- const existing = states.get(event);
24342
- if (existing) {
24343
- return existing;
24344
- }
24345
- const state = this.startOperationState({
24346
- args: event.arguments,
24347
- moduleVersion: typeof event.moduleVersion === "string" ? event.moduleVersion : void 0,
24348
- operation: event.operation,
24349
- session: event.session
24350
- });
24351
- states.set(event, state);
24352
- return state;
24353
- };
24354
- const unbindCurrentSpanStore = this.bindCurrentSpanStoreToOperationStart(
24355
- tracingChannel2,
24356
- ensureState2
24357
- );
24358
- const handlers = {
24359
- start: (event) => {
24360
- ensureState2(event);
24361
- },
24362
- asyncEnd: (event) => {
24363
- this.endOperationState(states.get(event), event.result);
24364
- states.delete(event);
24365
- },
24366
- error: (event) => {
24367
- const state = states.get(event);
24368
- if (state && event.error) {
24369
- safeLog3(state.span, { error: errorToString(event.error) });
24370
- this.finishOperationState(state);
24371
- }
24372
- states.delete(event);
24373
- }
24374
- };
24375
- tracingChannel2.subscribe(handlers);
24376
- this.unsubscribers.push(() => {
24377
- unbindCurrentSpanStore?.();
24378
- tracingChannel2.unsubscribe(handlers);
24379
- });
24380
- }
24381
- subscribeToCompact() {
24382
- const tracingChannel2 = flueChannels.compact.tracingChannel();
24383
- const states = /* @__PURE__ */ new WeakMap();
24384
- const ensureState2 = (event) => {
24385
- const existing = states.get(event);
24386
- if (existing) {
24387
- return existing;
24388
- }
24389
- const state = this.startOperationState({
24390
- args: [],
24391
- moduleVersion: typeof event.moduleVersion === "string" ? event.moduleVersion : void 0,
24392
- operation: event.operation,
24393
- session: event.session
24288
+ handleRunEnd(event) {
24289
+ const state = this.runsById.get(event.runId);
24290
+ this.finishPendingSpansForRun(event);
24291
+ if (state) {
24292
+ safeLog3(state.span, {
24293
+ ...event.isError ? { error: errorToString(event.error) } : {},
24294
+ metadata: {
24295
+ ...state.metadata,
24296
+ ...extractEventMetadata(event),
24297
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
24298
+ },
24299
+ metrics: durationMetrics2(event.durationMs),
24300
+ output: event.result
24394
24301
  });
24395
- states.set(event, state);
24396
- return state;
24397
- };
24398
- const unbindCurrentSpanStore = this.bindCurrentSpanStoreToOperationStart(
24399
- tracingChannel2,
24400
- ensureState2
24401
- );
24402
- const handlers = {
24403
- start: (event) => {
24404
- ensureState2(event);
24405
- },
24406
- asyncEnd: (event) => {
24407
- this.endOperationState(states.get(event), void 0);
24408
- states.delete(event);
24409
- },
24410
- error: (event) => {
24411
- const state = states.get(event);
24412
- if (state && event.error) {
24413
- safeLog3(state.span, { error: errorToString(event.error) });
24414
- this.finishOperationState(state);
24415
- }
24416
- states.delete(event);
24417
- }
24418
- };
24419
- tracingChannel2.subscribe(handlers);
24420
- this.unsubscribers.push(() => {
24421
- unbindCurrentSpanStore?.();
24422
- tracingChannel2.unsubscribe(handlers);
24423
- });
24424
- }
24425
- subscribeToContextEvents() {
24426
- const channel2 = flueChannels.contextEvent.tracingChannel();
24427
- const handlers = {
24428
- start: (event) => {
24429
- const flueEvent = event.arguments[0];
24430
- if (!flueEvent) {
24431
- return;
24432
- }
24433
- try {
24434
- this.handleFlueEvent(flueEvent, {
24435
- captureTurnSpans: event.captureTurnSpans !== false
24436
- });
24437
- } catch (error) {
24438
- logInstrumentationError3("Flue event", error);
24439
- }
24440
- },
24441
- error: () => {
24442
- }
24443
- };
24444
- channel2.subscribe(handlers);
24445
- this.unsubscribers.push(() => {
24446
- channel2.unsubscribe(handlers);
24447
- });
24448
- }
24449
- bindCurrentSpanStoreToOperationStart(tracingChannel2, ensureState2) {
24450
- const state = _internalGetGlobalState();
24451
- const startChannel = tracingChannel2.start;
24452
- const contextManager = state?.contextManager;
24453
- const currentSpanStore = contextManager ? contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
24454
- if (!currentSpanStore || !startChannel) {
24455
- return void 0;
24302
+ safeEnd(state.span, eventTime(event.timestamp));
24303
+ this.runsById.delete(event.runId);
24456
24304
  }
24457
- startChannel.bindStore(currentSpanStore, (event) => {
24458
- const operationState = ensureState2(event);
24459
- return contextManager.wrapSpanForStore(operationState.span);
24305
+ void flush().catch((error) => {
24306
+ logInstrumentationError3("Flue flush", error);
24460
24307
  });
24461
- return () => {
24462
- startChannel.unbindStore(currentSpanStore);
24463
- };
24464
- }
24465
- startOperationState(args) {
24466
- const sessionName = getSessionName(args.session);
24467
- const metadata = {
24468
- ...extractOperationInputMetadata(args.operation, args.args),
24469
- ...extractSessionMetadata(args.session),
24470
- "flue.operation": args.operation,
24471
- provider: "flue",
24472
- ...args.moduleVersion ? { "flue.version": args.moduleVersion } : {}
24473
- };
24474
- const span = startSpan({
24475
- name: `flue.session.${args.operation}`,
24476
- spanAttributes: { type: "task" /* TASK */ }
24477
- });
24478
- const state = {
24479
- metadata,
24480
- operation: args.operation,
24481
- sessionName,
24482
- span,
24483
- startTime: getCurrentUnixTimestamp()
24484
- };
24485
- safeLog3(span, {
24486
- input: extractOperationInput(args.operation, args.args),
24487
- metadata
24488
- });
24489
- this.pendingOperationQueue(operationKey(sessionName, args.operation)).push(
24490
- state
24491
- );
24492
- addOperationToScope(
24493
- this.activeOperationsByScope,
24494
- sessionName ?? "unknown",
24495
- state
24496
- );
24497
- return state;
24498
- }
24499
- endOperationState(state, result) {
24500
- if (!state) {
24501
- return;
24502
- }
24503
- const metadata = {
24504
- ...state.metadata,
24505
- ...extractPromptResponseMetadata(result)
24506
- };
24507
- const metrics = {
24508
- ...buildDurationMetrics3(state.startTime),
24509
- ...metricsFromUsage(result?.usage)
24510
- };
24511
- safeLog3(state.span, {
24512
- metadata,
24513
- metrics,
24514
- output: extractOperationOutput(result)
24515
- });
24516
- this.finishCompactionsForOperation(state);
24517
- this.finishOperationState(state);
24518
- }
24519
- finishOperationState(state) {
24520
- removePendingOperation(this.pendingOperationsByKey, state);
24521
- if (state.operationId) {
24522
- this.activeOperationsById.delete(state.operationId);
24523
- }
24524
- removeScopedOperation(this.activeOperationsByScope, state);
24525
- state.span.end();
24526
- }
24527
- handleFlueEvent(event, options) {
24528
- switch (event.type) {
24529
- case "operation_start":
24530
- this.handleOperationStart(event);
24531
- return;
24532
- case "operation":
24533
- this.handleOperation(event);
24534
- return;
24535
- case "text_delta":
24536
- if (!options.captureTurnSpans) {
24537
- return;
24538
- }
24539
- this.ensureTurnState(event).text.push(
24540
- typeof event.text === "string" ? event.text : ""
24541
- );
24542
- return;
24543
- case "thinking_start":
24544
- if (!options.captureTurnSpans) {
24545
- return;
24546
- }
24547
- this.handleThinkingStart(event);
24548
- return;
24549
- case "thinking_delta":
24550
- if (!options.captureTurnSpans) {
24551
- return;
24552
- }
24553
- this.handleThinkingDelta(event);
24554
- return;
24555
- case "thinking_end":
24556
- if (!options.captureTurnSpans) {
24557
- return;
24558
- }
24559
- this.handleThinkingEnd(event);
24560
- return;
24561
- case "turn":
24562
- if (!options.captureTurnSpans) {
24563
- return;
24564
- }
24565
- this.handleTurn(event);
24566
- return;
24567
- case "tool_start":
24568
- this.handleToolStart(event, options);
24569
- return;
24570
- case "tool_call":
24571
- this.handleToolCall(event);
24572
- return;
24573
- case "task_start":
24574
- this.handleTaskStart(event);
24575
- return;
24576
- case "task":
24577
- this.handleTask(event);
24578
- return;
24579
- case "compaction_start":
24580
- this.handleCompactionStart(event);
24581
- return;
24582
- case "compaction":
24583
- this.handleCompaction(event);
24584
- return;
24585
- default:
24586
- return;
24587
- }
24588
24308
  }
24589
24309
  handleOperationStart(event) {
24590
- if (!isInstrumentedOperation(event.operationKind)) {
24310
+ if (!event.operationId || !isInstrumentedOperation(event.operationKind)) {
24591
24311
  return;
24592
24312
  }
24593
- const state = this.takePendingOperationForEvent(event);
24594
- if (!state) {
24595
- return;
24596
- }
24597
- state.operationId = event.operationId;
24598
- this.activeOperationsById.set(event.operationId, state);
24599
- addScopedOperation(this.activeOperationsByScope, event, state);
24600
- state.metadata = {
24601
- ...state.metadata,
24313
+ const metadata = {
24602
24314
  ...extractEventMetadata(event),
24603
- "flue.operation_id": event.operationId
24315
+ "flue.operation": event.operationKind,
24316
+ provider: "flue"
24604
24317
  };
24605
- safeLog3(state.span, { metadata: state.metadata });
24318
+ const parent = this.parentSpanForEvent(event);
24319
+ const span = startFlueSpan(parent, {
24320
+ name: `flue.${event.operationKind}`,
24321
+ spanAttributes: { type: "task" /* TASK */ },
24322
+ startTime: eventTime(event.timestamp),
24323
+ event: { metadata }
24324
+ });
24325
+ this.operationsById.set(event.operationId, { metadata, span });
24606
24326
  }
24607
24327
  handleOperation(event) {
24608
- const state = event.operationId ? this.activeOperationsById.get(event.operationId) : void 0;
24609
- if (!state) {
24328
+ if (!isInstrumentedOperation(event.operationKind)) {
24610
24329
  return;
24611
24330
  }
24331
+ const state = this.operationsById.get(event.operationId) ?? this.startSyntheticOperation(event);
24332
+ const output = operationOutput(event);
24612
24333
  const metadata = {
24613
24334
  ...state.metadata,
24614
24335
  ...extractEventMetadata(event),
24615
- ...typeof event.durationMs === "number" ? { "flue.duration_ms": event.durationMs } : {},
24616
- ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
24336
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {},
24337
+ ...event.usage ? { "flue.usage": event.usage } : {}
24617
24338
  };
24618
- const metrics = metricsFromUsage(event.usage);
24339
+ this.finishPendingChildrenForOperation(event, output);
24619
24340
  safeLog3(state.span, {
24620
- ...event.error ? { error: errorToString(event.error) } : {},
24341
+ ...event.isError ? { error: errorToString(event.error) } : {},
24621
24342
  metadata,
24622
- ...Object.keys(metrics).length ? { metrics } : {}
24343
+ metrics: durationMetrics2(event.durationMs),
24344
+ output
24623
24345
  });
24346
+ safeEnd(state.span, eventTime(event.timestamp));
24347
+ this.operationsById.delete(event.operationId);
24624
24348
  }
24625
- ensureTurnState(event) {
24626
- const scope = scopeKey(event);
24627
- const existing = this.turnsByScope.get(scope);
24628
- if (existing) {
24629
- return existing;
24349
+ handleTurnRequest(event) {
24350
+ const key = turnKey(event);
24351
+ if (!key) {
24352
+ return;
24630
24353
  }
24631
- const parent = this.parentSpanForEvent(event);
24632
24354
  const metadata = {
24633
24355
  ...extractEventMetadata(event),
24634
- provider: "flue"
24356
+ ...event.api ? { "flue.api": event.api } : {},
24357
+ ...event.model ? { model: event.model, "flue.model": event.model } : {},
24358
+ ...event.provider ? { provider: event.provider } : { provider: "flue" },
24359
+ ...event.provider ? { "flue.provider": event.provider } : {},
24360
+ ...event.purpose ? { "flue.turn_purpose": event.purpose } : {},
24361
+ ...event.reasoning ? { reasoning: event.reasoning } : {},
24362
+ ...event.input?.systemPrompt ? { "flue.system_prompt": event.input.systemPrompt } : {},
24363
+ ...event.input?.tools ? { tools: event.input.tools } : {}
24635
24364
  };
24365
+ const parent = this.parentSpanForTurn(event);
24636
24366
  const span = startFlueSpan(parent, {
24637
- name: "flue.turn",
24638
- spanAttributes: { type: "llm" /* LLM */ }
24367
+ name: `llm:${event.model ?? event.purpose ?? "unknown"}`,
24368
+ spanAttributes: { type: "llm" /* LLM */ },
24369
+ startTime: eventTime(event.timestamp),
24370
+ event: {
24371
+ input: event.input?.messages,
24372
+ metadata
24373
+ }
24639
24374
  });
24640
- const state = {
24641
- metadata,
24642
- span,
24643
- hasThinking: false,
24644
- startTime: getCurrentUnixTimestamp(),
24645
- text: [],
24646
- thinking: [],
24647
- toolCalls: []
24648
- };
24649
- safeLog3(span, { metadata });
24650
- this.turnsByScope.set(scope, state);
24651
- return state;
24375
+ this.logOperationInput(
24376
+ event.operationId,
24377
+ event.input?.messages ?? event.input
24378
+ );
24379
+ this.turnsByKey.set(key, { metadata, span });
24652
24380
  }
24653
24381
  handleTurn(event) {
24654
- const scope = scopeKey(event);
24655
- const state = this.ensureTurnState(event);
24656
- const text = state.text.join("");
24657
- const reasoning = state.finalThinking ?? state.thinking.join("");
24658
- const outputReasoning = reasoning || (state.hasThinking ? "[reasoning stream present; content unavailable]" : void 0);
24382
+ const key = turnKey(event);
24383
+ if (!key) {
24384
+ return;
24385
+ }
24386
+ const state = this.turnsByKey.get(key) ?? this.startSyntheticTurn(event);
24659
24387
  const metadata = {
24660
24388
  ...state.metadata,
24661
24389
  ...extractEventMetadata(event),
24390
+ ...event.api ? { "flue.api": event.api } : {},
24662
24391
  ...event.model ? { model: event.model, "flue.model": event.model } : {},
24392
+ ...event.provider ? { provider: event.provider } : {},
24393
+ ...event.provider ? { "flue.provider": event.provider } : {},
24394
+ ...event.purpose ? { "flue.turn_purpose": event.purpose } : {},
24663
24395
  ...event.stopReason ? { "flue.stop_reason": event.stopReason } : {},
24664
- ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {},
24665
- provider: "flue"
24396
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
24666
24397
  };
24667
24398
  safeLog3(state.span, {
24668
- ...event.error ? { error: errorToString(event.error) } : {},
24399
+ ...event.isError ? { error: errorToString(event.error) } : {},
24669
24400
  metadata,
24670
24401
  metrics: {
24671
- ...durationMsMetrics(event.durationMs),
24402
+ ...durationMetrics2(event.durationMs),
24672
24403
  ...metricsFromUsage(event.usage)
24673
24404
  },
24674
- output: toAssistantOutput(
24675
- text,
24676
- event.stopReason,
24677
- outputReasoning,
24678
- state.toolCalls
24679
- )
24405
+ output: event.output
24680
24406
  });
24681
- state.span.end();
24682
- this.turnsByScope.delete(scope);
24683
- }
24684
- handleThinkingDelta(event) {
24685
- const delta = event.delta;
24686
- if (typeof delta !== "string" || !delta) {
24687
- return;
24688
- }
24689
- const state = this.ensureTurnState(event);
24690
- state.hasThinking = true;
24691
- state.metadata["flue.thinking"] = true;
24692
- state.thinking.push(delta);
24693
- }
24694
- handleThinkingStart(event) {
24695
- const state = this.ensureTurnState(event);
24696
- state.hasThinking = true;
24697
- state.metadata["flue.thinking"] = true;
24698
- }
24699
- handleThinkingEnd(event) {
24700
- const state = this.ensureTurnState(event);
24701
- state.hasThinking = true;
24702
- state.metadata["flue.thinking"] = true;
24703
- if (typeof event.content === "string" && event.content) {
24704
- state.finalThinking = event.content;
24705
- }
24407
+ safeEnd(state.span, eventTime(event.timestamp));
24408
+ this.turnsByKey.delete(key);
24706
24409
  }
24707
- handleToolStart(event, options) {
24708
- const toolCallId = event.toolCallId;
24709
- if (!toolCallId) {
24410
+ handleToolStart(event) {
24411
+ if (!event.toolCallId) {
24710
24412
  return;
24711
24413
  }
24712
- const parent = this.parentSpanForEvent(event);
24713
- const scope = scopeKey(event);
24714
- let turnState = this.turnsByScope.get(scope);
24715
- if (!turnState && parent && options.captureTurnSpans) {
24716
- turnState = this.ensureTurnState(event);
24717
- }
24718
24414
  const metadata = {
24719
24415
  ...extractEventMetadata(event),
24720
24416
  ...event.toolName ? { "flue.tool_name": event.toolName } : {},
24721
- "flue.tool_call_id": toolCallId,
24417
+ "flue.tool_call_id": event.toolCallId,
24722
24418
  provider: "flue"
24723
24419
  };
24420
+ const parent = this.parentSpanForTool(event);
24724
24421
  const span = startFlueSpan(parent, {
24725
- name: `tool: ${event.toolName ?? "unknown"}`,
24726
- spanAttributes: { type: "tool" /* TOOL */ }
24727
- });
24728
- if (turnState) {
24729
- turnState.toolCalls.push({
24730
- args: event.args,
24731
- toolCallId,
24732
- toolName: event.toolName
24733
- });
24734
- }
24735
- safeLog3(span, {
24736
- input: event.args,
24737
- metadata
24738
- });
24739
- this.toolsById.set(toolKey(event), {
24740
- metadata,
24741
- span,
24742
- startTime: getCurrentUnixTimestamp()
24422
+ name: `tool:${event.toolName ?? "unknown"}`,
24423
+ spanAttributes: { type: "tool" /* TOOL */ },
24424
+ startTime: eventTime(event.timestamp),
24425
+ event: {
24426
+ input: event.args,
24427
+ metadata
24428
+ }
24743
24429
  });
24430
+ this.toolsByKey.set(toolKey(event), { metadata, span });
24744
24431
  }
24745
24432
  handleToolCall(event) {
24433
+ if (!event.toolCallId) {
24434
+ return;
24435
+ }
24746
24436
  const key = toolKey(event);
24747
- const state = this.toolsById.get(key) ?? this.startSyntheticToolState(event, event.toolName ?? "unknown");
24437
+ const state = this.toolsByKey.get(key) ?? this.startSyntheticTool(event);
24748
24438
  const metadata = {
24749
24439
  ...state.metadata,
24750
24440
  ...extractEventMetadata(event),
24751
24441
  ...event.toolName ? { "flue.tool_name": event.toolName } : {},
24752
- ...event.toolCallId ? { "flue.tool_call_id": event.toolCallId } : {},
24442
+ "flue.tool_call_id": event.toolCallId,
24753
24443
  ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
24754
24444
  };
24755
24445
  safeLog3(state.span, {
24756
24446
  ...event.isError ? { error: errorToString(event.result) } : {},
24757
24447
  metadata,
24758
- metrics: durationMsMetrics(event.durationMs),
24448
+ metrics: durationMetrics2(event.durationMs),
24759
24449
  output: event.result
24760
24450
  });
24761
- state.span.end();
24762
- this.toolsById.delete(key);
24451
+ safeEnd(state.span, eventTime(event.timestamp));
24452
+ this.toolsByKey.delete(key);
24763
24453
  }
24764
24454
  handleTaskStart(event) {
24765
- const parent = this.parentSpanForEvent(event);
24455
+ if (!event.taskId) {
24456
+ return;
24457
+ }
24766
24458
  const metadata = {
24767
24459
  ...extractEventMetadata(event),
24768
- ...event.role ? { "flue.role": event.role } : {},
24460
+ ...event.agent ? { "flue.agent": event.agent } : {},
24769
24461
  ...event.cwd ? { "flue.cwd": event.cwd } : {},
24770
24462
  "flue.task_id": event.taskId,
24771
24463
  provider: "flue"
24772
24464
  };
24465
+ const parent = this.parentSpanForEvent(event);
24773
24466
  const span = startFlueSpan(parent, {
24774
- name: "flue.task",
24775
- spanAttributes: { type: "task" /* TASK */ }
24776
- });
24777
- safeLog3(span, {
24778
- input: event.prompt,
24779
- metadata
24780
- });
24781
- this.tasksById.set(event.taskId, {
24782
- metadata,
24783
- span,
24784
- startTime: getCurrentUnixTimestamp()
24467
+ name: event.agent ? `task:${event.agent}` : "flue.task",
24468
+ spanAttributes: { type: "task" /* TASK */ },
24469
+ startTime: eventTime(event.timestamp),
24470
+ event: {
24471
+ input: event.prompt,
24472
+ metadata
24473
+ }
24785
24474
  });
24475
+ this.tasksById.set(event.taskId, { metadata, span });
24786
24476
  }
24787
24477
  handleTask(event) {
24788
24478
  const state = this.tasksById.get(event.taskId);
@@ -24794,426 +24484,372 @@ var FluePlugin = class extends BasePlugin {
24794
24484
  metadata: {
24795
24485
  ...state.metadata,
24796
24486
  ...extractEventMetadata(event),
24487
+ ...event.agent ? { "flue.agent": event.agent } : {},
24797
24488
  ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
24798
24489
  },
24799
- metrics: durationMsMetrics(event.durationMs),
24490
+ metrics: durationMetrics2(event.durationMs),
24800
24491
  output: event.result
24801
24492
  });
24802
- state.span.end();
24493
+ safeEnd(state.span, eventTime(event.timestamp));
24803
24494
  this.tasksById.delete(event.taskId);
24804
24495
  }
24805
24496
  handleCompactionStart(event) {
24806
- const operationState = this.operationStateForEvent(event);
24807
- const parent = operationState?.span ?? this.parentSpanForEvent(event);
24497
+ const key = compactionKey(event);
24498
+ const input = {
24499
+ ...event.estimatedTokens !== void 0 ? { estimatedTokens: event.estimatedTokens } : {},
24500
+ ...event.reason ? { reason: event.reason } : {}
24501
+ };
24808
24502
  const metadata = {
24809
24503
  ...extractEventMetadata(event),
24810
24504
  ...event.reason ? { "flue.compaction_reason": event.reason } : {},
24811
24505
  provider: "flue"
24812
24506
  };
24813
- const input = {
24814
- ...typeof event.estimatedTokens === "number" ? { estimatedTokens: event.estimatedTokens } : {},
24815
- ...event.reason ? { reason: event.reason } : {}
24816
- };
24507
+ const parent = this.parentSpanForEvent(event);
24817
24508
  const span = startFlueSpan(parent, {
24818
- name: "flue.compaction",
24819
- spanAttributes: { type: "task" /* TASK */ }
24820
- });
24821
- safeLog3(span, {
24822
- input,
24823
- metadata
24824
- });
24825
- this.compactionsByScope.set(scopeKey(event), {
24826
- input,
24827
- metadata,
24828
- operationState,
24829
- span,
24830
- startTime: getCurrentUnixTimestamp()
24509
+ name: `compaction:${event.reason ?? "unknown"}`,
24510
+ spanAttributes: { type: "task" /* TASK */ },
24511
+ startTime: eventTime(event.timestamp),
24512
+ event: {
24513
+ input,
24514
+ metadata
24515
+ }
24831
24516
  });
24517
+ this.logOperationInput(event.operationId, input);
24518
+ this.compactionsByKey.set(key, { metadata, span });
24832
24519
  }
24833
24520
  handleCompaction(event) {
24834
- const key = scopeKey(event);
24835
- const state = this.compactionsByScope.get(key) ?? this.findCompactionState(event);
24836
- if (!state) {
24837
- return;
24838
- }
24521
+ const key = compactionKey(event);
24522
+ const state = this.compactionsByKey.get(key) ?? this.startSyntheticCompaction(event);
24523
+ const metadata = {
24524
+ ...state.metadata,
24525
+ ...extractEventMetadata(event),
24526
+ ...event.usage ? { "flue.usage": event.usage } : {}
24527
+ };
24839
24528
  safeLog3(state.span, {
24840
- metadata: {
24841
- ...state.metadata,
24842
- ...extractEventMetadata(event),
24843
- ...typeof event.messagesBefore === "number" ? { "flue.messages_before": event.messagesBefore } : {},
24844
- ...typeof event.messagesAfter === "number" ? { "flue.messages_after": event.messagesAfter } : {}
24845
- },
24529
+ metadata,
24846
24530
  metrics: {
24847
- ...durationMsMetrics(event.durationMs),
24848
- ...metricsFromUsage(event.usage)
24531
+ ...durationMetrics2(event.durationMs),
24532
+ ...typeof event.messagesBefore === "number" ? { messages_before: event.messagesBefore } : {},
24533
+ ...typeof event.messagesAfter === "number" ? { messages_after: event.messagesAfter } : {}
24849
24534
  },
24850
24535
  output: {
24851
24536
  messagesAfter: event.messagesAfter,
24852
24537
  messagesBefore: event.messagesBefore
24853
24538
  }
24854
24539
  });
24855
- state.span.end();
24856
- this.deleteCompactionState(state);
24540
+ safeEnd(state.span, eventTime(event.timestamp));
24541
+ this.compactionsByKey.delete(key);
24857
24542
  }
24858
- findCompactionState(event) {
24859
- const operationState = this.operationStateForEvent(event);
24860
- for (const state of this.compactionsByScope.values()) {
24861
- if (operationState && state.operationState === operationState) {
24862
- return state;
24543
+ parentSpanForTurn(event) {
24544
+ if (event.purpose === "compaction" || event.purpose === "compaction_prefix") {
24545
+ const compaction = this.compactionsByKey.get(compactionKey(event));
24546
+ if (compaction) {
24547
+ return compaction.span;
24863
24548
  }
24864
24549
  }
24865
- return void 0;
24550
+ return this.parentSpanForEvent(event);
24866
24551
  }
24867
- finishCompactionsForOperation(operationState) {
24868
- for (const state of [...this.compactionsByScope.values()]) {
24869
- if (state.operationState !== operationState) {
24870
- continue;
24552
+ parentSpanForEvent(event) {
24553
+ const turn = turnKey(event);
24554
+ if (turn) {
24555
+ const turnState = this.turnsByKey.get(turn);
24556
+ if (turnState) {
24557
+ return turnState.span;
24871
24558
  }
24872
- safeLog3(state.span, {
24873
- input: state.input,
24874
- metadata: state.metadata,
24875
- metrics: {
24876
- ...buildDurationMetrics3(state.startTime)
24877
- },
24878
- output: { completed: true }
24879
- });
24880
- state.span.end();
24881
- this.deleteCompactionState(state);
24882
24559
  }
24883
- }
24884
- deleteCompactionState(state) {
24885
- for (const [key, candidate] of this.compactionsByScope) {
24886
- if (candidate !== state) {
24887
- continue;
24560
+ if (event.taskId) {
24561
+ const task = this.tasksById.get(event.taskId);
24562
+ if (task) {
24563
+ return task.span;
24888
24564
  }
24889
- this.compactionsByScope.delete(key);
24890
- return;
24891
24565
  }
24892
- }
24893
- startSyntheticToolState(event, toolName) {
24894
- const parent = this.parentSpanForEvent(event);
24895
- const metadata = {
24896
- ...extractEventMetadata(event),
24897
- ...event.toolCallId ? { "flue.tool_call_id": event.toolCallId } : {},
24898
- "flue.tool_name": toolName,
24899
- provider: "flue"
24900
- };
24901
- const span = startFlueSpan(parent, {
24902
- name: `tool: ${toolName}`,
24903
- spanAttributes: { type: "tool" /* TOOL */ }
24904
- });
24905
- safeLog3(span, { metadata });
24906
- return { metadata, span, startTime: getCurrentUnixTimestamp() };
24907
- }
24908
- operationStateForEvent(event) {
24909
24566
  if (event.operationId) {
24910
- const operation = this.activeOperationsById.get(event.operationId) ?? this.promotePendingOperationForEvent(event);
24567
+ const operation = this.operationsById.get(event.operationId);
24911
24568
  if (operation) {
24912
- return operation;
24569
+ return operation.span;
24913
24570
  }
24914
24571
  }
24915
- return this.activeOperationForEventScope(event) ?? this.pendingOperationForEventScope(event);
24572
+ if (event.runId) {
24573
+ return this.runsById.get(event.runId)?.span;
24574
+ }
24575
+ return void 0;
24916
24576
  }
24917
- parentSpanForEvent(event) {
24577
+ parentSpanForTool(event) {
24578
+ if (event.taskId) {
24579
+ const task = this.tasksById.get(event.taskId);
24580
+ if (task) {
24581
+ return task.span;
24582
+ }
24583
+ }
24918
24584
  if (event.operationId) {
24919
- const operation = this.operationStateForEvent(event);
24585
+ const operation = this.operationsById.get(event.operationId);
24920
24586
  if (operation) {
24921
24587
  return operation.span;
24922
24588
  }
24923
24589
  }
24924
- if (event.taskId) {
24925
- return this.tasksById.get(event.taskId)?.span;
24590
+ if (event.runId) {
24591
+ return this.runsById.get(event.runId)?.span;
24926
24592
  }
24927
- return this.operationStateForEvent(event)?.span;
24593
+ return void 0;
24928
24594
  }
24929
- promotePendingOperationForEvent(event) {
24930
- if (!event.operationId) {
24931
- return void 0;
24595
+ logOperationInput(operationId, input) {
24596
+ if (!operationId || input === void 0) {
24597
+ return;
24598
+ }
24599
+ const operation = this.operationsById.get(operationId);
24600
+ if (!operation || operation.loggedInput) {
24601
+ return;
24602
+ }
24603
+ safeLog3(operation.span, { input });
24604
+ operation.loggedInput = true;
24605
+ }
24606
+ startSyntheticOperation(event) {
24607
+ const metadata = {
24608
+ ...extractEventMetadata(event),
24609
+ "flue.operation": event.operationKind,
24610
+ provider: "flue"
24611
+ };
24612
+ const span = startFlueSpan(this.parentSpanForEvent(event), {
24613
+ name: `flue.${event.operationKind}`,
24614
+ spanAttributes: { type: "task" /* TASK */ },
24615
+ startTime: eventTime(event.timestamp),
24616
+ event: { metadata }
24617
+ });
24618
+ return { metadata, span };
24619
+ }
24620
+ startSyntheticTurn(event) {
24621
+ const metadata = {
24622
+ ...extractEventMetadata(event),
24623
+ ...event.api ? { "flue.api": event.api } : {},
24624
+ ...event.model ? { model: event.model, "flue.model": event.model } : {},
24625
+ ...event.provider ? { provider: event.provider } : { provider: "flue" },
24626
+ ...event.provider ? { "flue.provider": event.provider } : {},
24627
+ ...event.purpose ? { "flue.turn_purpose": event.purpose } : {}
24628
+ };
24629
+ const span = startFlueSpan(this.parentSpanForEvent(event), {
24630
+ name: `llm:${event.model ?? event.purpose ?? "unknown"}`,
24631
+ spanAttributes: { type: "llm" /* LLM */ },
24632
+ startTime: eventTime(event.timestamp),
24633
+ event: { metadata }
24634
+ });
24635
+ return { metadata, span };
24636
+ }
24637
+ startSyntheticTool(event) {
24638
+ const metadata = {
24639
+ ...extractEventMetadata(event),
24640
+ ...event.toolName ? { "flue.tool_name": event.toolName } : {},
24641
+ "flue.tool_call_id": event.toolCallId,
24642
+ provider: "flue"
24643
+ };
24644
+ const span = startFlueSpan(this.parentSpanForTool(event), {
24645
+ name: `tool:${event.toolName ?? "unknown"}`,
24646
+ spanAttributes: { type: "tool" /* TOOL */ },
24647
+ startTime: eventTime(event.timestamp),
24648
+ event: { metadata }
24649
+ });
24650
+ return { metadata, span };
24651
+ }
24652
+ startSyntheticCompaction(event) {
24653
+ const metadata = {
24654
+ ...extractEventMetadata(event),
24655
+ provider: "flue"
24656
+ };
24657
+ const span = startFlueSpan(this.parentSpanForEvent(event), {
24658
+ name: "compaction:unknown",
24659
+ spanAttributes: { type: "task" /* TASK */ },
24660
+ startTime: eventTime(event.timestamp),
24661
+ event: { metadata }
24662
+ });
24663
+ return { metadata, span };
24664
+ }
24665
+ finishPendingChildrenForOperation(event, operationOutput2) {
24666
+ const endTime = eventTime(event.timestamp);
24667
+ const usage = event.usage ?? usageFromOperationResult(event.result);
24668
+ const turnEntries = [...this.turnsByKey].filter(
24669
+ ([, state]) => stateMatchesOperation(state, event.operationId)
24670
+ );
24671
+ turnEntries.forEach(([key, state], index) => {
24672
+ const shouldLogOperationOutput = (event.operationKind === "prompt" || event.operationKind === "skill") && index === turnEntries.length - 1 && operationOutput2 !== void 0;
24673
+ safeLog3(state.span, {
24674
+ metadata: state.metadata,
24675
+ metrics: metricsFromUsage(usage),
24676
+ ...shouldLogOperationOutput ? { output: operationOutput2 } : {}
24677
+ });
24678
+ safeEnd(state.span, endTime);
24679
+ this.turnsByKey.delete(key);
24680
+ });
24681
+ for (const [key, state] of this.toolsByKey) {
24682
+ if (!stateMatchesOperation(state, event.operationId)) {
24683
+ continue;
24684
+ }
24685
+ safeEnd(state.span, endTime);
24686
+ this.toolsByKey.delete(key);
24932
24687
  }
24933
- const scopePrefixes = operationScopePrefixes(event);
24934
- for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
24935
- if (!candidateQueue.length || !operationKeyMatchesScopes(candidateKey, scopePrefixes)) {
24688
+ for (const [key, state] of this.tasksById) {
24689
+ if (!stateMatchesOperation(state, event.operationId)) {
24936
24690
  continue;
24937
24691
  }
24938
- const state = candidateQueue.shift();
24939
- if (!state) {
24940
- return void 0;
24692
+ safeEnd(state.span, endTime);
24693
+ this.tasksById.delete(key);
24694
+ }
24695
+ for (const [key, state] of this.compactionsByKey) {
24696
+ if (!stateMatchesOperation(state, event.operationId)) {
24697
+ continue;
24941
24698
  }
24942
- state.operationId = event.operationId;
24943
- this.activeOperationsById.set(event.operationId, state);
24944
- addScopedOperation(this.activeOperationsByScope, event, state);
24945
- state.metadata = {
24946
- ...state.metadata,
24947
- ...extractEventMetadata(event),
24948
- "flue.operation_id": event.operationId
24949
- };
24950
- safeLog3(state.span, { metadata: state.metadata });
24951
- return state;
24699
+ safeLog3(state.span, {
24700
+ metadata: state.metadata,
24701
+ metrics: durationMetrics2(event.durationMs),
24702
+ output: { completed: true }
24703
+ });
24704
+ safeEnd(state.span, eventTime(event.timestamp));
24705
+ this.compactionsByKey.delete(key);
24952
24706
  }
24953
- return void 0;
24954
24707
  }
24955
- activeOperationForEventScope(event) {
24956
- for (const scope of operationScopeNames(event)) {
24957
- const operations = this.activeOperationsByScope.get(scope);
24958
- if (operations?.length) {
24959
- return operations[operations.length - 1];
24708
+ finishPendingSpansForRun(event) {
24709
+ const endTime = eventTime(event.timestamp);
24710
+ for (const [key, state] of this.toolsByKey) {
24711
+ if (!stateMatchesRun(state, event.runId)) {
24712
+ continue;
24960
24713
  }
24714
+ safeEnd(state.span, endTime);
24715
+ this.toolsByKey.delete(key);
24961
24716
  }
24962
- return void 0;
24963
- }
24964
- pendingOperationForEventScope(event) {
24965
- const scopePrefixes = operationScopePrefixes(event);
24966
- for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
24967
- if (!candidateQueue.length || !operationKeyMatchesScopes(candidateKey, scopePrefixes)) {
24717
+ for (const [key, state] of this.turnsByKey) {
24718
+ if (!stateMatchesRun(state, event.runId)) {
24968
24719
  continue;
24969
24720
  }
24970
- return candidateQueue[0];
24721
+ safeEnd(state.span, endTime);
24722
+ this.turnsByKey.delete(key);
24971
24723
  }
24972
- return void 0;
24973
- }
24974
- takePendingOperationForEvent(event) {
24975
- const key = operationKey(event.session, event.operationKind);
24976
- const queue2 = this.pendingOperationsByKey.get(key);
24977
- if (queue2?.length) {
24978
- return queue2.shift();
24724
+ for (const [key, state] of this.tasksById) {
24725
+ if (!stateMatchesRun(state, event.runId)) {
24726
+ continue;
24727
+ }
24728
+ safeEnd(state.span, endTime);
24729
+ this.tasksById.delete(key);
24979
24730
  }
24980
- for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
24981
- if (candidateKey.endsWith(`::${event.operationKind}`) && candidateQueue.length) {
24982
- return candidateQueue.shift();
24731
+ for (const [key, state] of this.compactionsByKey) {
24732
+ if (!stateMatchesRun(state, event.runId)) {
24733
+ continue;
24983
24734
  }
24735
+ safeLog3(state.span, {
24736
+ metadata: state.metadata,
24737
+ output: { completed: true }
24738
+ });
24739
+ safeEnd(state.span, endTime);
24740
+ this.compactionsByKey.delete(key);
24984
24741
  }
24985
- return void 0;
24986
- }
24987
- pendingOperationQueue(key) {
24988
- const existing = this.pendingOperationsByKey.get(key);
24989
- if (existing) {
24990
- return existing;
24742
+ for (const [key, state] of this.operationsById) {
24743
+ if (!stateMatchesRun(state, event.runId)) {
24744
+ continue;
24745
+ }
24746
+ safeLog3(state.span, {
24747
+ metadata: state.metadata,
24748
+ ...state.metadata["flue.operation"] === "compact" ? { output: { completed: true } } : {}
24749
+ });
24750
+ safeEnd(state.span, endTime);
24751
+ this.operationsById.delete(key);
24991
24752
  }
24992
- const queue2 = [];
24993
- this.pendingOperationsByKey.set(key, queue2);
24994
- return queue2;
24995
24753
  }
24996
24754
  };
24997
24755
  function isInstrumentedOperation(operation) {
24998
- return operation === "prompt" || operation === "skill" || operation === "task" || operation === "compact";
24999
- }
25000
- function getSessionName(session) {
25001
- return typeof session?.name === "string" ? session.name : void 0;
25002
- }
25003
- function operationKey(sessionName, operation) {
25004
- return `${sessionName ?? "unknown"}::${operation}`;
25005
- }
25006
- function operationScopePrefixes(event) {
25007
- const scopes = /* @__PURE__ */ new Set();
25008
- for (const scope of operationScopeNames(event)) {
25009
- scopes.add(`${scope}::`);
25010
- }
25011
- return scopes;
25012
- }
25013
- function operationKeyMatchesScopes(key, scopes) {
25014
- for (const scope of scopes) {
25015
- if (key.startsWith(scope)) {
25016
- return true;
25017
- }
25018
- }
25019
- return false;
24756
+ return operation === "prompt" || operation === "skill" || operation === "compact";
25020
24757
  }
25021
- function operationScopeNames(event) {
25022
- const scopes = /* @__PURE__ */ new Set();
25023
- if (event.session) {
25024
- scopes.add(event.session);
25025
- }
25026
- if (event.parentSession) {
25027
- scopes.add(event.parentSession);
25028
- }
25029
- if (!scopes.size) {
25030
- scopes.add("unknown");
25031
- }
25032
- return scopes;
25033
- }
25034
- function addScopedOperation(operationsByScope, event, state) {
25035
- for (const scope of operationScopeNames(event)) {
25036
- addOperationToScope(operationsByScope, scope, state);
25037
- }
25038
- }
25039
- function addOperationToScope(operationsByScope, scope, state) {
25040
- const operations = operationsByScope.get(scope);
25041
- if (operations) {
25042
- if (!operations.includes(state)) {
25043
- operations.push(state);
25044
- }
25045
- } else {
25046
- operationsByScope.set(scope, [state]);
25047
- }
25048
- }
25049
- function removeScopedOperation(operationsByScope, state) {
25050
- for (const [scope, operations] of operationsByScope) {
25051
- const index = operations.indexOf(state);
25052
- if (index === -1) {
25053
- continue;
25054
- }
25055
- operations.splice(index, 1);
25056
- if (operations.length === 0) {
25057
- operationsByScope.delete(scope);
25058
- }
25059
- }
25060
- }
25061
- function removePendingOperation(pendingOperationsByKey, state) {
25062
- for (const [key, queue2] of pendingOperationsByKey) {
25063
- const index = queue2.indexOf(state);
25064
- if (index === -1) {
25065
- continue;
25066
- }
25067
- queue2.splice(index, 1);
25068
- if (queue2.length === 0) {
25069
- pendingOperationsByKey.delete(key);
25070
- }
25071
- return;
25072
- }
25073
- }
25074
- function extractSessionMetadata(session) {
25075
- const sessionName = getSessionName(session);
25076
- return sessionName ? { "flue.session": sessionName } : {};
25077
- }
25078
- function extractEventMetadata(event) {
24758
+ function extractEventMetadata(event, ctx) {
25079
24759
  return {
25080
24760
  ...event.runId ? { "flue.run_id": event.runId } : {},
24761
+ ...event.instanceId ? { "flue.instance_id": event.instanceId } : {},
24762
+ ...event.dispatchId ? { "flue.dispatch_id": event.dispatchId } : {},
25081
24763
  ...typeof event.eventIndex === "number" ? { "flue.event_index": event.eventIndex } : {},
25082
24764
  ...event.session ? { "flue.session": event.session } : {},
25083
24765
  ...event.parentSession ? { "flue.parent_session": event.parentSession } : {},
25084
24766
  ...event.harness ? { "flue.harness": event.harness } : {},
25085
24767
  ...event.taskId ? { "flue.task_id": event.taskId } : {},
25086
- ...event.operationId ? { "flue.operation_id": event.operationId } : {}
24768
+ ...event.operationId ? { "flue.operation_id": event.operationId } : {},
24769
+ ...event.turnId ? { "flue.turn_id": event.turnId } : {},
24770
+ ...typeof ctx?.id === "string" ? { "flue.context_id": ctx.id } : {},
24771
+ ...typeof ctx?.runId === "string" ? { "flue.context_run_id": ctx.runId } : {}
25087
24772
  };
25088
24773
  }
25089
- function extractOperationInput(operation, args) {
25090
- switch (operation) {
25091
- case "prompt":
25092
- case "task":
25093
- return args[0];
25094
- case "skill":
25095
- return {
25096
- args: getOptionObject(args[1])?.args,
25097
- name: args[0]
25098
- };
25099
- case "compact":
25100
- return void 0;
24774
+ function extractPayloadMetadata(payload) {
24775
+ if (!isObjectLike(payload)) {
24776
+ return {};
25101
24777
  }
24778
+ const metadata = Reflect.get(payload, "metadata");
24779
+ if (!isObjectLike(metadata)) {
24780
+ return {};
24781
+ }
24782
+ return Object.fromEntries(Object.entries(metadata));
25102
24783
  }
25103
- function extractOperationInputMetadata(operation, args) {
25104
- const options = getOptionObject(args[1]);
25105
- return {
25106
- ...operation === "skill" && typeof args[0] === "string" ? { "flue.skill_name": args[0] } : {},
25107
- ...options?.model ? { model: options.model, "flue.model": options.model } : {},
25108
- ...options?.role ? { "flue.role": options.role } : {},
25109
- ...options?.thinkingLevel ? { "flue.thinking_level": options.thinkingLevel } : {},
25110
- ...typeof options?.cwd === "string" ? { "flue.cwd": options.cwd } : {},
25111
- ...Array.isArray(options?.tools) ? {
25112
- "flue.tools_count": options.tools.length,
25113
- tools: summarizeTools(options.tools)
25114
- } : {},
25115
- ...Array.isArray(options?.images) ? { "flue.images_count": options.images.length } : {},
25116
- ...options?.result || options?.schema ? { "flue.result_schema": true } : {}
25117
- };
25118
- }
25119
- function getOptionObject(value) {
25120
- return isObject(value) ? value : void 0;
25121
- }
25122
- function summarizeTools(tools) {
25123
- return tools.flatMap((tool) => {
25124
- if (!isObject(tool)) {
25125
- return [];
25126
- }
25127
- const name = typeof tool.name === "string" ? tool.name : void 0;
25128
- if (!name) {
25129
- return [];
25130
- }
25131
- return [
25132
- {
25133
- function: {
25134
- description: typeof tool.description === "string" ? tool.description : void 0,
25135
- name,
25136
- parameters: tool.parameters
25137
- },
25138
- type: "function"
25139
- }
25140
- ];
25141
- });
24784
+ function operationOutput(event) {
24785
+ if (event.operationKind === "prompt" || event.operationKind === "skill") {
24786
+ return llmResultFromOperationResult(event.result);
24787
+ }
24788
+ return event.result ?? (event.operationKind === "compact" ? { completed: true } : void 0);
25142
24789
  }
25143
- function extractPromptResponseMetadata(result) {
25144
- const modelId = result?.model && typeof result.model.id === "string" ? result.model.id : void 0;
25145
- return modelId ? {
25146
- model: modelId,
25147
- "flue.model": modelId
25148
- } : {};
24790
+ function llmResultFromOperationResult(result) {
24791
+ if (!isObjectLike(result)) {
24792
+ return result;
24793
+ }
24794
+ const text = Reflect.get(result, "text");
24795
+ return text === void 0 ? result : text;
25149
24796
  }
25150
- function extractOperationOutput(result) {
25151
- if (!result) {
24797
+ function usageFromOperationResult(result) {
24798
+ if (!isObjectLike(result)) {
25152
24799
  return void 0;
25153
24800
  }
25154
- if ("data" in result) {
25155
- return result.data;
25156
- }
25157
- if ("text" in result) {
25158
- return result.text;
25159
- }
25160
- return result;
24801
+ return Reflect.get(result, "usage");
25161
24802
  }
25162
24803
  function metricsFromUsage(usage) {
24804
+ if (!isObjectLike(usage)) {
24805
+ return {};
24806
+ }
24807
+ const cacheRead = Reflect.get(usage, "cacheRead");
24808
+ const cacheWrite = Reflect.get(usage, "cacheWrite");
24809
+ const cost = Reflect.get(usage, "cost");
24810
+ const input = Reflect.get(usage, "input");
24811
+ const output = Reflect.get(usage, "output");
24812
+ const totalTokens = Reflect.get(usage, "totalTokens");
24813
+ const totalCost = isObjectLike(cost) ? Reflect.get(cost, "total") : void 0;
25163
24814
  return {
25164
- ...typeof usage?.input === "number" ? { prompt_tokens: usage.input } : {},
25165
- ...typeof usage?.output === "number" ? { completion_tokens: usage.output } : {},
25166
- ...typeof usage?.cacheRead === "number" ? { prompt_cached_tokens: usage.cacheRead } : {},
25167
- ...typeof usage?.cacheWrite === "number" ? { prompt_cache_creation_tokens: usage.cacheWrite } : {},
25168
- ...typeof usage?.totalTokens === "number" ? { tokens: usage.totalTokens } : {},
25169
- ...typeof usage?.cost?.total === "number" ? { estimated_cost: usage.cost.total } : {}
25170
- };
25171
- }
25172
- function buildDurationMetrics3(startTime) {
25173
- return {
25174
- duration_ms: Math.max(0, (getCurrentUnixTimestamp() - startTime) * 1e3)
24815
+ ...typeof input === "number" ? { prompt_tokens: input } : {},
24816
+ ...typeof output === "number" ? { completion_tokens: output } : {},
24817
+ ...typeof cacheRead === "number" ? { prompt_cached_tokens: cacheRead } : {},
24818
+ ...typeof cacheWrite === "number" ? { prompt_cache_creation_tokens: cacheWrite } : {},
24819
+ ...typeof totalTokens === "number" ? { tokens: totalTokens } : {},
24820
+ ...typeof totalCost === "number" ? { estimated_cost: totalCost } : {}
25175
24821
  };
25176
24822
  }
25177
- function durationMsMetrics(durationMs) {
24823
+ function durationMetrics2(durationMs) {
25178
24824
  return typeof durationMs === "number" ? { duration_ms: durationMs } : {};
25179
24825
  }
25180
- function scopeKey(event) {
25181
- if (event.operationId) {
25182
- return `operation:${event.operationId}`;
25183
- }
25184
- if (event.taskId) {
25185
- return `task:${event.taskId}`;
25186
- }
25187
- if (event.session) {
25188
- return `session:${event.session}`;
24826
+ function eventTime(value) {
24827
+ if (typeof value !== "string") {
24828
+ return void 0;
25189
24829
  }
25190
- return "flue:unknown";
24830
+ const timestamp = Date.parse(value);
24831
+ return Number.isFinite(timestamp) ? timestamp / 1e3 : void 0;
24832
+ }
24833
+ function turnKey(event) {
24834
+ return event.turnId;
25191
24835
  }
25192
24836
  function toolKey(event) {
25193
- return `${scopeKey(event)}::tool:${event.toolCallId ?? "unknown"}`;
24837
+ return `${event.turnId ?? event.operationId ?? event.taskId ?? event.runId ?? "unknown"}:${event.toolCallId ?? "unknown"}`;
25194
24838
  }
25195
- function toAssistantOutput(text, finishReason, reasoning, toolCalls) {
24839
+ function compactionKey(event) {
25196
24840
  return [
25197
- {
25198
- finish_reason: finishReason ?? "stop",
25199
- index: 0,
25200
- message: {
25201
- content: text,
25202
- ...reasoning ? { reasoning } : {},
25203
- role: "assistant",
25204
- ...toolCalls?.length ? {
25205
- tool_calls: toolCalls.map((toolCall) => ({
25206
- function: {
25207
- arguments: toolCall.args === void 0 ? "{}" : JSON.stringify(toolCall.args),
25208
- name: toolCall.toolName ?? "unknown"
25209
- },
25210
- ...toolCall.toolCallId ? { id: toolCall.toolCallId } : {},
25211
- type: "function"
25212
- }))
25213
- } : {}
25214
- }
25215
- }
25216
- ];
24841
+ event.instanceId ?? "",
24842
+ event.runId ?? "",
24843
+ event.session ?? "",
24844
+ event.operationId ?? "",
24845
+ event.taskId ?? ""
24846
+ ].join(":");
24847
+ }
24848
+ function stateMatchesOperation(state, operationId) {
24849
+ return state.metadata["flue.operation_id"] === operationId;
24850
+ }
24851
+ function stateMatchesRun(state, runId) {
24852
+ return state.metadata["flue.run_id"] === runId;
25217
24853
  }
25218
24854
  function startFlueSpan(parent, args) {
25219
24855
  return parent ? withCurrent(parent, () => startSpan(args)) : startSpan(args);
@@ -25225,6 +24861,13 @@ function safeLog3(span, event) {
25225
24861
  logInstrumentationError3("Flue span log", error);
25226
24862
  }
25227
24863
  }
24864
+ function safeEnd(span, endTime) {
24865
+ try {
24866
+ span.end(endTime === void 0 ? void 0 : { endTime });
24867
+ } catch (error) {
24868
+ logInstrumentationError3("Flue span end", error);
24869
+ }
24870
+ }
25228
24871
  function errorToString(error) {
25229
24872
  if (error instanceof Error) {
25230
24873
  return error.message;
@@ -25718,7 +25361,7 @@ var BraintrustPlugin = class extends BasePlugin {
25718
25361
  this.config = config;
25719
25362
  }
25720
25363
  onEnable() {
25721
- const integrations = this.config.integrations || {};
25364
+ const integrations = this.config.integrations ?? {};
25722
25365
  if (integrations.openai !== false) {
25723
25366
  this.openaiPlugin = new OpenAIPlugin();
25724
25367
  this.openaiPlugin.enable();
@@ -25783,7 +25426,7 @@ var BraintrustPlugin = class extends BasePlugin {
25783
25426
  this.genkitPlugin = new GenkitPlugin();
25784
25427
  this.genkitPlugin.enable();
25785
25428
  }
25786
- if (getIntegrationConfig(integrations, "gitHubCopilot") !== false) {
25429
+ if (integrations.gitHubCopilot !== false) {
25787
25430
  this.gitHubCopilotPlugin = new GitHubCopilotPlugin();
25788
25431
  this.gitHubCopilotPlugin.enable();
25789
25432
  }
@@ -25896,6 +25539,7 @@ var envIntegrationAliases = {
25896
25539
  cursorsdk: "cursorSDK",
25897
25540
  flue: "flue",
25898
25541
  "flue-runtime": "flue",
25542
+ mastra: "mastra",
25899
25543
  "openai-agents": "openAIAgents",
25900
25544
  openaiagents: "openAIAgents",
25901
25545
  "openai-agents-core": "openAIAgents",
@@ -25938,6 +25582,7 @@ function getDefaultInstrumentationIntegrations() {
25938
25582
  cursor: true,
25939
25583
  cursorSDK: true,
25940
25584
  flue: true,
25585
+ mastra: true,
25941
25586
  openAIAgents: true,
25942
25587
  openrouter: true,
25943
25588
  openrouterAgent: true,
@@ -26110,6 +25755,7 @@ __export(exports_exports, {
26110
25755
  BaseExperiment: () => BaseExperiment,
26111
25756
  BraintrustLangChainCallbackHandler: () => BraintrustLangChainCallbackHandler,
26112
25757
  BraintrustMiddleware: () => BraintrustMiddleware,
25758
+ BraintrustObservabilityExporter: () => BraintrustObservabilityExporter,
26113
25759
  BraintrustState: () => BraintrustState,
26114
25760
  BraintrustStream: () => BraintrustStream,
26115
25761
  CachedSpanFetcher: () => CachedSpanFetcher,
@@ -26156,6 +25802,7 @@ __export(exports_exports, {
26156
25802
  _internalIso: () => isomorph_default,
26157
25803
  _internalSetInitialState: () => _internalSetInitialState,
26158
25804
  addAzureBlobHeaders: () => addAzureBlobHeaders,
25805
+ braintrustFlueObserver: () => braintrustFlueObserver,
26159
25806
  braintrustStreamChunkSchema: () => braintrustStreamChunkSchema,
26160
25807
  buildLocalSummary: () => buildLocalSummary,
26161
25808
  configureInstrumentation: () => configureInstrumentation,
@@ -26235,8 +25882,6 @@ __export(exports_exports, {
26235
25882
  wrapCohere: () => wrapCohere,
26236
25883
  wrapCopilotClient: () => wrapCopilotClient,
26237
25884
  wrapCursorSDK: () => wrapCursorSDK,
26238
- wrapFlueContext: () => wrapFlueContext,
26239
- wrapFlueSession: () => wrapFlueSession,
26240
25885
  wrapGenkit: () => wrapGenkit,
26241
25886
  wrapGoogleADK: () => wrapGoogleADK,
26242
25887
  wrapGoogleGenAI: () => wrapGoogleGenAI,
@@ -27726,6 +27371,196 @@ function toolRunnerProxy(toolRunner, anthropic, channel2) {
27726
27371
  }
27727
27372
 
27728
27373
  // src/wrappers/mastra.ts
27374
+ var MASTRA_BRAINTRUST_EXPORTER_NAME = "braintrust";
27375
+ var SPAN_TYPE_MAP = {
27376
+ agent_run: "task" /* TASK */,
27377
+ model_generation: "llm" /* LLM */,
27378
+ model_step: "llm" /* LLM */,
27379
+ model_chunk: "llm" /* LLM */,
27380
+ tool_call: "tool" /* TOOL */,
27381
+ mcp_tool_call: "tool" /* TOOL */,
27382
+ workflow_run: "task" /* TASK */,
27383
+ workflow_step: "function" /* FUNCTION */,
27384
+ workflow_conditional: "function" /* FUNCTION */,
27385
+ workflow_conditional_eval: "function" /* FUNCTION */,
27386
+ workflow_parallel: "function" /* FUNCTION */,
27387
+ workflow_loop: "function" /* FUNCTION */,
27388
+ workflow_sleep: "function" /* FUNCTION */,
27389
+ workflow_wait_event: "function" /* FUNCTION */,
27390
+ memory_operation: "function" /* FUNCTION */,
27391
+ workspace_action: "function" /* FUNCTION */,
27392
+ rag_ingestion: "task" /* TASK */,
27393
+ rag_embedding: "llm" /* LLM */,
27394
+ rag_vector_operation: "function" /* FUNCTION */,
27395
+ rag_action: "function" /* FUNCTION */,
27396
+ graph_action: "function" /* FUNCTION */,
27397
+ scorer_run: "score" /* SCORE */,
27398
+ scorer_step: "score" /* SCORE */,
27399
+ processor_run: "function" /* FUNCTION */,
27400
+ generic: "function" /* FUNCTION */
27401
+ };
27402
+ function spanTypeFor(mastraType) {
27403
+ return SPAN_TYPE_MAP[mastraType] ?? "function" /* FUNCTION */;
27404
+ }
27405
+ function epochSeconds(value) {
27406
+ if (value === void 0) return void 0;
27407
+ const ms = value instanceof Date ? value.getTime() : typeof value === "number" ? value : Date.parse(value);
27408
+ return Number.isFinite(ms) ? ms / 1e3 : void 0;
27409
+ }
27410
+ function modelMetrics(attributes) {
27411
+ if (!isObject(attributes)) return void 0;
27412
+ const usage = isObject(attributes.usage) ? attributes.usage : void 0;
27413
+ if (!usage) return void 0;
27414
+ const out = {};
27415
+ if (typeof usage.inputTokens === "number")
27416
+ out.prompt_tokens = usage.inputTokens;
27417
+ if (typeof usage.outputTokens === "number")
27418
+ out.completion_tokens = usage.outputTokens;
27419
+ if (typeof usage.inputTokens === "number" && typeof usage.outputTokens === "number") {
27420
+ out.tokens = usage.inputTokens + usage.outputTokens;
27421
+ }
27422
+ const inputDetails = isObject(usage.inputDetails) ? usage.inputDetails : void 0;
27423
+ const outputDetails = isObject(usage.outputDetails) ? usage.outputDetails : void 0;
27424
+ if (inputDetails && typeof inputDetails.cacheRead === "number") {
27425
+ out.prompt_cached_tokens = inputDetails.cacheRead;
27426
+ }
27427
+ if (inputDetails && typeof inputDetails.cacheWrite === "number") {
27428
+ out.prompt_cache_creation_tokens = inputDetails.cacheWrite;
27429
+ }
27430
+ if (outputDetails && typeof outputDetails.reasoning === "number") {
27431
+ out.completion_reasoning_tokens = outputDetails.reasoning;
27432
+ }
27433
+ return Object.keys(out).length > 0 ? out : void 0;
27434
+ }
27435
+ function buildMetadata(exported) {
27436
+ const out = {};
27437
+ if (exported.entityId !== void 0) out.entity_id = exported.entityId;
27438
+ if (exported.entityName !== void 0) out.entity_name = exported.entityName;
27439
+ if (exported.entityType !== void 0) out.entity_type = exported.entityType;
27440
+ if (exported.metadata && isObject(exported.metadata)) {
27441
+ Object.assign(out, exported.metadata);
27442
+ }
27443
+ if (exported.attributes && isObject(exported.attributes)) {
27444
+ for (const [key, value] of Object.entries(exported.attributes)) {
27445
+ if (key === "usage") continue;
27446
+ if (value !== void 0) out[key] = value;
27447
+ }
27448
+ }
27449
+ if (exported.tags && exported.tags.length > 0) {
27450
+ out.tags = exported.tags;
27451
+ }
27452
+ if (exported.requestContext && isObject(exported.requestContext)) {
27453
+ out.request_context = exported.requestContext;
27454
+ }
27455
+ return out;
27456
+ }
27457
+ var BraintrustObservabilityExporter = class {
27458
+ name = MASTRA_BRAINTRUST_EXPORTER_NAME;
27459
+ spans = /* @__PURE__ */ new Map();
27460
+ // Captured at the first SPAN_STARTED event. Mastra's observability bus may
27461
+ // dispatch later events outside the user's AsyncLocalStorage context, where
27462
+ // `currentSpan()` returns NOOP_SPAN — which would make our `startSpan()`
27463
+ // calls go to a no-op logger and silently drop. Anchoring on the parent
27464
+ // we observe while still in-context keeps the whole Mastra subtree under
27465
+ // the user's traced scenario.
27466
+ capturedParent;
27467
+ constructor() {
27468
+ _internalSetInitialState();
27469
+ }
27470
+ async exportTracingEvent(event) {
27471
+ const exported = event.exportedSpan;
27472
+ if (exported.isInternal === true) return;
27473
+ try {
27474
+ switch (event.type) {
27475
+ case "span_started":
27476
+ this.onStart(exported);
27477
+ break;
27478
+ case "span_updated":
27479
+ this.onUpdate(exported);
27480
+ break;
27481
+ case "span_ended":
27482
+ this.onEnd(exported);
27483
+ break;
27484
+ }
27485
+ } catch (err) {
27486
+ logExporterError(err);
27487
+ }
27488
+ }
27489
+ async flush() {
27490
+ const state = _internalGetGlobalState();
27491
+ if (state) {
27492
+ await state.bgLogger().flush();
27493
+ }
27494
+ }
27495
+ async shutdown() {
27496
+ await this.flush();
27497
+ this.spans.clear();
27498
+ }
27499
+ onStart(exported) {
27500
+ if (this.spans.has(exported.id)) return;
27501
+ const args = {
27502
+ name: exported.name,
27503
+ spanAttributes: { type: spanTypeFor(exported.type) },
27504
+ startTime: epochSeconds(exported.startTime)
27505
+ };
27506
+ const parentRecord = exported.parentSpanId ? this.spans.get(exported.parentSpanId) : void 0;
27507
+ if (!this.capturedParent) {
27508
+ const probe = currentSpan();
27509
+ if (probe && probe.spanId) {
27510
+ this.capturedParent = probe;
27511
+ }
27512
+ }
27513
+ const span = parentRecord ? parentRecord.span.startSpan(args) : this.capturedParent ? this.capturedParent.startSpan(args) : startSpan(args);
27514
+ const record = { span, hasLoggedInput: false };
27515
+ this.logPayload(record, exported);
27516
+ this.spans.set(exported.id, record);
27517
+ if (exported.isEvent === true) {
27518
+ span.end({ endTime: args.startTime });
27519
+ this.spans.delete(exported.id);
27520
+ }
27521
+ }
27522
+ onUpdate(exported) {
27523
+ const record = this.spans.get(exported.id);
27524
+ if (!record) return;
27525
+ this.logPayload(record, exported);
27526
+ }
27527
+ onEnd(exported) {
27528
+ const record = this.spans.get(exported.id);
27529
+ if (!record) return;
27530
+ this.logPayload(record, exported);
27531
+ if (exported.errorInfo) {
27532
+ record.span.log({
27533
+ error: exported.errorInfo.message || exported.errorInfo.name || "Unknown Mastra error"
27534
+ });
27535
+ }
27536
+ record.span.end({ endTime: epochSeconds(exported.endTime) });
27537
+ this.spans.delete(exported.id);
27538
+ }
27539
+ logPayload(record, exported) {
27540
+ const event = {};
27541
+ if (exported.input !== void 0) {
27542
+ event.input = exported.input;
27543
+ record.hasLoggedInput = true;
27544
+ }
27545
+ if (exported.output !== void 0) {
27546
+ event.output = exported.output;
27547
+ }
27548
+ const metadata = buildMetadata(exported);
27549
+ if (Object.keys(metadata).length > 0) {
27550
+ event.metadata = metadata;
27551
+ }
27552
+ const metrics = modelMetrics(exported.attributes);
27553
+ if (metrics) {
27554
+ event.metrics = metrics;
27555
+ }
27556
+ if (Object.keys(event).length > 0) {
27557
+ record.span.log(event);
27558
+ }
27559
+ }
27560
+ };
27561
+ function logExporterError(err) {
27562
+ debugLogger.warn("Mastra exporter failure:", err);
27563
+ }
27729
27564
  function wrapMastraAgent(agent, _options) {
27730
27565
  return agent;
27731
27566
  }
@@ -33224,6 +33059,7 @@ export {
33224
33059
  BaseExperiment,
33225
33060
  BraintrustLangChainCallbackHandler,
33226
33061
  BraintrustMiddleware,
33062
+ BraintrustObservabilityExporter,
33227
33063
  BraintrustState,
33228
33064
  BraintrustStream,
33229
33065
  CachedSpanFetcher,
@@ -33270,6 +33106,7 @@ export {
33270
33106
  isomorph_default as _internalIso,
33271
33107
  _internalSetInitialState,
33272
33108
  addAzureBlobHeaders,
33109
+ braintrustFlueObserver,
33273
33110
  braintrustStreamChunkSchema,
33274
33111
  buildLocalSummary,
33275
33112
  configureInstrumentation,
@@ -33350,8 +33187,6 @@ export {
33350
33187
  wrapCohere,
33351
33188
  wrapCopilotClient,
33352
33189
  wrapCursorSDK,
33353
- wrapFlueContext,
33354
- wrapFlueSession,
33355
33190
  wrapGenkit,
33356
33191
  wrapGoogleADK,
33357
33192
  wrapGoogleGenAI,