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