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
@@ -33,6 +33,7 @@ __export(index_exports, {
33
33
  BasePlugin: () => BasePlugin,
34
34
  BraintrustPlugin: () => BraintrustPlugin,
35
35
  OpenAIAgentsTraceProcessor: () => OpenAIAgentsTraceProcessor,
36
+ braintrustFlueObserver: () => braintrustFlueObserver,
36
37
  configureInstrumentation: () => configureInstrumentation,
37
38
  createChannelName: () => createChannelName,
38
39
  isValidChannelName: () => isValidChannelName,
@@ -4019,6 +4020,76 @@ var LRUCache = class {
4019
4020
  }
4020
4021
  };
4021
4022
 
4023
+ // src/prompt-cache/cache-config.ts
4024
+ var CACHE_LOCATION_ENV_VAR = "BRAINTRUST_CACHE_LOCATION";
4025
+ var DEFAULT_CACHE_MEMORY_MAX = 1 << 10;
4026
+ var DEFAULT_CACHE_DISK_MAX = 1 << 20;
4027
+ var warnedInvalidCacheModeEnvValue = false;
4028
+ var warnedUnavailableDiskCacheMode = false;
4029
+ function warnInvalidCacheMode(value) {
4030
+ if (warnedInvalidCacheModeEnvValue) {
4031
+ return;
4032
+ }
4033
+ warnedInvalidCacheModeEnvValue = true;
4034
+ debugLogger.warn(
4035
+ `Invalid ${CACHE_LOCATION_ENV_VAR} value "${value}". Expected "mixed", "memory", "disk", or "none". Falling back to "mixed".`
4036
+ );
4037
+ }
4038
+ function warnUnavailableDiskCache() {
4039
+ if (warnedUnavailableDiskCacheMode) {
4040
+ return;
4041
+ }
4042
+ warnedUnavailableDiskCacheMode = true;
4043
+ debugLogger.warn(
4044
+ `Disk cache is not supported on this platform, so ${CACHE_LOCATION_ENV_VAR}="disk" disables prompt and parameters caching.`
4045
+ );
4046
+ }
4047
+ function parseCacheMode() {
4048
+ const value = isomorph_default.getEnv(CACHE_LOCATION_ENV_VAR);
4049
+ const normalized = value?.trim().toLowerCase();
4050
+ if (!normalized) {
4051
+ return "mixed";
4052
+ }
4053
+ if (normalized === "mixed" || normalized === "memory" || normalized === "disk" || normalized === "none") {
4054
+ return normalized;
4055
+ }
4056
+ warnInvalidCacheMode(value ?? "");
4057
+ return "mixed";
4058
+ }
4059
+ function parsePositiveIntegerEnv(envVar, defaultValue) {
4060
+ const value = Number(isomorph_default.getEnv(envVar));
4061
+ return Number.isInteger(value) && value > 0 ? value : defaultValue;
4062
+ }
4063
+ function createCacheLayers({
4064
+ memoryMaxEnvVar,
4065
+ diskCacheDirEnvVar,
4066
+ diskMaxEnvVar,
4067
+ getDefaultDiskCacheDir
4068
+ }) {
4069
+ const mode = parseCacheMode();
4070
+ const memoryCache = mode === "mixed" || mode === "memory" ? new LRUCache({
4071
+ max: parsePositiveIntegerEnv(
4072
+ memoryMaxEnvVar,
4073
+ DEFAULT_CACHE_MEMORY_MAX
4074
+ )
4075
+ }) : void 0;
4076
+ let diskCache;
4077
+ if (mode === "mixed" || mode === "disk") {
4078
+ if (canUseDiskCache()) {
4079
+ diskCache = new DiskCache({
4080
+ cacheDir: isomorph_default.getEnv(diskCacheDirEnvVar) ?? getDefaultDiskCacheDir(),
4081
+ max: parsePositiveIntegerEnv(diskMaxEnvVar, DEFAULT_CACHE_DISK_MAX)
4082
+ });
4083
+ } else if (mode === "disk") {
4084
+ warnUnavailableDiskCache();
4085
+ }
4086
+ }
4087
+ if (diskCache) {
4088
+ return { memoryCache, diskCache };
4089
+ }
4090
+ return { memoryCache };
4091
+ }
4092
+
4022
4093
  // src/prompt-cache/prompt-cache.ts
4023
4094
  function createCacheKey(key) {
4024
4095
  if (key.id) {
@@ -4046,16 +4117,18 @@ var PromptCache = class {
4046
4117
  */
4047
4118
  async get(key) {
4048
4119
  const cacheKey = createCacheKey(key);
4049
- const memoryPrompt = this.memoryCache.get(cacheKey);
4050
- if (memoryPrompt !== void 0) {
4051
- return memoryPrompt;
4120
+ if (this.memoryCache) {
4121
+ const memoryPrompt = this.memoryCache.get(cacheKey);
4122
+ if (memoryPrompt !== void 0) {
4123
+ return memoryPrompt;
4124
+ }
4052
4125
  }
4053
4126
  if (this.diskCache) {
4054
4127
  const diskPrompt = await this.diskCache.get(cacheKey);
4055
4128
  if (!diskPrompt) {
4056
4129
  return void 0;
4057
4130
  }
4058
- this.memoryCache.set(cacheKey, diskPrompt);
4131
+ this.memoryCache?.set(cacheKey, diskPrompt);
4059
4132
  return diskPrompt;
4060
4133
  }
4061
4134
  return void 0;
@@ -4070,7 +4143,7 @@ var PromptCache = class {
4070
4143
  */
4071
4144
  async set(key, value) {
4072
4145
  const cacheKey = createCacheKey(key);
4073
- this.memoryCache.set(cacheKey, value);
4146
+ this.memoryCache?.set(cacheKey, value);
4074
4147
  if (this.diskCache) {
4075
4148
  await this.diskCache.set(cacheKey, value);
4076
4149
  }
@@ -4100,23 +4173,25 @@ var ParametersCache = class {
4100
4173
  }
4101
4174
  async get(key) {
4102
4175
  const cacheKey = createCacheKey2(key);
4103
- const memoryParams = this.memoryCache.get(cacheKey);
4104
- if (memoryParams !== void 0) {
4105
- return memoryParams;
4176
+ if (this.memoryCache) {
4177
+ const memoryParams = this.memoryCache.get(cacheKey);
4178
+ if (memoryParams !== void 0) {
4179
+ return memoryParams;
4180
+ }
4106
4181
  }
4107
4182
  if (this.diskCache) {
4108
4183
  const diskParams = await this.diskCache.get(cacheKey);
4109
4184
  if (!diskParams) {
4110
4185
  return void 0;
4111
4186
  }
4112
- this.memoryCache.set(cacheKey, diskParams);
4187
+ this.memoryCache?.set(cacheKey, diskParams);
4113
4188
  return diskParams;
4114
4189
  }
4115
4190
  return void 0;
4116
4191
  }
4117
4192
  async set(key, value) {
4118
4193
  const cacheKey = createCacheKey2(key);
4119
- this.memoryCache.set(cacheKey, value);
4194
+ this.memoryCache?.set(cacheKey, value);
4120
4195
  if (this.diskCache) {
4121
4196
  await this.diskCache.set(cacheKey, value);
4122
4197
  }
@@ -4648,21 +4723,22 @@ var BraintrustState = class _BraintrustState {
4648
4723
  setGlobalDebugLogLevel(void 0);
4649
4724
  }
4650
4725
  this.resetLoginInfo();
4651
- const memoryCache = new LRUCache({
4652
- max: Number(isomorph_default.getEnv("BRAINTRUST_PROMPT_CACHE_MEMORY_MAX")) ?? 1 << 10
4726
+ const { memoryCache, diskCache } = createCacheLayers({
4727
+ memoryMaxEnvVar: "BRAINTRUST_PROMPT_CACHE_MEMORY_MAX",
4728
+ diskCacheDirEnvVar: "BRAINTRUST_PROMPT_CACHE_DIR",
4729
+ diskMaxEnvVar: "BRAINTRUST_PROMPT_CACHE_DISK_MAX",
4730
+ getDefaultDiskCacheDir: () => `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/prompt_cache`
4653
4731
  });
4654
- const diskCache = canUseDiskCache() ? new DiskCache({
4655
- cacheDir: isomorph_default.getEnv("BRAINTRUST_PROMPT_CACHE_DIR") ?? `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/prompt_cache`,
4656
- max: Number(isomorph_default.getEnv("BRAINTRUST_PROMPT_CACHE_DISK_MAX")) ?? 1 << 20
4657
- }) : void 0;
4658
4732
  this.promptCache = new PromptCache({ memoryCache, diskCache });
4659
- const parametersMemoryCache = new LRUCache({
4660
- max: Number(isomorph_default.getEnv("BRAINTRUST_PARAMETERS_CACHE_MEMORY_MAX")) ?? 1 << 10
4733
+ const {
4734
+ memoryCache: parametersMemoryCache,
4735
+ diskCache: parametersDiskCache
4736
+ } = createCacheLayers({
4737
+ memoryMaxEnvVar: "BRAINTRUST_PARAMETERS_CACHE_MEMORY_MAX",
4738
+ diskCacheDirEnvVar: "BRAINTRUST_PARAMETERS_CACHE_DIR",
4739
+ diskMaxEnvVar: "BRAINTRUST_PARAMETERS_CACHE_DISK_MAX",
4740
+ getDefaultDiskCacheDir: () => `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/parameters_cache`
4661
4741
  });
4662
- const parametersDiskCache = canUseDiskCache() ? new DiskCache({
4663
- cacheDir: isomorph_default.getEnv("BRAINTRUST_PARAMETERS_CACHE_DIR") ?? `${isomorph_default.getEnv("HOME") ?? isomorph_default.homedir()}/.braintrust/parameters_cache`,
4664
- max: Number(isomorph_default.getEnv("BRAINTRUST_PARAMETERS_CACHE_DISK_MAX")) ?? 1 << 20
4665
- }) : void 0;
4666
4742
  this.parametersCache = new ParametersCache({
4667
4743
  memoryCache: parametersMemoryCache,
4668
4744
  diskCache: parametersDiskCache
@@ -6661,6 +6737,10 @@ ${stackTrace}` });
6661
6737
  function startSpan(args) {
6662
6738
  return startSpanAndIsLogger(args).span;
6663
6739
  }
6740
+ async function flush(options) {
6741
+ const state = options?.state ?? _globalState;
6742
+ return await state.bgLogger().flush();
6743
+ }
6664
6744
  function startSpanAndIsLogger(args) {
6665
6745
  const state = args?.state ?? _globalState;
6666
6746
  const parentObject = getSpanParentObject({
@@ -21902,803 +21982,440 @@ var flueChannels = defineChannels("@flue/runtime", {
21902
21982
  createContext: channel({
21903
21983
  channelName: "createFlueContext",
21904
21984
  kind: "sync-stream"
21905
- }),
21906
- openSession: channel({
21907
- channelName: "Harness.openSession",
21908
- kind: "async"
21909
- }),
21910
- contextEvent: channel({
21911
- channelName: "context.event",
21912
- kind: "sync-stream"
21913
- }),
21914
- prompt: channel({
21915
- channelName: "session.prompt",
21916
- kind: "async"
21917
- }),
21918
- skill: channel({
21919
- channelName: "session.skill",
21920
- kind: "async"
21921
- }),
21922
- task: channel({
21923
- channelName: "session.task",
21924
- kind: "async"
21925
- }),
21926
- compact: channel({
21927
- channelName: "session.compact",
21928
- kind: "async"
21929
21985
  })
21930
21986
  });
21931
21987
 
21932
- // src/wrappers/flue.ts
21933
- var WRAPPED_FLUE_CONTEXT = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-context");
21934
- var WRAPPED_FLUE_HARNESS = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-harness");
21935
- var WRAPPED_FLUE_SESSION = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-session");
21936
- var SUBSCRIBED_FLUE_CONTEXT_EVENTS = /* @__PURE__ */ Symbol.for(
21937
- "braintrust.flue.subscribed-context-events"
21938
- );
21939
- function patchFlueContextInPlace(ctx) {
21940
- const context = ctx;
21941
- if (context[WRAPPED_FLUE_CONTEXT]) {
21942
- return ctx;
21943
- }
21944
- const originalInit = context.init.bind(context);
21945
- try {
21946
- Object.defineProperty(context, WRAPPED_FLUE_CONTEXT, {
21947
- configurable: false,
21948
- enumerable: false,
21949
- value: true
21950
- });
21951
- Object.defineProperty(context, "init", {
21952
- configurable: true,
21953
- value: async function wrappedFlueInit(options) {
21954
- const harness = await originalInit(options);
21955
- return wrapFlueHarness(harness);
21956
- },
21957
- writable: true
21958
- });
21959
- } catch {
21960
- }
21961
- return ctx;
21962
- }
21963
- function subscribeFlueContextEvents(ctx, options = {}) {
21964
- if (!ctx || typeof ctx !== "object" || typeof ctx.subscribeEvent !== "function") {
21965
- return void 0;
21988
+ // src/instrumentation/plugins/flue-plugin.ts
21989
+ var FLUE_AUTO_STATE = /* @__PURE__ */ Symbol.for("braintrust.flue.auto-state");
21990
+ var FLUE_OBSERVE_BRIDGE = /* @__PURE__ */ Symbol.for("braintrust.flue.observe-bridge");
21991
+ var braintrustFlueObserver = (event, ctx) => {
21992
+ getObserveBridge().handle(event, ctx);
21993
+ };
21994
+ var FluePlugin = class extends BasePlugin {
21995
+ onEnable() {
21996
+ this.unsubscribers.push(enableFlueAutoInstrumentation());
21966
21997
  }
21967
- const context = ctx;
21968
- const captureTurnSpans = options.captureTurnSpans ?? true;
21969
- const existingSubscription = context[SUBSCRIBED_FLUE_CONTEXT_EVENTS];
21970
- if (existingSubscription) {
21971
- if (existingSubscription.captureTurnSpans || !captureTurnSpans) {
21972
- return void 0;
21973
- }
21974
- try {
21975
- existingSubscription.unsubscribe();
21976
- } catch {
21998
+ onDisable() {
21999
+ for (const unsubscribe of this.unsubscribers) {
22000
+ unsubscribe();
21977
22001
  }
22002
+ this.unsubscribers = [];
21978
22003
  }
21979
- try {
21980
- const unsubscribe = ctx.subscribeEvent((event) => {
21981
- flueChannels.contextEvent.traceSync(() => void 0, {
21982
- arguments: [event],
21983
- captureTurnSpans,
21984
- context: ctx
21985
- });
21986
- });
21987
- if (existingSubscription) {
21988
- existingSubscription.captureTurnSpans = captureTurnSpans;
21989
- existingSubscription.unsubscribe = unsubscribe;
21990
- } else {
21991
- Object.defineProperty(context, SUBSCRIBED_FLUE_CONTEXT_EVENTS, {
21992
- configurable: false,
21993
- enumerable: false,
21994
- value: {
21995
- captureTurnSpans,
21996
- unsubscribe
21997
- }
21998
- });
22004
+ };
22005
+ function enableFlueAutoInstrumentation() {
22006
+ const state = getAutoState();
22007
+ state.refCount += 1;
22008
+ if (!state.handlers) {
22009
+ const channel2 = flueChannels.createContext.tracingChannel();
22010
+ const handlers = {
22011
+ end: (event) => {
22012
+ subscribeToFlueContext(event.result, state);
22013
+ }
22014
+ };
22015
+ channel2.subscribe(handlers);
22016
+ state.channel = channel2;
22017
+ state.handlers = handlers;
22018
+ }
22019
+ let released = false;
22020
+ return () => {
22021
+ if (released) {
22022
+ return;
21999
22023
  }
22000
- return unsubscribe;
22001
- } catch {
22002
- return void 0;
22024
+ released = true;
22025
+ releaseAutoState(state);
22026
+ };
22027
+ }
22028
+ function getAutoState() {
22029
+ const existing = Reflect.get(globalThis, FLUE_AUTO_STATE);
22030
+ if (isAutoState(existing)) {
22031
+ return existing;
22003
22032
  }
22033
+ const state = {
22034
+ contexts: /* @__PURE__ */ new WeakSet(),
22035
+ refCount: 0
22036
+ };
22037
+ Reflect.set(globalThis, FLUE_AUTO_STATE, state);
22038
+ return state;
22004
22039
  }
22005
- function wrapFlueHarness(harness) {
22006
- if (!isPlausibleFlueHarness(harness)) {
22007
- return harness;
22040
+ function getObserveBridge() {
22041
+ const existing = Reflect.get(globalThis, FLUE_OBSERVE_BRIDGE);
22042
+ if (isFlueObserveBridge(existing)) {
22043
+ return existing;
22008
22044
  }
22009
- const target = harness;
22010
- if (target[WRAPPED_FLUE_HARNESS]) {
22011
- return harness;
22045
+ const bridge = new FlueObserveBridge();
22046
+ Reflect.set(globalThis, FLUE_OBSERVE_BRIDGE, bridge);
22047
+ return bridge;
22048
+ }
22049
+ function isFlueObserveBridge(value) {
22050
+ return isObjectLike(value) && typeof Reflect.get(value, "handle") === "function" && typeof Reflect.get(value, "reset") === "function";
22051
+ }
22052
+ function isAutoState(value) {
22053
+ return isObjectLike(value) && Reflect.get(value, "contexts") instanceof WeakSet && typeof Reflect.get(value, "refCount") === "number";
22054
+ }
22055
+ function releaseAutoState(state) {
22056
+ state.refCount -= 1;
22057
+ if (state.refCount > 0) {
22058
+ return;
22012
22059
  }
22013
- const originalSession = target.session.bind(target);
22014
22060
  try {
22015
- Object.defineProperty(target, WRAPPED_FLUE_HARNESS, {
22016
- configurable: false,
22017
- enumerable: false,
22018
- value: true
22019
- });
22020
- Object.defineProperty(target, "session", {
22021
- configurable: true,
22022
- value: async function wrappedFlueHarnessSession(name, options) {
22023
- const session = await originalSession(name, options);
22024
- return patchFlueSessionInPlace(session);
22025
- },
22026
- writable: true
22027
- });
22028
- const sessions = target.sessions;
22029
- if (sessions && typeof sessions === "object") {
22030
- patchFlueSessionFactory(sessions, "get");
22031
- patchFlueSessionFactory(sessions, "create");
22061
+ if (state.channel && state.handlers) {
22062
+ state.channel.unsubscribe(state.handlers);
22032
22063
  }
22033
- } catch {
22064
+ } finally {
22065
+ Reflect.deleteProperty(globalThis, FLUE_AUTO_STATE);
22034
22066
  }
22035
- return harness;
22036
22067
  }
22037
- function patchFlueSessionInPlace(session) {
22038
- if (session[WRAPPED_FLUE_SESSION]) {
22039
- return session;
22068
+ function subscribeToFlueContext(value, state) {
22069
+ if (!isObservableFlueContext(value) || state.contexts.has(value)) {
22070
+ return;
22040
22071
  }
22072
+ const ctx = flueContextFromUnknown(value);
22073
+ let released = false;
22074
+ let unsubscribe;
22075
+ const release = () => {
22076
+ if (released) {
22077
+ return;
22078
+ }
22079
+ released = true;
22080
+ try {
22081
+ unsubscribe?.();
22082
+ } catch (error) {
22083
+ logInstrumentationError3("Flue context unsubscribe", error);
22084
+ }
22085
+ };
22041
22086
  try {
22042
- Object.defineProperty(session, WRAPPED_FLUE_SESSION, {
22043
- configurable: false,
22044
- enumerable: false,
22045
- value: true
22087
+ unsubscribe = value.subscribeEvent((event) => {
22088
+ if (state.refCount <= 0) {
22089
+ release();
22090
+ return;
22091
+ }
22092
+ braintrustFlueObserver(event, ctx);
22093
+ if (isAutoContextTerminalEvent(event, ctx)) {
22094
+ release();
22095
+ }
22046
22096
  });
22047
- patchCallHandleMethod(session, "prompt", flueChannels.prompt);
22048
- patchCallHandleMethod(session, "skill", flueChannels.skill);
22049
- patchCallHandleMethod(session, "task", flueChannels.task);
22050
- patchCompact(session);
22051
- } catch {
22097
+ state.contexts.add(value);
22098
+ } catch (error) {
22099
+ logInstrumentationError3("Flue context subscription", error);
22052
22100
  }
22053
- return session;
22054
22101
  }
22055
- function patchFlueSessionFactory(sessions, method) {
22056
- const original = sessions[method];
22057
- if (typeof original !== "function") {
22058
- return;
22102
+ function isAutoContextTerminalEvent(event, ctx) {
22103
+ if (!isObjectLike(event)) {
22104
+ return false;
22059
22105
  }
22060
- const bound = original.bind(sessions);
22061
- Object.defineProperty(sessions, method, {
22062
- configurable: true,
22063
- value: async function wrappedFlueSessionFactory(name, options) {
22064
- const session = await bound(name, options);
22065
- return patchFlueSessionInPlace(session);
22066
- },
22067
- writable: true
22068
- });
22069
- }
22070
- function patchCallHandleMethod(session, method, channel2) {
22071
- const original = session[method];
22072
- if (typeof original !== "function") {
22073
- return;
22106
+ const type = Reflect.get(event, "type");
22107
+ if (type === "run_end") {
22108
+ return true;
22074
22109
  }
22075
- const bound = original.bind(session);
22076
- Object.defineProperty(session, method, {
22077
- configurable: true,
22078
- value(input, options) {
22079
- const args = [input, options];
22080
- const { originalResult, traced } = traceFlueOperation(channel2, {
22081
- context: {
22082
- arguments: args,
22083
- operation: method,
22084
- session
22085
- },
22086
- run: () => bound(input, options)
22087
- });
22088
- return preserveCallHandle(originalResult, traced);
22089
- },
22090
- writable: true
22091
- });
22092
- }
22093
- function patchCompact(session) {
22094
- const original = session.compact;
22095
- if (typeof original !== "function") {
22096
- return;
22110
+ if (type !== "operation") {
22111
+ return false;
22097
22112
  }
22098
- const bound = original.bind(session);
22099
- Object.defineProperty(session, "compact", {
22100
- configurable: true,
22101
- value() {
22102
- const context = {
22103
- arguments: [],
22104
- operation: "compact",
22105
- session
22106
- };
22107
- return flueChannels.compact.tracePromise(() => bound(), context);
22108
- },
22109
- writable: true
22110
- });
22113
+ return !ctx?.runId && typeof Reflect.get(event, "runId") !== "string";
22111
22114
  }
22112
- function traceFlueOperation(channel2, args) {
22113
- const tracingChannel = channel2.tracingChannel();
22114
- const context = args.context;
22115
- let originalResult;
22116
- let traced;
22117
- const run = () => {
22118
- try {
22119
- originalResult = args.run();
22120
- tracingChannel.end?.publish(context);
22121
- } catch (error) {
22122
- context.error = normalizeError3(error);
22123
- tracingChannel.error?.publish(context);
22124
- tracingChannel.end?.publish(context);
22125
- throw error;
22126
- }
22127
- traced = Promise.resolve(originalResult).then(
22128
- (result) => {
22129
- context.result = result;
22130
- tracingChannel.asyncStart?.publish(context);
22131
- tracingChannel.asyncEnd?.publish(context);
22132
- return result;
22133
- },
22134
- (error) => {
22135
- context.error = normalizeError3(error);
22136
- tracingChannel.error?.publish(context);
22137
- tracingChannel.asyncStart?.publish(context);
22138
- tracingChannel.asyncEnd?.publish(context);
22139
- throw error;
22140
- }
22141
- );
22142
- };
22143
- if (tracingChannel.start?.runStores) {
22144
- tracingChannel.start.runStores(context, run);
22145
- } else {
22146
- tracingChannel.start?.publish(context);
22147
- run();
22148
- }
22149
- return { originalResult, traced };
22115
+ function isObservableFlueContext(value) {
22116
+ return isObjectLike(value) && typeof Reflect.get(value, "subscribeEvent") === "function";
22150
22117
  }
22151
- function normalizeError3(error) {
22152
- return error instanceof Error ? error : new Error(String(error));
22118
+ function isFlueEvent(event) {
22119
+ const type = Reflect.get(event, "type");
22120
+ 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";
22153
22121
  }
22154
- function preserveCallHandle(originalHandle, traced) {
22155
- if (!isFlueCallHandle(originalHandle)) {
22156
- return traced;
22122
+ function flueContextFromUnknown(ctx) {
22123
+ if (!isObjectLike(ctx)) {
22124
+ return void 0;
22157
22125
  }
22158
- const handle = originalHandle;
22159
- const wrapped = {
22160
- get signal() {
22161
- return handle.signal;
22162
- },
22163
- abort(reason) {
22164
- return handle.abort(reason);
22165
- },
22166
- then(onfulfilled, onrejected) {
22167
- return traced.then(onfulfilled, onrejected);
22168
- }
22126
+ const id = Reflect.get(ctx, "id");
22127
+ const runId = Reflect.get(ctx, "runId");
22128
+ return {
22129
+ ...typeof id === "string" ? { id } : {},
22130
+ ...typeof runId === "string" ? { runId } : {}
22169
22131
  };
22170
- return wrapped;
22171
22132
  }
22172
- function isPlausibleFlueHarness(value) {
22173
- return !!value && typeof value === "object" && typeof value.session === "function";
22174
- }
22175
- function isFlueCallHandle(value) {
22176
- return !!value && typeof value === "object" && typeof value.then === "function" && typeof value.abort === "function" && "signal" in value;
22133
+ function isObjectLike(value) {
22134
+ return typeof value === "object" && value !== null && !Array.isArray(value);
22177
22135
  }
22178
-
22179
- // src/instrumentation/plugins/flue-plugin.ts
22180
- var FluePlugin = class extends BasePlugin {
22181
- activeOperationsById = /* @__PURE__ */ new Map();
22182
- activeOperationsByScope = /* @__PURE__ */ new Map();
22183
- compactionsByScope = /* @__PURE__ */ new Map();
22184
- pendingOperationsByKey = /* @__PURE__ */ new Map();
22136
+ var FlueObserveBridge = class {
22137
+ compactionsByKey = /* @__PURE__ */ new Map();
22138
+ operationsById = /* @__PURE__ */ new Map();
22139
+ runsById = /* @__PURE__ */ new Map();
22140
+ seenEvents = /* @__PURE__ */ new WeakSet();
22185
22141
  tasksById = /* @__PURE__ */ new Map();
22186
- toolsById = /* @__PURE__ */ new Map();
22187
- turnsByScope = /* @__PURE__ */ new Map();
22188
- onEnable() {
22189
- this.subscribeToContextCreation();
22190
- this.subscribeToSessionCreation();
22191
- this.subscribeToContextEvents();
22192
- this.subscribeToSessionOperations();
22193
- }
22194
- onDisable() {
22195
- for (const unsubscribe of this.unsubscribers) {
22196
- unsubscribe();
22142
+ toolsByKey = /* @__PURE__ */ new Map();
22143
+ turnsByKey = /* @__PURE__ */ new Map();
22144
+ handle(event, ctx) {
22145
+ if (!isObjectLike(event) || !isFlueEvent(event)) {
22146
+ return;
22197
22147
  }
22198
- this.unsubscribers = [];
22199
- this.activeOperationsById.clear();
22200
- this.activeOperationsByScope.clear();
22201
- this.compactionsByScope.clear();
22202
- this.pendingOperationsByKey.clear();
22148
+ if (this.seenEvents.has(event)) {
22149
+ return;
22150
+ }
22151
+ this.seenEvents.add(event);
22152
+ try {
22153
+ this.handleEvent(event, flueContextFromUnknown(ctx));
22154
+ } catch (error) {
22155
+ logInstrumentationError3("Flue observe", error);
22156
+ }
22157
+ }
22158
+ reset() {
22159
+ this.compactionsByKey.clear();
22160
+ this.operationsById.clear();
22161
+ this.runsById.clear();
22162
+ this.seenEvents = /* @__PURE__ */ new WeakSet();
22203
22163
  this.tasksById.clear();
22204
- this.toolsById.clear();
22205
- this.turnsByScope.clear();
22164
+ this.toolsByKey.clear();
22165
+ this.turnsByKey.clear();
22206
22166
  }
22207
- subscribeToContextCreation() {
22208
- const channel2 = flueChannels.createContext.tracingChannel();
22209
- const handlers = {
22210
- end: (event) => {
22211
- const ctx = event.result;
22212
- if (!ctx) {
22213
- return;
22214
- }
22215
- subscribeFlueContextEvents(ctx, { captureTurnSpans: false });
22216
- patchFlueContextInPlace(ctx);
22217
- },
22218
- error: () => {
22219
- }
22220
- };
22221
- channel2.subscribe(handlers);
22222
- this.unsubscribers.push(() => {
22223
- channel2.unsubscribe(handlers);
22224
- });
22167
+ handleEvent(event, ctx) {
22168
+ switch (event.type) {
22169
+ case "run_start":
22170
+ this.handleRunStart(event, ctx);
22171
+ return;
22172
+ case "run_end":
22173
+ this.handleRunEnd(event);
22174
+ return;
22175
+ case "operation_start":
22176
+ this.handleOperationStart(event);
22177
+ return;
22178
+ case "operation":
22179
+ this.handleOperation(event);
22180
+ return;
22181
+ case "turn_request":
22182
+ this.handleTurnRequest(event);
22183
+ return;
22184
+ case "turn":
22185
+ this.handleTurn(event);
22186
+ return;
22187
+ case "tool_start":
22188
+ this.handleToolStart(event);
22189
+ return;
22190
+ case "tool_call":
22191
+ this.handleToolCall(event);
22192
+ return;
22193
+ case "task_start":
22194
+ this.handleTaskStart(event);
22195
+ return;
22196
+ case "task":
22197
+ this.handleTask(event);
22198
+ return;
22199
+ case "compaction_start":
22200
+ this.handleCompactionStart(event);
22201
+ return;
22202
+ case "compaction":
22203
+ this.handleCompaction(event);
22204
+ return;
22205
+ default:
22206
+ return;
22207
+ }
22225
22208
  }
22226
- subscribeToSessionCreation() {
22227
- const channel2 = flueChannels.openSession.tracingChannel();
22228
- const handlers = {
22229
- asyncEnd: (event) => {
22230
- if (event.result) {
22231
- patchFlueSessionInPlace(
22232
- event.result
22233
- );
22234
- }
22235
- if (event.harness) {
22236
- wrapFlueHarness(event.harness);
22237
- }
22238
- },
22239
- error: () => {
22240
- }
22209
+ handleRunStart(event, ctx) {
22210
+ if (!event.runId) {
22211
+ return;
22212
+ }
22213
+ const workflowName = event.workflowName ?? event.owner?.workflowName ?? (typeof ctx?.id === "string" ? ctx.id : "unknown");
22214
+ const metadata = {
22215
+ ...extractPayloadMetadata(event.payload),
22216
+ ...extractEventMetadata(event, ctx),
22217
+ ...workflowName ? { "flue.workflow_name": workflowName } : {},
22218
+ provider: "flue"
22241
22219
  };
22242
- channel2.subscribe(handlers);
22243
- this.unsubscribers.push(() => {
22244
- channel2.unsubscribe(handlers);
22220
+ const span = startSpan({
22221
+ name: `workflow:${workflowName}`,
22222
+ spanAttributes: { type: "task" /* TASK */ },
22223
+ startTime: eventTime(event.startedAt ?? event.timestamp),
22224
+ event: {
22225
+ input: event.payload,
22226
+ metadata
22227
+ }
22245
22228
  });
22229
+ this.runsById.set(event.runId, { metadata, span });
22246
22230
  }
22247
- subscribeToSessionOperations() {
22248
- this.subscribeToSessionOperation(flueChannels.prompt);
22249
- this.subscribeToSessionOperation(flueChannels.skill);
22250
- this.subscribeToSessionOperation(flueChannels.task);
22251
- this.subscribeToCompact();
22252
- }
22253
- subscribeToSessionOperation(channel2) {
22254
- const tracingChannel = channel2.tracingChannel();
22255
- const states = /* @__PURE__ */ new WeakMap();
22256
- const ensureState2 = (event) => {
22257
- const existing = states.get(event);
22258
- if (existing) {
22259
- return existing;
22260
- }
22261
- const state = this.startOperationState({
22262
- args: event.arguments,
22263
- moduleVersion: typeof event.moduleVersion === "string" ? event.moduleVersion : void 0,
22264
- operation: event.operation,
22265
- session: event.session
22231
+ handleRunEnd(event) {
22232
+ const state = this.runsById.get(event.runId);
22233
+ this.finishPendingSpansForRun(event);
22234
+ if (state) {
22235
+ safeLog3(state.span, {
22236
+ ...event.isError ? { error: errorToString(event.error) } : {},
22237
+ metadata: {
22238
+ ...state.metadata,
22239
+ ...extractEventMetadata(event),
22240
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
22241
+ },
22242
+ metrics: durationMetrics2(event.durationMs),
22243
+ output: event.result
22266
22244
  });
22267
- states.set(event, state);
22268
- return state;
22269
- };
22270
- const unbindCurrentSpanStore = this.bindCurrentSpanStoreToOperationStart(
22271
- tracingChannel,
22272
- ensureState2
22273
- );
22274
- const handlers = {
22275
- start: (event) => {
22276
- ensureState2(event);
22277
- },
22278
- asyncEnd: (event) => {
22279
- this.endOperationState(states.get(event), event.result);
22280
- states.delete(event);
22281
- },
22282
- error: (event) => {
22283
- const state = states.get(event);
22284
- if (state && event.error) {
22285
- safeLog3(state.span, { error: errorToString(event.error) });
22286
- this.finishOperationState(state);
22287
- }
22288
- states.delete(event);
22289
- }
22290
- };
22291
- tracingChannel.subscribe(handlers);
22292
- this.unsubscribers.push(() => {
22293
- unbindCurrentSpanStore?.();
22294
- tracingChannel.unsubscribe(handlers);
22295
- });
22296
- }
22297
- subscribeToCompact() {
22298
- const tracingChannel = flueChannels.compact.tracingChannel();
22299
- const states = /* @__PURE__ */ new WeakMap();
22300
- const ensureState2 = (event) => {
22301
- const existing = states.get(event);
22302
- if (existing) {
22303
- return existing;
22304
- }
22305
- const state = this.startOperationState({
22306
- args: [],
22307
- moduleVersion: typeof event.moduleVersion === "string" ? event.moduleVersion : void 0,
22308
- operation: event.operation,
22309
- session: event.session
22310
- });
22311
- states.set(event, state);
22312
- return state;
22313
- };
22314
- const unbindCurrentSpanStore = this.bindCurrentSpanStoreToOperationStart(
22315
- tracingChannel,
22316
- ensureState2
22317
- );
22318
- const handlers = {
22319
- start: (event) => {
22320
- ensureState2(event);
22321
- },
22322
- asyncEnd: (event) => {
22323
- this.endOperationState(states.get(event), void 0);
22324
- states.delete(event);
22325
- },
22326
- error: (event) => {
22327
- const state = states.get(event);
22328
- if (state && event.error) {
22329
- safeLog3(state.span, { error: errorToString(event.error) });
22330
- this.finishOperationState(state);
22331
- }
22332
- states.delete(event);
22333
- }
22334
- };
22335
- tracingChannel.subscribe(handlers);
22336
- this.unsubscribers.push(() => {
22337
- unbindCurrentSpanStore?.();
22338
- tracingChannel.unsubscribe(handlers);
22339
- });
22340
- }
22341
- subscribeToContextEvents() {
22342
- const channel2 = flueChannels.contextEvent.tracingChannel();
22343
- const handlers = {
22344
- start: (event) => {
22345
- const flueEvent = event.arguments[0];
22346
- if (!flueEvent) {
22347
- return;
22348
- }
22349
- try {
22350
- this.handleFlueEvent(flueEvent, {
22351
- captureTurnSpans: event.captureTurnSpans !== false
22352
- });
22353
- } catch (error) {
22354
- logInstrumentationError3("Flue event", error);
22355
- }
22356
- },
22357
- error: () => {
22358
- }
22359
- };
22360
- channel2.subscribe(handlers);
22361
- this.unsubscribers.push(() => {
22362
- channel2.unsubscribe(handlers);
22363
- });
22364
- }
22365
- bindCurrentSpanStoreToOperationStart(tracingChannel, ensureState2) {
22366
- const state = _internalGetGlobalState();
22367
- const startChannel = tracingChannel.start;
22368
- const contextManager = state?.contextManager;
22369
- const currentSpanStore = contextManager ? contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
22370
- if (!currentSpanStore || !startChannel) {
22371
- return void 0;
22245
+ safeEnd(state.span, eventTime(event.timestamp));
22246
+ this.runsById.delete(event.runId);
22372
22247
  }
22373
- startChannel.bindStore(currentSpanStore, (event) => {
22374
- const operationState = ensureState2(event);
22375
- return contextManager.wrapSpanForStore(operationState.span);
22248
+ void flush().catch((error) => {
22249
+ logInstrumentationError3("Flue flush", error);
22376
22250
  });
22377
- return () => {
22378
- startChannel.unbindStore(currentSpanStore);
22379
- };
22380
- }
22381
- startOperationState(args) {
22382
- const sessionName = getSessionName(args.session);
22383
- const metadata = {
22384
- ...extractOperationInputMetadata(args.operation, args.args),
22385
- ...extractSessionMetadata(args.session),
22386
- "flue.operation": args.operation,
22387
- provider: "flue",
22388
- ...args.moduleVersion ? { "flue.version": args.moduleVersion } : {}
22389
- };
22390
- const span = startSpan({
22391
- name: `flue.session.${args.operation}`,
22392
- spanAttributes: { type: "task" /* TASK */ }
22393
- });
22394
- const state = {
22395
- metadata,
22396
- operation: args.operation,
22397
- sessionName,
22398
- span,
22399
- startTime: getCurrentUnixTimestamp()
22400
- };
22401
- safeLog3(span, {
22402
- input: extractOperationInput(args.operation, args.args),
22403
- metadata
22404
- });
22405
- this.pendingOperationQueue(operationKey(sessionName, args.operation)).push(
22406
- state
22407
- );
22408
- addOperationToScope(
22409
- this.activeOperationsByScope,
22410
- sessionName ?? "unknown",
22411
- state
22412
- );
22413
- return state;
22414
- }
22415
- endOperationState(state, result) {
22416
- if (!state) {
22417
- return;
22418
- }
22419
- const metadata = {
22420
- ...state.metadata,
22421
- ...extractPromptResponseMetadata(result)
22422
- };
22423
- const metrics = {
22424
- ...buildDurationMetrics3(state.startTime),
22425
- ...metricsFromUsage(result?.usage)
22426
- };
22427
- safeLog3(state.span, {
22428
- metadata,
22429
- metrics,
22430
- output: extractOperationOutput(result)
22431
- });
22432
- this.finishCompactionsForOperation(state);
22433
- this.finishOperationState(state);
22434
- }
22435
- finishOperationState(state) {
22436
- removePendingOperation(this.pendingOperationsByKey, state);
22437
- if (state.operationId) {
22438
- this.activeOperationsById.delete(state.operationId);
22439
- }
22440
- removeScopedOperation(this.activeOperationsByScope, state);
22441
- state.span.end();
22442
- }
22443
- handleFlueEvent(event, options) {
22444
- switch (event.type) {
22445
- case "operation_start":
22446
- this.handleOperationStart(event);
22447
- return;
22448
- case "operation":
22449
- this.handleOperation(event);
22450
- return;
22451
- case "text_delta":
22452
- if (!options.captureTurnSpans) {
22453
- return;
22454
- }
22455
- this.ensureTurnState(event).text.push(
22456
- typeof event.text === "string" ? event.text : ""
22457
- );
22458
- return;
22459
- case "thinking_start":
22460
- if (!options.captureTurnSpans) {
22461
- return;
22462
- }
22463
- this.handleThinkingStart(event);
22464
- return;
22465
- case "thinking_delta":
22466
- if (!options.captureTurnSpans) {
22467
- return;
22468
- }
22469
- this.handleThinkingDelta(event);
22470
- return;
22471
- case "thinking_end":
22472
- if (!options.captureTurnSpans) {
22473
- return;
22474
- }
22475
- this.handleThinkingEnd(event);
22476
- return;
22477
- case "turn":
22478
- if (!options.captureTurnSpans) {
22479
- return;
22480
- }
22481
- this.handleTurn(event);
22482
- return;
22483
- case "tool_start":
22484
- this.handleToolStart(event, options);
22485
- return;
22486
- case "tool_call":
22487
- this.handleToolCall(event);
22488
- return;
22489
- case "task_start":
22490
- this.handleTaskStart(event);
22491
- return;
22492
- case "task":
22493
- this.handleTask(event);
22494
- return;
22495
- case "compaction_start":
22496
- this.handleCompactionStart(event);
22497
- return;
22498
- case "compaction":
22499
- this.handleCompaction(event);
22500
- return;
22501
- default:
22502
- return;
22503
- }
22504
22251
  }
22505
22252
  handleOperationStart(event) {
22506
- if (!isInstrumentedOperation(event.operationKind)) {
22253
+ if (!event.operationId || !isInstrumentedOperation(event.operationKind)) {
22507
22254
  return;
22508
22255
  }
22509
- const state = this.takePendingOperationForEvent(event);
22510
- if (!state) {
22511
- return;
22512
- }
22513
- state.operationId = event.operationId;
22514
- this.activeOperationsById.set(event.operationId, state);
22515
- addScopedOperation(this.activeOperationsByScope, event, state);
22516
- state.metadata = {
22517
- ...state.metadata,
22256
+ const metadata = {
22518
22257
  ...extractEventMetadata(event),
22519
- "flue.operation_id": event.operationId
22258
+ "flue.operation": event.operationKind,
22259
+ provider: "flue"
22520
22260
  };
22521
- safeLog3(state.span, { metadata: state.metadata });
22261
+ const parent = this.parentSpanForEvent(event);
22262
+ const span = startFlueSpan(parent, {
22263
+ name: `flue.${event.operationKind}`,
22264
+ spanAttributes: { type: "task" /* TASK */ },
22265
+ startTime: eventTime(event.timestamp),
22266
+ event: { metadata }
22267
+ });
22268
+ this.operationsById.set(event.operationId, { metadata, span });
22522
22269
  }
22523
22270
  handleOperation(event) {
22524
- const state = event.operationId ? this.activeOperationsById.get(event.operationId) : void 0;
22525
- if (!state) {
22271
+ if (!isInstrumentedOperation(event.operationKind)) {
22526
22272
  return;
22527
22273
  }
22274
+ const state = this.operationsById.get(event.operationId) ?? this.startSyntheticOperation(event);
22275
+ const output = operationOutput(event);
22528
22276
  const metadata = {
22529
22277
  ...state.metadata,
22530
22278
  ...extractEventMetadata(event),
22531
- ...typeof event.durationMs === "number" ? { "flue.duration_ms": event.durationMs } : {},
22532
- ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
22279
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {},
22280
+ ...event.usage ? { "flue.usage": event.usage } : {}
22533
22281
  };
22534
- const metrics = metricsFromUsage(event.usage);
22282
+ this.finishPendingChildrenForOperation(event, output);
22535
22283
  safeLog3(state.span, {
22536
- ...event.error ? { error: errorToString(event.error) } : {},
22284
+ ...event.isError ? { error: errorToString(event.error) } : {},
22537
22285
  metadata,
22538
- ...Object.keys(metrics).length ? { metrics } : {}
22286
+ metrics: durationMetrics2(event.durationMs),
22287
+ output
22539
22288
  });
22289
+ safeEnd(state.span, eventTime(event.timestamp));
22290
+ this.operationsById.delete(event.operationId);
22540
22291
  }
22541
- ensureTurnState(event) {
22542
- const scope = scopeKey(event);
22543
- const existing = this.turnsByScope.get(scope);
22544
- if (existing) {
22545
- return existing;
22292
+ handleTurnRequest(event) {
22293
+ const key = turnKey(event);
22294
+ if (!key) {
22295
+ return;
22546
22296
  }
22547
- const parent = this.parentSpanForEvent(event);
22548
22297
  const metadata = {
22549
22298
  ...extractEventMetadata(event),
22550
- provider: "flue"
22299
+ ...event.api ? { "flue.api": event.api } : {},
22300
+ ...event.model ? { model: event.model, "flue.model": event.model } : {},
22301
+ ...event.provider ? { provider: event.provider } : { provider: "flue" },
22302
+ ...event.provider ? { "flue.provider": event.provider } : {},
22303
+ ...event.purpose ? { "flue.turn_purpose": event.purpose } : {},
22304
+ ...event.reasoning ? { reasoning: event.reasoning } : {},
22305
+ ...event.input?.systemPrompt ? { "flue.system_prompt": event.input.systemPrompt } : {},
22306
+ ...event.input?.tools ? { tools: event.input.tools } : {}
22551
22307
  };
22308
+ const parent = this.parentSpanForTurn(event);
22552
22309
  const span = startFlueSpan(parent, {
22553
- name: "flue.turn",
22554
- spanAttributes: { type: "llm" /* LLM */ }
22310
+ name: `llm:${event.model ?? event.purpose ?? "unknown"}`,
22311
+ spanAttributes: { type: "llm" /* LLM */ },
22312
+ startTime: eventTime(event.timestamp),
22313
+ event: {
22314
+ input: event.input?.messages,
22315
+ metadata
22316
+ }
22555
22317
  });
22556
- const state = {
22557
- metadata,
22558
- span,
22559
- hasThinking: false,
22560
- startTime: getCurrentUnixTimestamp(),
22561
- text: [],
22562
- thinking: [],
22563
- toolCalls: []
22564
- };
22565
- safeLog3(span, { metadata });
22566
- this.turnsByScope.set(scope, state);
22567
- return state;
22318
+ this.logOperationInput(
22319
+ event.operationId,
22320
+ event.input?.messages ?? event.input
22321
+ );
22322
+ this.turnsByKey.set(key, { metadata, span });
22568
22323
  }
22569
22324
  handleTurn(event) {
22570
- const scope = scopeKey(event);
22571
- const state = this.ensureTurnState(event);
22572
- const text = state.text.join("");
22573
- const reasoning = state.finalThinking ?? state.thinking.join("");
22574
- const outputReasoning = reasoning || (state.hasThinking ? "[reasoning stream present; content unavailable]" : void 0);
22325
+ const key = turnKey(event);
22326
+ if (!key) {
22327
+ return;
22328
+ }
22329
+ const state = this.turnsByKey.get(key) ?? this.startSyntheticTurn(event);
22575
22330
  const metadata = {
22576
22331
  ...state.metadata,
22577
22332
  ...extractEventMetadata(event),
22333
+ ...event.api ? { "flue.api": event.api } : {},
22578
22334
  ...event.model ? { model: event.model, "flue.model": event.model } : {},
22335
+ ...event.provider ? { provider: event.provider } : {},
22336
+ ...event.provider ? { "flue.provider": event.provider } : {},
22337
+ ...event.purpose ? { "flue.turn_purpose": event.purpose } : {},
22579
22338
  ...event.stopReason ? { "flue.stop_reason": event.stopReason } : {},
22580
- ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {},
22581
- provider: "flue"
22339
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
22582
22340
  };
22583
22341
  safeLog3(state.span, {
22584
- ...event.error ? { error: errorToString(event.error) } : {},
22342
+ ...event.isError ? { error: errorToString(event.error) } : {},
22585
22343
  metadata,
22586
22344
  metrics: {
22587
- ...durationMsMetrics(event.durationMs),
22345
+ ...durationMetrics2(event.durationMs),
22588
22346
  ...metricsFromUsage(event.usage)
22589
22347
  },
22590
- output: toAssistantOutput(
22591
- text,
22592
- event.stopReason,
22593
- outputReasoning,
22594
- state.toolCalls
22595
- )
22348
+ output: event.output
22596
22349
  });
22597
- state.span.end();
22598
- this.turnsByScope.delete(scope);
22350
+ safeEnd(state.span, eventTime(event.timestamp));
22351
+ this.turnsByKey.delete(key);
22599
22352
  }
22600
- handleThinkingDelta(event) {
22601
- const delta = event.delta;
22602
- if (typeof delta !== "string" || !delta) {
22353
+ handleToolStart(event) {
22354
+ if (!event.toolCallId) {
22603
22355
  return;
22604
22356
  }
22605
- const state = this.ensureTurnState(event);
22606
- state.hasThinking = true;
22607
- state.metadata["flue.thinking"] = true;
22608
- state.thinking.push(delta);
22609
- }
22610
- handleThinkingStart(event) {
22611
- const state = this.ensureTurnState(event);
22612
- state.hasThinking = true;
22613
- state.metadata["flue.thinking"] = true;
22614
- }
22615
- handleThinkingEnd(event) {
22616
- const state = this.ensureTurnState(event);
22617
- state.hasThinking = true;
22618
- state.metadata["flue.thinking"] = true;
22619
- if (typeof event.content === "string" && event.content) {
22620
- state.finalThinking = event.content;
22621
- }
22622
- }
22623
- handleToolStart(event, options) {
22624
- const toolCallId = event.toolCallId;
22625
- if (!toolCallId) {
22626
- return;
22627
- }
22628
- const parent = this.parentSpanForEvent(event);
22629
- const scope = scopeKey(event);
22630
- let turnState = this.turnsByScope.get(scope);
22631
- if (!turnState && parent && options.captureTurnSpans) {
22632
- turnState = this.ensureTurnState(event);
22633
- }
22634
22357
  const metadata = {
22635
22358
  ...extractEventMetadata(event),
22636
22359
  ...event.toolName ? { "flue.tool_name": event.toolName } : {},
22637
- "flue.tool_call_id": toolCallId,
22360
+ "flue.tool_call_id": event.toolCallId,
22638
22361
  provider: "flue"
22639
22362
  };
22363
+ const parent = this.parentSpanForTool(event);
22640
22364
  const span = startFlueSpan(parent, {
22641
- name: `tool: ${event.toolName ?? "unknown"}`,
22642
- spanAttributes: { type: "tool" /* TOOL */ }
22643
- });
22644
- if (turnState) {
22645
- turnState.toolCalls.push({
22646
- args: event.args,
22647
- toolCallId,
22648
- toolName: event.toolName
22649
- });
22650
- }
22651
- safeLog3(span, {
22652
- input: event.args,
22653
- metadata
22654
- });
22655
- this.toolsById.set(toolKey(event), {
22656
- metadata,
22657
- span,
22658
- startTime: getCurrentUnixTimestamp()
22365
+ name: `tool:${event.toolName ?? "unknown"}`,
22366
+ spanAttributes: { type: "tool" /* TOOL */ },
22367
+ startTime: eventTime(event.timestamp),
22368
+ event: {
22369
+ input: event.args,
22370
+ metadata
22371
+ }
22659
22372
  });
22373
+ this.toolsByKey.set(toolKey(event), { metadata, span });
22660
22374
  }
22661
22375
  handleToolCall(event) {
22376
+ if (!event.toolCallId) {
22377
+ return;
22378
+ }
22662
22379
  const key = toolKey(event);
22663
- const state = this.toolsById.get(key) ?? this.startSyntheticToolState(event, event.toolName ?? "unknown");
22380
+ const state = this.toolsByKey.get(key) ?? this.startSyntheticTool(event);
22664
22381
  const metadata = {
22665
22382
  ...state.metadata,
22666
22383
  ...extractEventMetadata(event),
22667
22384
  ...event.toolName ? { "flue.tool_name": event.toolName } : {},
22668
- ...event.toolCallId ? { "flue.tool_call_id": event.toolCallId } : {},
22385
+ "flue.tool_call_id": event.toolCallId,
22669
22386
  ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
22670
22387
  };
22671
22388
  safeLog3(state.span, {
22672
22389
  ...event.isError ? { error: errorToString(event.result) } : {},
22673
22390
  metadata,
22674
- metrics: durationMsMetrics(event.durationMs),
22391
+ metrics: durationMetrics2(event.durationMs),
22675
22392
  output: event.result
22676
22393
  });
22677
- state.span.end();
22678
- this.toolsById.delete(key);
22394
+ safeEnd(state.span, eventTime(event.timestamp));
22395
+ this.toolsByKey.delete(key);
22679
22396
  }
22680
22397
  handleTaskStart(event) {
22681
- const parent = this.parentSpanForEvent(event);
22398
+ if (!event.taskId) {
22399
+ return;
22400
+ }
22682
22401
  const metadata = {
22683
22402
  ...extractEventMetadata(event),
22684
- ...event.role ? { "flue.role": event.role } : {},
22403
+ ...event.agent ? { "flue.agent": event.agent } : {},
22685
22404
  ...event.cwd ? { "flue.cwd": event.cwd } : {},
22686
22405
  "flue.task_id": event.taskId,
22687
22406
  provider: "flue"
22688
22407
  };
22408
+ const parent = this.parentSpanForEvent(event);
22689
22409
  const span = startFlueSpan(parent, {
22690
- name: "flue.task",
22691
- spanAttributes: { type: "task" /* TASK */ }
22692
- });
22693
- safeLog3(span, {
22694
- input: event.prompt,
22695
- metadata
22696
- });
22697
- this.tasksById.set(event.taskId, {
22698
- metadata,
22699
- span,
22700
- startTime: getCurrentUnixTimestamp()
22410
+ name: event.agent ? `task:${event.agent}` : "flue.task",
22411
+ spanAttributes: { type: "task" /* TASK */ },
22412
+ startTime: eventTime(event.timestamp),
22413
+ event: {
22414
+ input: event.prompt,
22415
+ metadata
22416
+ }
22701
22417
  });
22418
+ this.tasksById.set(event.taskId, { metadata, span });
22702
22419
  }
22703
22420
  handleTask(event) {
22704
22421
  const state = this.tasksById.get(event.taskId);
@@ -22710,426 +22427,372 @@ var FluePlugin = class extends BasePlugin {
22710
22427
  metadata: {
22711
22428
  ...state.metadata,
22712
22429
  ...extractEventMetadata(event),
22430
+ ...event.agent ? { "flue.agent": event.agent } : {},
22713
22431
  ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
22714
22432
  },
22715
- metrics: durationMsMetrics(event.durationMs),
22433
+ metrics: durationMetrics2(event.durationMs),
22716
22434
  output: event.result
22717
22435
  });
22718
- state.span.end();
22436
+ safeEnd(state.span, eventTime(event.timestamp));
22719
22437
  this.tasksById.delete(event.taskId);
22720
22438
  }
22721
22439
  handleCompactionStart(event) {
22722
- const operationState = this.operationStateForEvent(event);
22723
- const parent = operationState?.span ?? this.parentSpanForEvent(event);
22440
+ const key = compactionKey(event);
22441
+ const input = {
22442
+ ...event.estimatedTokens !== void 0 ? { estimatedTokens: event.estimatedTokens } : {},
22443
+ ...event.reason ? { reason: event.reason } : {}
22444
+ };
22724
22445
  const metadata = {
22725
22446
  ...extractEventMetadata(event),
22726
22447
  ...event.reason ? { "flue.compaction_reason": event.reason } : {},
22727
22448
  provider: "flue"
22728
22449
  };
22729
- const input = {
22730
- ...typeof event.estimatedTokens === "number" ? { estimatedTokens: event.estimatedTokens } : {},
22731
- ...event.reason ? { reason: event.reason } : {}
22732
- };
22450
+ const parent = this.parentSpanForEvent(event);
22733
22451
  const span = startFlueSpan(parent, {
22734
- name: "flue.compaction",
22735
- spanAttributes: { type: "task" /* TASK */ }
22736
- });
22737
- safeLog3(span, {
22738
- input,
22739
- metadata
22740
- });
22741
- this.compactionsByScope.set(scopeKey(event), {
22742
- input,
22743
- metadata,
22744
- operationState,
22745
- span,
22746
- startTime: getCurrentUnixTimestamp()
22452
+ name: `compaction:${event.reason ?? "unknown"}`,
22453
+ spanAttributes: { type: "task" /* TASK */ },
22454
+ startTime: eventTime(event.timestamp),
22455
+ event: {
22456
+ input,
22457
+ metadata
22458
+ }
22747
22459
  });
22460
+ this.logOperationInput(event.operationId, input);
22461
+ this.compactionsByKey.set(key, { metadata, span });
22748
22462
  }
22749
22463
  handleCompaction(event) {
22750
- const key = scopeKey(event);
22751
- const state = this.compactionsByScope.get(key) ?? this.findCompactionState(event);
22752
- if (!state) {
22753
- return;
22754
- }
22464
+ const key = compactionKey(event);
22465
+ const state = this.compactionsByKey.get(key) ?? this.startSyntheticCompaction(event);
22466
+ const metadata = {
22467
+ ...state.metadata,
22468
+ ...extractEventMetadata(event),
22469
+ ...event.usage ? { "flue.usage": event.usage } : {}
22470
+ };
22755
22471
  safeLog3(state.span, {
22756
- metadata: {
22757
- ...state.metadata,
22758
- ...extractEventMetadata(event),
22759
- ...typeof event.messagesBefore === "number" ? { "flue.messages_before": event.messagesBefore } : {},
22760
- ...typeof event.messagesAfter === "number" ? { "flue.messages_after": event.messagesAfter } : {}
22761
- },
22472
+ metadata,
22762
22473
  metrics: {
22763
- ...durationMsMetrics(event.durationMs),
22764
- ...metricsFromUsage(event.usage)
22474
+ ...durationMetrics2(event.durationMs),
22475
+ ...typeof event.messagesBefore === "number" ? { messages_before: event.messagesBefore } : {},
22476
+ ...typeof event.messagesAfter === "number" ? { messages_after: event.messagesAfter } : {}
22765
22477
  },
22766
22478
  output: {
22767
22479
  messagesAfter: event.messagesAfter,
22768
22480
  messagesBefore: event.messagesBefore
22769
22481
  }
22770
22482
  });
22771
- state.span.end();
22772
- this.deleteCompactionState(state);
22483
+ safeEnd(state.span, eventTime(event.timestamp));
22484
+ this.compactionsByKey.delete(key);
22773
22485
  }
22774
- findCompactionState(event) {
22775
- const operationState = this.operationStateForEvent(event);
22776
- for (const state of this.compactionsByScope.values()) {
22777
- if (operationState && state.operationState === operationState) {
22778
- return state;
22486
+ parentSpanForTurn(event) {
22487
+ if (event.purpose === "compaction" || event.purpose === "compaction_prefix") {
22488
+ const compaction = this.compactionsByKey.get(compactionKey(event));
22489
+ if (compaction) {
22490
+ return compaction.span;
22779
22491
  }
22780
22492
  }
22781
- return void 0;
22493
+ return this.parentSpanForEvent(event);
22782
22494
  }
22783
- finishCompactionsForOperation(operationState) {
22784
- for (const state of [...this.compactionsByScope.values()]) {
22785
- if (state.operationState !== operationState) {
22786
- continue;
22495
+ parentSpanForEvent(event) {
22496
+ const turn = turnKey(event);
22497
+ if (turn) {
22498
+ const turnState = this.turnsByKey.get(turn);
22499
+ if (turnState) {
22500
+ return turnState.span;
22787
22501
  }
22788
- safeLog3(state.span, {
22789
- input: state.input,
22790
- metadata: state.metadata,
22791
- metrics: {
22792
- ...buildDurationMetrics3(state.startTime)
22793
- },
22794
- output: { completed: true }
22795
- });
22796
- state.span.end();
22797
- this.deleteCompactionState(state);
22798
22502
  }
22799
- }
22800
- deleteCompactionState(state) {
22801
- for (const [key, candidate] of this.compactionsByScope) {
22802
- if (candidate !== state) {
22803
- continue;
22503
+ if (event.taskId) {
22504
+ const task = this.tasksById.get(event.taskId);
22505
+ if (task) {
22506
+ return task.span;
22804
22507
  }
22805
- this.compactionsByScope.delete(key);
22806
- return;
22807
22508
  }
22808
- }
22809
- startSyntheticToolState(event, toolName) {
22810
- const parent = this.parentSpanForEvent(event);
22811
- const metadata = {
22812
- ...extractEventMetadata(event),
22813
- ...event.toolCallId ? { "flue.tool_call_id": event.toolCallId } : {},
22814
- "flue.tool_name": toolName,
22815
- provider: "flue"
22816
- };
22817
- const span = startFlueSpan(parent, {
22818
- name: `tool: ${toolName}`,
22819
- spanAttributes: { type: "tool" /* TOOL */ }
22820
- });
22821
- safeLog3(span, { metadata });
22822
- return { metadata, span, startTime: getCurrentUnixTimestamp() };
22823
- }
22824
- operationStateForEvent(event) {
22825
22509
  if (event.operationId) {
22826
- const operation = this.activeOperationsById.get(event.operationId) ?? this.promotePendingOperationForEvent(event);
22510
+ const operation = this.operationsById.get(event.operationId);
22827
22511
  if (operation) {
22828
- return operation;
22512
+ return operation.span;
22829
22513
  }
22830
22514
  }
22831
- return this.activeOperationForEventScope(event) ?? this.pendingOperationForEventScope(event);
22515
+ if (event.runId) {
22516
+ return this.runsById.get(event.runId)?.span;
22517
+ }
22518
+ return void 0;
22832
22519
  }
22833
- parentSpanForEvent(event) {
22520
+ parentSpanForTool(event) {
22521
+ if (event.taskId) {
22522
+ const task = this.tasksById.get(event.taskId);
22523
+ if (task) {
22524
+ return task.span;
22525
+ }
22526
+ }
22834
22527
  if (event.operationId) {
22835
- const operation = this.operationStateForEvent(event);
22528
+ const operation = this.operationsById.get(event.operationId);
22836
22529
  if (operation) {
22837
22530
  return operation.span;
22838
22531
  }
22839
22532
  }
22840
- if (event.taskId) {
22841
- return this.tasksById.get(event.taskId)?.span;
22533
+ if (event.runId) {
22534
+ return this.runsById.get(event.runId)?.span;
22842
22535
  }
22843
- return this.operationStateForEvent(event)?.span;
22536
+ return void 0;
22844
22537
  }
22845
- promotePendingOperationForEvent(event) {
22846
- if (!event.operationId) {
22847
- return void 0;
22538
+ logOperationInput(operationId, input) {
22539
+ if (!operationId || input === void 0) {
22540
+ return;
22848
22541
  }
22849
- const scopePrefixes = operationScopePrefixes(event);
22850
- for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
22851
- if (!candidateQueue.length || !operationKeyMatchesScopes(candidateKey, scopePrefixes)) {
22542
+ const operation = this.operationsById.get(operationId);
22543
+ if (!operation || operation.loggedInput) {
22544
+ return;
22545
+ }
22546
+ safeLog3(operation.span, { input });
22547
+ operation.loggedInput = true;
22548
+ }
22549
+ startSyntheticOperation(event) {
22550
+ const metadata = {
22551
+ ...extractEventMetadata(event),
22552
+ "flue.operation": event.operationKind,
22553
+ provider: "flue"
22554
+ };
22555
+ const span = startFlueSpan(this.parentSpanForEvent(event), {
22556
+ name: `flue.${event.operationKind}`,
22557
+ spanAttributes: { type: "task" /* TASK */ },
22558
+ startTime: eventTime(event.timestamp),
22559
+ event: { metadata }
22560
+ });
22561
+ return { metadata, span };
22562
+ }
22563
+ startSyntheticTurn(event) {
22564
+ const metadata = {
22565
+ ...extractEventMetadata(event),
22566
+ ...event.api ? { "flue.api": event.api } : {},
22567
+ ...event.model ? { model: event.model, "flue.model": event.model } : {},
22568
+ ...event.provider ? { provider: event.provider } : { provider: "flue" },
22569
+ ...event.provider ? { "flue.provider": event.provider } : {},
22570
+ ...event.purpose ? { "flue.turn_purpose": event.purpose } : {}
22571
+ };
22572
+ const span = startFlueSpan(this.parentSpanForEvent(event), {
22573
+ name: `llm:${event.model ?? event.purpose ?? "unknown"}`,
22574
+ spanAttributes: { type: "llm" /* LLM */ },
22575
+ startTime: eventTime(event.timestamp),
22576
+ event: { metadata }
22577
+ });
22578
+ return { metadata, span };
22579
+ }
22580
+ startSyntheticTool(event) {
22581
+ const metadata = {
22582
+ ...extractEventMetadata(event),
22583
+ ...event.toolName ? { "flue.tool_name": event.toolName } : {},
22584
+ "flue.tool_call_id": event.toolCallId,
22585
+ provider: "flue"
22586
+ };
22587
+ const span = startFlueSpan(this.parentSpanForTool(event), {
22588
+ name: `tool:${event.toolName ?? "unknown"}`,
22589
+ spanAttributes: { type: "tool" /* TOOL */ },
22590
+ startTime: eventTime(event.timestamp),
22591
+ event: { metadata }
22592
+ });
22593
+ return { metadata, span };
22594
+ }
22595
+ startSyntheticCompaction(event) {
22596
+ const metadata = {
22597
+ ...extractEventMetadata(event),
22598
+ provider: "flue"
22599
+ };
22600
+ const span = startFlueSpan(this.parentSpanForEvent(event), {
22601
+ name: "compaction:unknown",
22602
+ spanAttributes: { type: "task" /* TASK */ },
22603
+ startTime: eventTime(event.timestamp),
22604
+ event: { metadata }
22605
+ });
22606
+ return { metadata, span };
22607
+ }
22608
+ finishPendingChildrenForOperation(event, operationOutput2) {
22609
+ const endTime = eventTime(event.timestamp);
22610
+ const usage = event.usage ?? usageFromOperationResult(event.result);
22611
+ const turnEntries = [...this.turnsByKey].filter(
22612
+ ([, state]) => stateMatchesOperation(state, event.operationId)
22613
+ );
22614
+ turnEntries.forEach(([key, state], index) => {
22615
+ const shouldLogOperationOutput = (event.operationKind === "prompt" || event.operationKind === "skill") && index === turnEntries.length - 1 && operationOutput2 !== void 0;
22616
+ safeLog3(state.span, {
22617
+ metadata: state.metadata,
22618
+ metrics: metricsFromUsage(usage),
22619
+ ...shouldLogOperationOutput ? { output: operationOutput2 } : {}
22620
+ });
22621
+ safeEnd(state.span, endTime);
22622
+ this.turnsByKey.delete(key);
22623
+ });
22624
+ for (const [key, state] of this.toolsByKey) {
22625
+ if (!stateMatchesOperation(state, event.operationId)) {
22852
22626
  continue;
22853
22627
  }
22854
- const state = candidateQueue.shift();
22855
- if (!state) {
22856
- return void 0;
22628
+ safeEnd(state.span, endTime);
22629
+ this.toolsByKey.delete(key);
22630
+ }
22631
+ for (const [key, state] of this.tasksById) {
22632
+ if (!stateMatchesOperation(state, event.operationId)) {
22633
+ continue;
22857
22634
  }
22858
- state.operationId = event.operationId;
22859
- this.activeOperationsById.set(event.operationId, state);
22860
- addScopedOperation(this.activeOperationsByScope, event, state);
22861
- state.metadata = {
22862
- ...state.metadata,
22863
- ...extractEventMetadata(event),
22864
- "flue.operation_id": event.operationId
22865
- };
22866
- safeLog3(state.span, { metadata: state.metadata });
22867
- return state;
22635
+ safeEnd(state.span, endTime);
22636
+ this.tasksById.delete(key);
22868
22637
  }
22869
- return void 0;
22870
- }
22871
- activeOperationForEventScope(event) {
22872
- for (const scope of operationScopeNames(event)) {
22873
- const operations = this.activeOperationsByScope.get(scope);
22874
- if (operations?.length) {
22875
- return operations[operations.length - 1];
22638
+ for (const [key, state] of this.compactionsByKey) {
22639
+ if (!stateMatchesOperation(state, event.operationId)) {
22640
+ continue;
22876
22641
  }
22642
+ safeLog3(state.span, {
22643
+ metadata: state.metadata,
22644
+ metrics: durationMetrics2(event.durationMs),
22645
+ output: { completed: true }
22646
+ });
22647
+ safeEnd(state.span, eventTime(event.timestamp));
22648
+ this.compactionsByKey.delete(key);
22877
22649
  }
22878
- return void 0;
22879
22650
  }
22880
- pendingOperationForEventScope(event) {
22881
- const scopePrefixes = operationScopePrefixes(event);
22882
- for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
22883
- if (!candidateQueue.length || !operationKeyMatchesScopes(candidateKey, scopePrefixes)) {
22651
+ finishPendingSpansForRun(event) {
22652
+ const endTime = eventTime(event.timestamp);
22653
+ for (const [key, state] of this.toolsByKey) {
22654
+ if (!stateMatchesRun(state, event.runId)) {
22884
22655
  continue;
22885
22656
  }
22886
- return candidateQueue[0];
22657
+ safeEnd(state.span, endTime);
22658
+ this.toolsByKey.delete(key);
22887
22659
  }
22888
- return void 0;
22889
- }
22890
- takePendingOperationForEvent(event) {
22891
- const key = operationKey(event.session, event.operationKind);
22892
- const queue = this.pendingOperationsByKey.get(key);
22893
- if (queue?.length) {
22894
- return queue.shift();
22660
+ for (const [key, state] of this.turnsByKey) {
22661
+ if (!stateMatchesRun(state, event.runId)) {
22662
+ continue;
22663
+ }
22664
+ safeEnd(state.span, endTime);
22665
+ this.turnsByKey.delete(key);
22895
22666
  }
22896
- for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
22897
- if (candidateKey.endsWith(`::${event.operationKind}`) && candidateQueue.length) {
22898
- return candidateQueue.shift();
22667
+ for (const [key, state] of this.tasksById) {
22668
+ if (!stateMatchesRun(state, event.runId)) {
22669
+ continue;
22899
22670
  }
22671
+ safeEnd(state.span, endTime);
22672
+ this.tasksById.delete(key);
22900
22673
  }
22901
- return void 0;
22902
- }
22903
- pendingOperationQueue(key) {
22904
- const existing = this.pendingOperationsByKey.get(key);
22905
- if (existing) {
22906
- return existing;
22674
+ for (const [key, state] of this.compactionsByKey) {
22675
+ if (!stateMatchesRun(state, event.runId)) {
22676
+ continue;
22677
+ }
22678
+ safeLog3(state.span, {
22679
+ metadata: state.metadata,
22680
+ output: { completed: true }
22681
+ });
22682
+ safeEnd(state.span, endTime);
22683
+ this.compactionsByKey.delete(key);
22684
+ }
22685
+ for (const [key, state] of this.operationsById) {
22686
+ if (!stateMatchesRun(state, event.runId)) {
22687
+ continue;
22688
+ }
22689
+ safeLog3(state.span, {
22690
+ metadata: state.metadata,
22691
+ ...state.metadata["flue.operation"] === "compact" ? { output: { completed: true } } : {}
22692
+ });
22693
+ safeEnd(state.span, endTime);
22694
+ this.operationsById.delete(key);
22907
22695
  }
22908
- const queue = [];
22909
- this.pendingOperationsByKey.set(key, queue);
22910
- return queue;
22911
22696
  }
22912
22697
  };
22913
22698
  function isInstrumentedOperation(operation) {
22914
- return operation === "prompt" || operation === "skill" || operation === "task" || operation === "compact";
22699
+ return operation === "prompt" || operation === "skill" || operation === "compact";
22915
22700
  }
22916
- function getSessionName(session) {
22917
- return typeof session?.name === "string" ? session.name : void 0;
22918
- }
22919
- function operationKey(sessionName, operation) {
22920
- return `${sessionName ?? "unknown"}::${operation}`;
22921
- }
22922
- function operationScopePrefixes(event) {
22923
- const scopes = /* @__PURE__ */ new Set();
22924
- for (const scope of operationScopeNames(event)) {
22925
- scopes.add(`${scope}::`);
22926
- }
22927
- return scopes;
22928
- }
22929
- function operationKeyMatchesScopes(key, scopes) {
22930
- for (const scope of scopes) {
22931
- if (key.startsWith(scope)) {
22932
- return true;
22933
- }
22934
- }
22935
- return false;
22936
- }
22937
- function operationScopeNames(event) {
22938
- const scopes = /* @__PURE__ */ new Set();
22939
- if (event.session) {
22940
- scopes.add(event.session);
22941
- }
22942
- if (event.parentSession) {
22943
- scopes.add(event.parentSession);
22944
- }
22945
- if (!scopes.size) {
22946
- scopes.add("unknown");
22947
- }
22948
- return scopes;
22949
- }
22950
- function addScopedOperation(operationsByScope, event, state) {
22951
- for (const scope of operationScopeNames(event)) {
22952
- addOperationToScope(operationsByScope, scope, state);
22953
- }
22954
- }
22955
- function addOperationToScope(operationsByScope, scope, state) {
22956
- const operations = operationsByScope.get(scope);
22957
- if (operations) {
22958
- if (!operations.includes(state)) {
22959
- operations.push(state);
22960
- }
22961
- } else {
22962
- operationsByScope.set(scope, [state]);
22963
- }
22964
- }
22965
- function removeScopedOperation(operationsByScope, state) {
22966
- for (const [scope, operations] of operationsByScope) {
22967
- const index = operations.indexOf(state);
22968
- if (index === -1) {
22969
- continue;
22970
- }
22971
- operations.splice(index, 1);
22972
- if (operations.length === 0) {
22973
- operationsByScope.delete(scope);
22974
- }
22975
- }
22976
- }
22977
- function removePendingOperation(pendingOperationsByKey, state) {
22978
- for (const [key, queue] of pendingOperationsByKey) {
22979
- const index = queue.indexOf(state);
22980
- if (index === -1) {
22981
- continue;
22982
- }
22983
- queue.splice(index, 1);
22984
- if (queue.length === 0) {
22985
- pendingOperationsByKey.delete(key);
22986
- }
22987
- return;
22988
- }
22989
- }
22990
- function extractSessionMetadata(session) {
22991
- const sessionName = getSessionName(session);
22992
- return sessionName ? { "flue.session": sessionName } : {};
22993
- }
22994
- function extractEventMetadata(event) {
22701
+ function extractEventMetadata(event, ctx) {
22995
22702
  return {
22996
22703
  ...event.runId ? { "flue.run_id": event.runId } : {},
22704
+ ...event.instanceId ? { "flue.instance_id": event.instanceId } : {},
22705
+ ...event.dispatchId ? { "flue.dispatch_id": event.dispatchId } : {},
22997
22706
  ...typeof event.eventIndex === "number" ? { "flue.event_index": event.eventIndex } : {},
22998
22707
  ...event.session ? { "flue.session": event.session } : {},
22999
22708
  ...event.parentSession ? { "flue.parent_session": event.parentSession } : {},
23000
22709
  ...event.harness ? { "flue.harness": event.harness } : {},
23001
22710
  ...event.taskId ? { "flue.task_id": event.taskId } : {},
23002
- ...event.operationId ? { "flue.operation_id": event.operationId } : {}
22711
+ ...event.operationId ? { "flue.operation_id": event.operationId } : {},
22712
+ ...event.turnId ? { "flue.turn_id": event.turnId } : {},
22713
+ ...typeof ctx?.id === "string" ? { "flue.context_id": ctx.id } : {},
22714
+ ...typeof ctx?.runId === "string" ? { "flue.context_run_id": ctx.runId } : {}
23003
22715
  };
23004
22716
  }
23005
- function extractOperationInput(operation, args) {
23006
- switch (operation) {
23007
- case "prompt":
23008
- case "task":
23009
- return args[0];
23010
- case "skill":
23011
- return {
23012
- args: getOptionObject(args[1])?.args,
23013
- name: args[0]
23014
- };
23015
- case "compact":
23016
- return void 0;
22717
+ function extractPayloadMetadata(payload) {
22718
+ if (!isObjectLike(payload)) {
22719
+ return {};
23017
22720
  }
22721
+ const metadata = Reflect.get(payload, "metadata");
22722
+ if (!isObjectLike(metadata)) {
22723
+ return {};
22724
+ }
22725
+ return Object.fromEntries(Object.entries(metadata));
23018
22726
  }
23019
- function extractOperationInputMetadata(operation, args) {
23020
- const options = getOptionObject(args[1]);
23021
- return {
23022
- ...operation === "skill" && typeof args[0] === "string" ? { "flue.skill_name": args[0] } : {},
23023
- ...options?.model ? { model: options.model, "flue.model": options.model } : {},
23024
- ...options?.role ? { "flue.role": options.role } : {},
23025
- ...options?.thinkingLevel ? { "flue.thinking_level": options.thinkingLevel } : {},
23026
- ...typeof options?.cwd === "string" ? { "flue.cwd": options.cwd } : {},
23027
- ...Array.isArray(options?.tools) ? {
23028
- "flue.tools_count": options.tools.length,
23029
- tools: summarizeTools(options.tools)
23030
- } : {},
23031
- ...Array.isArray(options?.images) ? { "flue.images_count": options.images.length } : {},
23032
- ...options?.result || options?.schema ? { "flue.result_schema": true } : {}
23033
- };
23034
- }
23035
- function getOptionObject(value) {
23036
- return isObject(value) ? value : void 0;
23037
- }
23038
- function summarizeTools(tools) {
23039
- return tools.flatMap((tool) => {
23040
- if (!isObject(tool)) {
23041
- return [];
23042
- }
23043
- const name = typeof tool.name === "string" ? tool.name : void 0;
23044
- if (!name) {
23045
- return [];
23046
- }
23047
- return [
23048
- {
23049
- function: {
23050
- description: typeof tool.description === "string" ? tool.description : void 0,
23051
- name,
23052
- parameters: tool.parameters
23053
- },
23054
- type: "function"
23055
- }
23056
- ];
23057
- });
22727
+ function operationOutput(event) {
22728
+ if (event.operationKind === "prompt" || event.operationKind === "skill") {
22729
+ return llmResultFromOperationResult(event.result);
22730
+ }
22731
+ return event.result ?? (event.operationKind === "compact" ? { completed: true } : void 0);
23058
22732
  }
23059
- function extractPromptResponseMetadata(result) {
23060
- const modelId = result?.model && typeof result.model.id === "string" ? result.model.id : void 0;
23061
- return modelId ? {
23062
- model: modelId,
23063
- "flue.model": modelId
23064
- } : {};
22733
+ function llmResultFromOperationResult(result) {
22734
+ if (!isObjectLike(result)) {
22735
+ return result;
22736
+ }
22737
+ const text = Reflect.get(result, "text");
22738
+ return text === void 0 ? result : text;
23065
22739
  }
23066
- function extractOperationOutput(result) {
23067
- if (!result) {
22740
+ function usageFromOperationResult(result) {
22741
+ if (!isObjectLike(result)) {
23068
22742
  return void 0;
23069
22743
  }
23070
- if ("data" in result) {
23071
- return result.data;
23072
- }
23073
- if ("text" in result) {
23074
- return result.text;
23075
- }
23076
- return result;
22744
+ return Reflect.get(result, "usage");
23077
22745
  }
23078
22746
  function metricsFromUsage(usage) {
22747
+ if (!isObjectLike(usage)) {
22748
+ return {};
22749
+ }
22750
+ const cacheRead = Reflect.get(usage, "cacheRead");
22751
+ const cacheWrite = Reflect.get(usage, "cacheWrite");
22752
+ const cost = Reflect.get(usage, "cost");
22753
+ const input = Reflect.get(usage, "input");
22754
+ const output = Reflect.get(usage, "output");
22755
+ const totalTokens = Reflect.get(usage, "totalTokens");
22756
+ const totalCost = isObjectLike(cost) ? Reflect.get(cost, "total") : void 0;
23079
22757
  return {
23080
- ...typeof usage?.input === "number" ? { prompt_tokens: usage.input } : {},
23081
- ...typeof usage?.output === "number" ? { completion_tokens: usage.output } : {},
23082
- ...typeof usage?.cacheRead === "number" ? { prompt_cached_tokens: usage.cacheRead } : {},
23083
- ...typeof usage?.cacheWrite === "number" ? { prompt_cache_creation_tokens: usage.cacheWrite } : {},
23084
- ...typeof usage?.totalTokens === "number" ? { tokens: usage.totalTokens } : {},
23085
- ...typeof usage?.cost?.total === "number" ? { estimated_cost: usage.cost.total } : {}
23086
- };
23087
- }
23088
- function buildDurationMetrics3(startTime) {
23089
- return {
23090
- duration_ms: Math.max(0, (getCurrentUnixTimestamp() - startTime) * 1e3)
22758
+ ...typeof input === "number" ? { prompt_tokens: input } : {},
22759
+ ...typeof output === "number" ? { completion_tokens: output } : {},
22760
+ ...typeof cacheRead === "number" ? { prompt_cached_tokens: cacheRead } : {},
22761
+ ...typeof cacheWrite === "number" ? { prompt_cache_creation_tokens: cacheWrite } : {},
22762
+ ...typeof totalTokens === "number" ? { tokens: totalTokens } : {},
22763
+ ...typeof totalCost === "number" ? { estimated_cost: totalCost } : {}
23091
22764
  };
23092
22765
  }
23093
- function durationMsMetrics(durationMs) {
22766
+ function durationMetrics2(durationMs) {
23094
22767
  return typeof durationMs === "number" ? { duration_ms: durationMs } : {};
23095
22768
  }
23096
- function scopeKey(event) {
23097
- if (event.operationId) {
23098
- return `operation:${event.operationId}`;
23099
- }
23100
- if (event.taskId) {
23101
- return `task:${event.taskId}`;
23102
- }
23103
- if (event.session) {
23104
- return `session:${event.session}`;
22769
+ function eventTime(value) {
22770
+ if (typeof value !== "string") {
22771
+ return void 0;
23105
22772
  }
23106
- return "flue:unknown";
22773
+ const timestamp = Date.parse(value);
22774
+ return Number.isFinite(timestamp) ? timestamp / 1e3 : void 0;
22775
+ }
22776
+ function turnKey(event) {
22777
+ return event.turnId;
23107
22778
  }
23108
22779
  function toolKey(event) {
23109
- return `${scopeKey(event)}::tool:${event.toolCallId ?? "unknown"}`;
22780
+ return `${event.turnId ?? event.operationId ?? event.taskId ?? event.runId ?? "unknown"}:${event.toolCallId ?? "unknown"}`;
23110
22781
  }
23111
- function toAssistantOutput(text, finishReason, reasoning, toolCalls) {
22782
+ function compactionKey(event) {
23112
22783
  return [
23113
- {
23114
- finish_reason: finishReason ?? "stop",
23115
- index: 0,
23116
- message: {
23117
- content: text,
23118
- ...reasoning ? { reasoning } : {},
23119
- role: "assistant",
23120
- ...toolCalls?.length ? {
23121
- tool_calls: toolCalls.map((toolCall) => ({
23122
- function: {
23123
- arguments: toolCall.args === void 0 ? "{}" : JSON.stringify(toolCall.args),
23124
- name: toolCall.toolName ?? "unknown"
23125
- },
23126
- ...toolCall.toolCallId ? { id: toolCall.toolCallId } : {},
23127
- type: "function"
23128
- }))
23129
- } : {}
23130
- }
23131
- }
23132
- ];
22784
+ event.instanceId ?? "",
22785
+ event.runId ?? "",
22786
+ event.session ?? "",
22787
+ event.operationId ?? "",
22788
+ event.taskId ?? ""
22789
+ ].join(":");
22790
+ }
22791
+ function stateMatchesOperation(state, operationId) {
22792
+ return state.metadata["flue.operation_id"] === operationId;
22793
+ }
22794
+ function stateMatchesRun(state, runId) {
22795
+ return state.metadata["flue.run_id"] === runId;
23133
22796
  }
23134
22797
  function startFlueSpan(parent, args) {
23135
22798
  return parent ? withCurrent(parent, () => startSpan(args)) : startSpan(args);
@@ -23141,6 +22804,13 @@ function safeLog3(span, event) {
23141
22804
  logInstrumentationError3("Flue span log", error);
23142
22805
  }
23143
22806
  }
22807
+ function safeEnd(span, endTime) {
22808
+ try {
22809
+ span.end(endTime === void 0 ? void 0 : { endTime });
22810
+ } catch (error) {
22811
+ logInstrumentationError3("Flue span end", error);
22812
+ }
22813
+ }
23144
22814
  function errorToString(error) {
23145
22815
  if (error instanceof Error) {
23146
22816
  return error.message;
@@ -23634,7 +23304,7 @@ var BraintrustPlugin = class extends BasePlugin {
23634
23304
  this.config = config;
23635
23305
  }
23636
23306
  onEnable() {
23637
- const integrations = this.config.integrations || {};
23307
+ const integrations = this.config.integrations ?? {};
23638
23308
  if (integrations.openai !== false) {
23639
23309
  this.openaiPlugin = new OpenAIPlugin();
23640
23310
  this.openaiPlugin.enable();
@@ -23699,7 +23369,7 @@ var BraintrustPlugin = class extends BasePlugin {
23699
23369
  this.genkitPlugin = new GenkitPlugin();
23700
23370
  this.genkitPlugin.enable();
23701
23371
  }
23702
- if (getIntegrationConfig(integrations, "gitHubCopilot") !== false) {
23372
+ if (integrations.gitHubCopilot !== false) {
23703
23373
  this.gitHubCopilotPlugin = new GitHubCopilotPlugin();
23704
23374
  this.gitHubCopilotPlugin.enable();
23705
23375
  }
@@ -23812,6 +23482,7 @@ var envIntegrationAliases = {
23812
23482
  cursorsdk: "cursorSDK",
23813
23483
  flue: "flue",
23814
23484
  "flue-runtime": "flue",
23485
+ mastra: "mastra",
23815
23486
  "openai-agents": "openAIAgents",
23816
23487
  openaiagents: "openAIAgents",
23817
23488
  "openai-agents-core": "openAIAgents",
@@ -23854,6 +23525,7 @@ function getDefaultInstrumentationIntegrations() {
23854
23525
  cursor: true,
23855
23526
  cursorSDK: true,
23856
23527
  flue: true,
23528
+ mastra: true,
23857
23529
  openAIAgents: true,
23858
23530
  openrouter: true,
23859
23531
  openrouterAgent: true,
@@ -23978,6 +23650,7 @@ function configureInstrumentation(config) {
23978
23650
  BasePlugin,
23979
23651
  BraintrustPlugin,
23980
23652
  OpenAIAgentsTraceProcessor,
23653
+ braintrustFlueObserver,
23981
23654
  configureInstrumentation,
23982
23655
  createChannelName,
23983
23656
  isValidChannelName,