braintrust 3.26.0 → 3.27.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 (57) hide show
  1. package/dev/dist/index.js +2440 -1067
  2. package/dev/dist/index.mjs +1644 -271
  3. package/dist/apply-auto-instrumentation.js +320 -261
  4. package/dist/apply-auto-instrumentation.mjs +118 -59
  5. package/dist/auto-instrumentations/bundler/esbuild.cjs +377 -59
  6. package/dist/auto-instrumentations/bundler/esbuild.mjs +5 -4
  7. package/dist/auto-instrumentations/bundler/next.cjs +379 -61
  8. package/dist/auto-instrumentations/bundler/next.mjs +6 -5
  9. package/dist/auto-instrumentations/bundler/rollup.cjs +377 -59
  10. package/dist/auto-instrumentations/bundler/rollup.mjs +5 -4
  11. package/dist/auto-instrumentations/bundler/vite.cjs +406 -60
  12. package/dist/auto-instrumentations/bundler/vite.mjs +34 -5
  13. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +363 -59
  14. package/dist/auto-instrumentations/bundler/webpack.cjs +377 -59
  15. package/dist/auto-instrumentations/bundler/webpack.mjs +6 -5
  16. package/dist/auto-instrumentations/{chunk-AKKPLXTP.mjs → chunk-2AMDGD65.mjs} +160 -57
  17. package/dist/auto-instrumentations/{chunk-V5LEODRX.mjs → chunk-BW33ULMW.mjs} +1 -1
  18. package/dist/auto-instrumentations/{chunk-XYN63IG5.mjs → chunk-I55G56ZL.mjs} +21 -58
  19. package/dist/auto-instrumentations/{chunk-NRE4QUQR.mjs → chunk-JPUFNW7X.mjs} +140 -0
  20. package/dist/auto-instrumentations/{chunk-6E22MYSW.mjs → chunk-XEYKUBLY.mjs} +30 -4
  21. package/dist/auto-instrumentations/{chunk-XZHHE4Y3.mjs → chunk-ZNHTSSGI.mjs} +155 -2
  22. package/dist/auto-instrumentations/hook.mjs +161 -3
  23. package/dist/auto-instrumentations/index.cjs +296 -1
  24. package/dist/auto-instrumentations/index.d.mts +5 -1
  25. package/dist/auto-instrumentations/index.d.ts +5 -1
  26. package/dist/auto-instrumentations/index.mjs +6 -2
  27. package/dist/auto-instrumentations/loader/cjs-patch.cjs +105 -57
  28. package/dist/auto-instrumentations/loader/cjs-patch.mjs +2 -2
  29. package/dist/auto-instrumentations/loader/esm-hook.mjs +1 -1
  30. package/dist/browser.d.mts +57 -5
  31. package/dist/browser.d.ts +57 -5
  32. package/dist/browser.js +1855 -285
  33. package/dist/browser.mjs +1855 -285
  34. package/dist/{chunk-PN7EWK66.mjs → chunk-CZM5JIQL.mjs} +209 -1
  35. package/dist/{chunk-VYNNEKSA.js → chunk-MF7NU6BT.js} +216 -8
  36. package/dist/{chunk-VRZZQPAA.js → chunk-QRHGVBKU.js} +2403 -1220
  37. package/dist/{chunk-XIZOM2HO.mjs → chunk-YKD22IMR.mjs} +1454 -271
  38. package/dist/cli.js +1645 -272
  39. package/dist/edge-light.d.mts +1 -1
  40. package/dist/edge-light.d.ts +1 -1
  41. package/dist/edge-light.js +1855 -285
  42. package/dist/edge-light.mjs +1855 -285
  43. package/dist/index.d.mts +57 -5
  44. package/dist/index.d.ts +57 -5
  45. package/dist/index.js +600 -413
  46. package/dist/index.mjs +201 -14
  47. package/dist/instrumentation/index.d.mts +8 -0
  48. package/dist/instrumentation/index.d.ts +8 -0
  49. package/dist/instrumentation/index.js +1839 -466
  50. package/dist/instrumentation/index.mjs +1839 -466
  51. package/dist/vitest-evals-reporter.js +45 -43
  52. package/dist/vitest-evals-reporter.mjs +5 -3
  53. package/dist/workerd.d.mts +1 -1
  54. package/dist/workerd.d.ts +1 -1
  55. package/dist/workerd.js +1855 -285
  56. package/dist/workerd.mjs +1855 -285
  57. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1236,7 +1236,7 @@ var require_package = __commonJS({
1236
1236
  "package.json"(exports2, module2) {
1237
1237
  module2.exports = {
1238
1238
  name: "braintrust",
1239
- version: "3.26.0",
1239
+ version: "3.27.0",
1240
1240
  description: "SDK for integrating Braintrust",
1241
1241
  repository: {
1242
1242
  type: "git",
@@ -1528,8 +1528,10 @@ var GLOBAL_INSTRUMENTATION_HOOKS_KEY = "__braintrust_instrumentation_hooks";
1528
1528
  var GLOBAL_INSTRUMENTATION_HOOKS_PROTOCOL_VERSION = 1;
1529
1529
  var GLOBAL_INSTRUMENTATION_HOOKS_REGISTRY_BRAND = "braintrust.global-instrumentation-hooks.registry";
1530
1530
  var GLOBAL_INSTRUMENTATION_HOOK_BRAND = "braintrust.global-instrumentation-hooks.hook";
1531
+ var GLOBAL_INVOCATION_HOOK_BRAND = "braintrust.global-instrumentation-hooks.invocation-hook";
1531
1532
  var registryBrand = Symbol.for(GLOBAL_INSTRUMENTATION_HOOKS_REGISTRY_BRAND);
1532
1533
  var hookBrand = Symbol.for(GLOBAL_INSTRUMENTATION_HOOK_BRAND);
1534
+ var invocationHookBrand = Symbol.for(GLOBAL_INVOCATION_HOOK_BRAND);
1533
1535
  var errorReporter;
1534
1536
  function setGlobalHookErrorReporter(reporter) {
1535
1537
  const previousReporter = errorReporter;
@@ -1674,12 +1676,69 @@ var traceEvents = [
1674
1676
  "asyncEnd",
1675
1677
  "error"
1676
1678
  ];
1679
+ function traceInvocation(hook, operator, target, thisArg, args, additional, callbackIndex = -1) {
1680
+ const context2 = {
1681
+ ...additional,
1682
+ arguments: args,
1683
+ self: thisArg
1684
+ };
1685
+ const invoke2 = () => hook.invoke(target, thisArg, args, additional);
1686
+ if (operator === "traceCallback") {
1687
+ return hook.traceCallback(invoke2, callbackIndex, context2);
1688
+ }
1689
+ if (operator === "tracePromise") {
1690
+ return hook.tracePromise(invoke2, context2);
1691
+ }
1692
+ return hook.traceSync(invoke2, context2);
1693
+ }
1694
+ var InvocationHook = class {
1695
+ interceptors = [];
1696
+ get hasInterceptors() {
1697
+ return this.interceptors.length > 0;
1698
+ }
1699
+ intercept(interceptor) {
1700
+ if (typeof interceptor !== "function") {
1701
+ throw new TypeError("interceptor must be a function");
1702
+ }
1703
+ this.interceptors = [...this.interceptors, interceptor];
1704
+ let active = true;
1705
+ return () => {
1706
+ if (!active) {
1707
+ return;
1708
+ }
1709
+ active = false;
1710
+ const index = this.interceptors.indexOf(interceptor);
1711
+ if (index !== -1) {
1712
+ this.interceptors = [
1713
+ ...this.interceptors.slice(0, index),
1714
+ ...this.interceptors.slice(index + 1)
1715
+ ];
1716
+ }
1717
+ };
1718
+ }
1719
+ invoke(target, thisArg, args, additional) {
1720
+ const interceptors = this.interceptors;
1721
+ if (interceptors.length === 0) {
1722
+ return Reflect.apply(target, thisArg, args);
1723
+ }
1724
+ let next = target;
1725
+ for (let index = interceptors.length - 1; index >= 0; index -= 1) {
1726
+ const interceptor = interceptors[index];
1727
+ const downstream = next;
1728
+ next = function(...nextArgs) {
1729
+ return interceptor(downstream, this, nextArgs, additional);
1730
+ };
1731
+ }
1732
+ return Reflect.apply(next, thisArg, args);
1733
+ }
1734
+ };
1677
1735
  var TracingHook = class {
1678
1736
  start;
1679
1737
  end;
1680
1738
  asyncStart;
1681
1739
  asyncEnd;
1682
1740
  error;
1741
+ invocationHook = new InvocationHook();
1683
1742
  constructor(nameOrChannels) {
1684
1743
  Object.defineProperty(this, hookBrand, {
1685
1744
  configurable: false,
@@ -1687,6 +1746,12 @@ var TracingHook = class {
1687
1746
  value: GLOBAL_INSTRUMENTATION_HOOKS_PROTOCOL_VERSION,
1688
1747
  writable: false
1689
1748
  });
1749
+ Object.defineProperty(this, invocationHookBrand, {
1750
+ configurable: false,
1751
+ enumerable: false,
1752
+ value: GLOBAL_INSTRUMENTATION_HOOKS_PROTOCOL_VERSION,
1753
+ writable: false
1754
+ });
1690
1755
  if (typeof nameOrChannels === "string") {
1691
1756
  this.start = new HookChannel(`tracing:${nameOrChannels}:start`);
1692
1757
  this.end = new HookChannel(`tracing:${nameOrChannels}:end`);
@@ -1704,6 +1769,26 @@ var TracingHook = class {
1704
1769
  get hasSubscribers() {
1705
1770
  return this.start.hasSubscribers || this.end.hasSubscribers || this.asyncStart.hasSubscribers || this.asyncEnd.hasSubscribers || this.error.hasSubscribers;
1706
1771
  }
1772
+ get hasInterceptors() {
1773
+ return this.invocationHook.hasInterceptors;
1774
+ }
1775
+ intercept(interceptor) {
1776
+ return this.invocationHook.intercept(interceptor);
1777
+ }
1778
+ invoke(target, thisArg, args, additional) {
1779
+ return this.invocationHook.invoke(target, thisArg, args, additional);
1780
+ }
1781
+ traceInvocation(operator, target, thisArg, args, additional, callbackIndex = -1) {
1782
+ return traceInvocation(
1783
+ this,
1784
+ operator,
1785
+ target,
1786
+ thisArg,
1787
+ args,
1788
+ additional,
1789
+ callbackIndex
1790
+ );
1791
+ }
1707
1792
  subscribe(handlers) {
1708
1793
  for (const eventName of traceEvents) {
1709
1794
  const handler = handlers[eventName];
@@ -1925,6 +2010,60 @@ function hasTracingHookShape(value) {
1925
2010
  return false;
1926
2011
  }
1927
2012
  }
2013
+ function hasInvocationHookShape(value) {
2014
+ if (typeof value !== "object" && typeof value !== "function" || value === null) {
2015
+ return false;
2016
+ }
2017
+ try {
2018
+ const hook = value;
2019
+ return value[invocationHookBrand] === GLOBAL_INSTRUMENTATION_HOOKS_PROTOCOL_VERSION && typeof hook.hasInterceptors === "boolean" && typeof hook.intercept === "function" && typeof hook.invoke === "function";
2020
+ } catch {
2021
+ return false;
2022
+ }
2023
+ }
2024
+ function installInvocationHook(value) {
2025
+ const hasInvocationHook = hasInvocationHookShape(value);
2026
+ const invocationHook = new InvocationHook();
2027
+ try {
2028
+ if (!hasInvocationHook) {
2029
+ Object.defineProperties(value, {
2030
+ [invocationHookBrand]: {
2031
+ configurable: false,
2032
+ enumerable: false,
2033
+ value: GLOBAL_INSTRUMENTATION_HOOKS_PROTOCOL_VERSION,
2034
+ writable: false
2035
+ },
2036
+ hasInterceptors: {
2037
+ configurable: false,
2038
+ enumerable: false,
2039
+ get: () => invocationHook.hasInterceptors
2040
+ },
2041
+ intercept: {
2042
+ configurable: false,
2043
+ enumerable: false,
2044
+ value: invocationHook.intercept.bind(invocationHook),
2045
+ writable: false
2046
+ },
2047
+ invoke: {
2048
+ configurable: false,
2049
+ enumerable: false,
2050
+ value: invocationHook.invoke.bind(invocationHook),
2051
+ writable: false
2052
+ }
2053
+ });
2054
+ }
2055
+ if (typeof value.traceInvocation !== "function") {
2056
+ Object.defineProperty(value, "traceInvocation", {
2057
+ configurable: false,
2058
+ enumerable: false,
2059
+ value: traceInvocation.bind(void 0, value),
2060
+ writable: false
2061
+ });
2062
+ }
2063
+ } catch (error2) {
2064
+ reportError(error2);
2065
+ }
2066
+ }
1928
2067
  function isCompatibleTracingHook(value) {
1929
2068
  try {
1930
2069
  return value[hookBrand] === GLOBAL_INSTRUMENTATION_HOOKS_PROTOCOL_VERSION && hasTracingHookShape(value);
@@ -2011,6 +2150,7 @@ function newGlobalTracingChannel(nameOrChannels) {
2011
2150
  return inertTracingHook;
2012
2151
  }
2013
2152
  if (isCompatibleTracingHook(existing)) {
2153
+ installInvocationHook(existing);
2014
2154
  return existing;
2015
2155
  }
2016
2156
  if (existing !== void 0) {
@@ -6866,6 +7006,7 @@ var INSTRUMENTATION_NAMES = {
6866
7006
  CLAUDE_AGENT_SDK: "claude-agent-sdk",
6867
7007
  CLOUDFLARE_AI_CHAT: "cloudflare-ai-chat",
6868
7008
  CLOUDFLARE_AGENTS: "cloudflare-agents",
7009
+ CLOUDFLARE_THINK: "cloudflare-think",
6869
7010
  COHERE: "cohere",
6870
7011
  CURSOR_SDK: "cursor-sdk",
6871
7012
  EVE: "eve",
@@ -6880,6 +7021,7 @@ var INSTRUMENTATION_NAMES = {
6880
7021
  LANGSMITH: "langsmith",
6881
7022
  MASTRA: "mastra",
6882
7023
  MISTRAL: "mistral",
7024
+ OLLAMA: "ollama",
6883
7025
  OPENAI: "openai",
6884
7026
  OPENAI_AGENTS: "openai-agents",
6885
7027
  OPENAI_CODEX: "openai-codex",
@@ -6891,7 +7033,7 @@ var INSTRUMENTATION_NAMES = {
6891
7033
  var INTERNAL_SPAN_INSTRUMENTATION_NAME = /* @__PURE__ */ Symbol.for(
6892
7034
  "braintrust.spanInstrumentationName"
6893
7035
  );
6894
- var SDK_VERSION = true ? "3.26.0" : "0.0.0";
7036
+ var SDK_VERSION = true ? "3.27.0" : "0.0.0";
6895
7037
  function withSpanInstrumentationName(args, instrumentationName) {
6896
7038
  return {
6897
7039
  ...args,
@@ -15222,6 +15364,34 @@ function patchStreamIfNeeded(stream, options) {
15222
15364
  );
15223
15365
  return stream;
15224
15366
  }
15367
+ const chunks = [];
15368
+ let completed = false;
15369
+ const notifyCancellation = async () => {
15370
+ try {
15371
+ await (options.onCancel ?? options.onComplete)(chunks);
15372
+ } catch (error2) {
15373
+ debugLogger.error("Error in stream cancellation handler:", error2);
15374
+ }
15375
+ };
15376
+ const patchAbortIfPresent = () => {
15377
+ try {
15378
+ if ("abort" in stream && typeof stream.abort === "function") {
15379
+ const originalAbort = stream.abort.bind(stream);
15380
+ stream.abort = (...args) => {
15381
+ try {
15382
+ return originalAbort(...args);
15383
+ } finally {
15384
+ if (!completed) {
15385
+ completed = true;
15386
+ void notifyCancellation();
15387
+ }
15388
+ }
15389
+ };
15390
+ }
15391
+ } catch (error2) {
15392
+ debugLogger.warn("Failed to patch stream abort method:", error2);
15393
+ }
15394
+ };
15225
15395
  if (hasAsyncIteratorMethods(stream) && isSelfAsyncIterator(stream)) {
15226
15396
  if ("__braintrust_patched_iterator_methods" in stream) {
15227
15397
  return stream;
@@ -15230,8 +15400,6 @@ function patchStreamIfNeeded(stream, options) {
15230
15400
  const originalNext = stream.next.bind(stream);
15231
15401
  const originalReturn = typeof stream.return === "function" ? stream.return.bind(stream) : null;
15232
15402
  const originalThrow = typeof stream.throw === "function" ? stream.throw.bind(stream) : null;
15233
- const chunks = [];
15234
- let completed = false;
15235
15403
  stream.next = async (...args) => {
15236
15404
  try {
15237
15405
  const result = await runNextWithWrapper(
@@ -15283,11 +15451,7 @@ function patchStreamIfNeeded(stream, options) {
15283
15451
  stream.return = async (...args) => {
15284
15452
  if (!completed) {
15285
15453
  completed = true;
15286
- try {
15287
- await options.onComplete(chunks);
15288
- } catch (error2) {
15289
- console.error("Error in stream onComplete handler:", error2);
15290
- }
15454
+ await notifyCancellation();
15291
15455
  }
15292
15456
  return originalReturn(...args);
15293
15457
  };
@@ -15312,6 +15476,7 @@ function patchStreamIfNeeded(stream, options) {
15312
15476
  Object.defineProperty(stream, "__braintrust_patched_iterator_methods", {
15313
15477
  value: true
15314
15478
  });
15479
+ patchAbortIfPresent();
15315
15480
  return stream;
15316
15481
  } catch (error2) {
15317
15482
  console.warn("Failed to patch stream iterator methods:", error2);
@@ -15325,8 +15490,6 @@ function patchStreamIfNeeded(stream, options) {
15325
15490
  const patchedIteratorFn = function() {
15326
15491
  const iterator = originalIteratorFn.call(this);
15327
15492
  const originalNext = iterator.next.bind(iterator);
15328
- const chunks = [];
15329
- let completed = false;
15330
15493
  iterator.next = async function(...args) {
15331
15494
  try {
15332
15495
  const result = await runNextWithWrapper(
@@ -15379,11 +15542,7 @@ function patchStreamIfNeeded(stream, options) {
15379
15542
  iterator.return = async function(...args) {
15380
15543
  if (!completed) {
15381
15544
  completed = true;
15382
- try {
15383
- await options.onComplete(chunks);
15384
- } catch (error2) {
15385
- console.error("Error in stream onComplete handler:", error2);
15386
- }
15545
+ await notifyCancellation();
15387
15546
  }
15388
15547
  return originalReturn(...args);
15389
15548
  };
@@ -15412,6 +15571,7 @@ function patchStreamIfNeeded(stream, options) {
15412
15571
  value: true
15413
15572
  });
15414
15573
  stream[Symbol.asyncIterator] = patchedIteratorFn;
15574
+ patchAbortIfPresent();
15415
15575
  return stream;
15416
15576
  } catch (error2) {
15417
15577
  console.warn("Failed to patch stream:", error2);
@@ -16160,6 +16320,33 @@ function traceStreamingChannel(channel2, config3) {
16160
16320
  const { span, startTime } = spanData;
16161
16321
  if (isAsyncIterable3(asyncEndEvent.result)) {
16162
16322
  let firstChunkTime;
16323
+ const handleStreamError = (error2) => {
16324
+ try {
16325
+ span.log({ error: error2 });
16326
+ } catch (loggingError) {
16327
+ debugLogger.error(
16328
+ `Error logging failure for ${channelName}:`,
16329
+ loggingError
16330
+ );
16331
+ }
16332
+ try {
16333
+ span.end();
16334
+ } catch (endingError) {
16335
+ debugLogger.error(
16336
+ `Error ending span for ${channelName}:`,
16337
+ endingError
16338
+ );
16339
+ }
16340
+ states.delete(event);
16341
+ runStreamingErrorHook({
16342
+ channelName,
16343
+ config: config3,
16344
+ error: error2,
16345
+ event: asyncEndEvent,
16346
+ span,
16347
+ startTime
16348
+ });
16349
+ };
16163
16350
  patchStreamIfNeeded(asyncEndEvent.result, {
16164
16351
  onChunk: () => {
16165
16352
  if (firstChunkTime === void 0) {
@@ -16239,33 +16426,12 @@ function traceStreamingChannel(channel2, config3) {
16239
16426
  });
16240
16427
  }
16241
16428
  },
16242
- onError: (error2) => {
16243
- try {
16244
- span.log({ error: error2 });
16245
- } catch (loggingError) {
16246
- debugLogger.error(
16247
- `Error logging failure for ${channelName}:`,
16248
- loggingError
16249
- );
16250
- }
16251
- try {
16252
- span.end();
16253
- } catch (endingError) {
16254
- debugLogger.error(
16255
- `Error ending span for ${channelName}:`,
16256
- endingError
16257
- );
16258
- }
16259
- states.delete(event);
16260
- runStreamingErrorHook({
16261
- channelName,
16262
- config: config3,
16263
- error: error2,
16264
- event: asyncEndEvent,
16265
- span,
16266
- startTime
16267
- });
16268
- }
16429
+ onCancel: () => {
16430
+ const error2 = new Error("Stream cancelled before completion");
16431
+ error2.name = "AbortError";
16432
+ handleStreamError(error2);
16433
+ },
16434
+ onError: handleStreamError
16269
16435
  });
16270
16436
  return;
16271
16437
  }
@@ -16653,11 +16819,21 @@ function defineChannels(pkg, channels, options) {
16653
16819
  const tracingChannel2 = () => isomorph_default.newTracingChannel(
16654
16820
  fullChannelName
16655
16821
  );
16822
+ const intercept2 = (interceptor) => {
16823
+ const hook = tracingChannel2();
16824
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
16825
+ };
16826
+ };
16656
16827
  return [
16657
16828
  key,
16658
16829
  {
16659
16830
  ...asyncSpec,
16660
16831
  instrumentationName,
16832
+ intercept: intercept2,
16833
+ invoke: (target, thisArg, args, additional) => {
16834
+ const hook = tracingChannel2();
16835
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
16836
+ },
16661
16837
  tracingChannel: tracingChannel2,
16662
16838
  tracePromise: (fn, context2) => tracingChannel2().tracePromise(
16663
16839
  fn,
@@ -16671,11 +16847,21 @@ function defineChannels(pkg, channels, options) {
16671
16847
  const tracingChannel = () => isomorph_default.newTracingChannel(
16672
16848
  fullChannelName
16673
16849
  );
16850
+ const intercept = (interceptor) => {
16851
+ const hook = tracingChannel();
16852
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
16853
+ };
16854
+ };
16674
16855
  return [
16675
16856
  key,
16676
16857
  {
16677
16858
  ...syncSpec,
16678
16859
  instrumentationName,
16860
+ intercept,
16861
+ invoke: (target, thisArg, args, additional) => {
16862
+ const hook = tracingChannel();
16863
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
16864
+ },
16679
16865
  tracingChannel,
16680
16866
  traceSync: (fn, context2) => tracingChannel().traceSync(
16681
16867
  fn,
@@ -17926,6 +18112,12 @@ function extractAnthropicCacheTokens(cacheReadTokens = 0, cacheCreationTokens =
17926
18112
  return cacheTokens;
17927
18113
  }
17928
18114
 
18115
+ // src/wrappers/anthropic-session-collector.ts
18116
+ var sessionStreamCollectors = /* @__PURE__ */ new WeakMap();
18117
+ function registerAnthropicSessionStreamCollector(stream, collector) {
18118
+ sessionStreamCollectors.set(stream, collector);
18119
+ }
18120
+
17929
18121
  // src/instrumentation/plugins/anthropic-channels.ts
17930
18122
  var anthropicChannels = defineChannels(
17931
18123
  "@anthropic-ai/sdk",
@@ -17941,6 +18133,14 @@ var anthropicChannels = defineChannels(
17941
18133
  betaMessagesToolRunner: channel({
17942
18134
  channelName: "beta.messages.toolRunner",
17943
18135
  kind: "sync-stream"
18136
+ }),
18137
+ betaSessionsEventsStream: channel({
18138
+ channelName: "beta.sessions.events.stream",
18139
+ kind: "async"
18140
+ }),
18141
+ betaSessionsThreadsEventsStream: channel({
18142
+ channelName: "beta.sessions.threads.events.stream",
18143
+ kind: "async"
17944
18144
  })
17945
18145
  },
17946
18146
  { instrumentationName: INSTRUMENTATION_NAMES.ANTHROPIC }
@@ -17954,6 +18154,7 @@ var AnthropicPlugin = class extends BasePlugin {
17954
18154
  onEnable() {
17955
18155
  this.subscribeToAnthropicChannels();
17956
18156
  this.subscribeToAnthropicToolRunner();
18157
+ this.subscribeToAnthropicSessionStreams();
17957
18158
  }
17958
18159
  onDisable() {
17959
18160
  this.unsubscribers = unsubscribeAll(this.unsubscribers);
@@ -18071,7 +18272,411 @@ var AnthropicPlugin = class extends BasePlugin {
18071
18272
  tracingChannel.unsubscribe(handlers);
18072
18273
  });
18073
18274
  }
18275
+ subscribeToAnthropicSessionStreams() {
18276
+ this.subscribeToAnthropicSessionStream(
18277
+ anthropicChannels.betaSessionsEventsStream,
18278
+ false
18279
+ );
18280
+ this.subscribeToAnthropicSessionStream(
18281
+ anthropicChannels.betaSessionsThreadsEventsStream,
18282
+ true
18283
+ );
18284
+ }
18285
+ subscribeToAnthropicSessionStream(channel2, isThread) {
18286
+ const tracingChannel = channel2.tracingChannel();
18287
+ const pending = /* @__PURE__ */ new WeakSet();
18288
+ const handlers = {
18289
+ start: (event) => {
18290
+ if (isAutoInstrumentationSuppressed()) {
18291
+ return;
18292
+ }
18293
+ pending.add(event);
18294
+ },
18295
+ asyncEnd: (event) => {
18296
+ if (!pending.delete(event)) {
18297
+ return;
18298
+ }
18299
+ const stream = event.result;
18300
+ if (!isAsyncIterable3(stream)) {
18301
+ return;
18302
+ }
18303
+ registerAnthropicSessionStreamCollector(stream, () => {
18304
+ wrapAnthropicSessionEventStream(stream, isThread);
18305
+ });
18306
+ },
18307
+ error: (event) => {
18308
+ pending.delete(event);
18309
+ }
18310
+ };
18311
+ tracingChannel.subscribe(handlers);
18312
+ this.unsubscribers.push(() => tracingChannel.unsubscribe(handlers));
18313
+ }
18074
18314
  };
18315
+ function wrapAnthropicSessionEventStream(stream, isThread) {
18316
+ const state = {
18317
+ history: [],
18318
+ isThread,
18319
+ pendingInput: [],
18320
+ seenEventIds: /* @__PURE__ */ new Set()
18321
+ };
18322
+ patchStreamIfNeeded(stream, {
18323
+ onChunk: (event) => handleAnthropicSessionEvent(state, event),
18324
+ onComplete: () => finalizeAnthropicSessionTurn(state),
18325
+ onError: (error2) => finalizeAnthropicSessionTurn(state, error2)
18326
+ });
18327
+ return stream;
18328
+ }
18329
+ function handleAnthropicSessionEvent(state, event) {
18330
+ try {
18331
+ if (typeof event.id === "string" && event.type !== "event_delta" && state.seenEventIds.has(event.id)) {
18332
+ return;
18333
+ }
18334
+ if (typeof event.id === "string" && event.type !== "event_delta") {
18335
+ state.seenEventIds.add(event.id);
18336
+ }
18337
+ switch (event.type) {
18338
+ case "user.message":
18339
+ case "system.message":
18340
+ recordAnthropicSessionInput(
18341
+ state,
18342
+ event
18343
+ );
18344
+ break;
18345
+ case "session.status_running":
18346
+ if (!state.isThread) {
18347
+ ensureAnthropicSessionTurn(state);
18348
+ }
18349
+ break;
18350
+ case "session.thread_status_running":
18351
+ if (state.isThread) {
18352
+ ensureAnthropicSessionTurn(state);
18353
+ }
18354
+ break;
18355
+ case "span.model_request_start":
18356
+ if (typeof event.id === "string") {
18357
+ startAnthropicSessionModel(state, event.id);
18358
+ }
18359
+ break;
18360
+ case "event_delta":
18361
+ recordAnthropicSessionFirstToken(state, event);
18362
+ break;
18363
+ case "agent.message":
18364
+ recordAnthropicSessionMessage(
18365
+ state,
18366
+ event
18367
+ );
18368
+ break;
18369
+ case "agent.custom_tool_use":
18370
+ case "agent.mcp_tool_use":
18371
+ case "agent.tool_use":
18372
+ startAnthropicSessionTool(state, event);
18373
+ break;
18374
+ case "user.tool_confirmation":
18375
+ recordAnthropicSessionToolConfirmation(state, event);
18376
+ break;
18377
+ case "agent.mcp_tool_result":
18378
+ case "agent.tool_result":
18379
+ case "user.custom_tool_result":
18380
+ case "user.tool_result":
18381
+ finalizeAnthropicSessionTool(
18382
+ state,
18383
+ event
18384
+ );
18385
+ break;
18386
+ case "span.model_request_end":
18387
+ finalizeAnthropicSessionModel(
18388
+ state,
18389
+ event
18390
+ );
18391
+ break;
18392
+ case "session.status_idle":
18393
+ case "session.thread_status_idle": {
18394
+ if (event.type === "session.status_idle" && state.isThread || event.type === "session.thread_status_idle" && !state.isThread) {
18395
+ break;
18396
+ }
18397
+ const stopReason = isObject(event.stop_reason) ? event.stop_reason.type : void 0;
18398
+ if (stopReason === "end_turn") {
18399
+ finalizeAnthropicSessionTurn(state);
18400
+ } else if (stopReason === "retries_exhausted") {
18401
+ finalizeAnthropicSessionTurn(
18402
+ state,
18403
+ "Anthropic session retries exhausted"
18404
+ );
18405
+ }
18406
+ break;
18407
+ }
18408
+ case "session.error":
18409
+ finalizeAnthropicSessionTurn(state, event.error);
18410
+ break;
18411
+ case "session.deleted":
18412
+ case "session.status_terminated":
18413
+ case "session.thread_status_terminated":
18414
+ if (event.type === "session.deleted" || event.type === "session.status_terminated" && !state.isThread || event.type === "session.thread_status_terminated" && state.isThread) {
18415
+ finalizeAnthropicSessionTurn(state);
18416
+ }
18417
+ break;
18418
+ default:
18419
+ break;
18420
+ }
18421
+ } catch (error2) {
18422
+ debugLogger.debug("Error processing Anthropic Sessions event:", error2);
18423
+ }
18424
+ }
18425
+ function ensureAnthropicSessionTurn(state) {
18426
+ if (state.activeTurn) {
18427
+ return state.activeTurn;
18428
+ }
18429
+ const input = state.pendingInput;
18430
+ state.pendingInput = [];
18431
+ const span = startSpan(
18432
+ withSpanInstrumentationName(
18433
+ {
18434
+ event: {
18435
+ ...input.length > 0 ? { input } : {},
18436
+ metadata: { provider: "anthropic" }
18437
+ },
18438
+ name: state.isThread ? "anthropic.beta.sessions.thread.turn" : "anthropic.beta.sessions.turn",
18439
+ spanAttributes: { type: "task" /* TASK */ }
18440
+ },
18441
+ INSTRUMENTATION_NAMES.ANTHROPIC
18442
+ )
18443
+ );
18444
+ state.activeTurn = {
18445
+ activeTools: /* @__PURE__ */ new Map(),
18446
+ input: input.slice(),
18447
+ metrics: {},
18448
+ span
18449
+ };
18450
+ return state.activeTurn;
18451
+ }
18452
+ function recordAnthropicSessionInput(state, event) {
18453
+ const content = processAttachmentsInInput(event.content);
18454
+ const message = {
18455
+ role: event.type === "system.message" ? "system" : "user",
18456
+ content
18457
+ };
18458
+ state.history.push(message);
18459
+ if (state.activeTurn) {
18460
+ state.activeTurn.input.push(message);
18461
+ } else {
18462
+ state.pendingInput.push(message);
18463
+ }
18464
+ }
18465
+ function startAnthropicSessionModel(state, startEventId) {
18466
+ const turn = ensureAnthropicSessionTurn(state);
18467
+ if (turn.activeModel) {
18468
+ finalizeAnthropicSessionModelState(state, turn);
18469
+ }
18470
+ const span = withCurrent(
18471
+ turn.span,
18472
+ () => startSpan(
18473
+ withSpanInstrumentationName(
18474
+ {
18475
+ event: {
18476
+ ...state.history.length > 0 ? { input: state.history.slice() } : {},
18477
+ metadata: { provider: "anthropic" }
18478
+ },
18479
+ name: "anthropic.messages.create",
18480
+ spanAttributes: { type: "llm" /* LLM */ }
18481
+ },
18482
+ INSTRUMENTATION_NAMES.ANTHROPIC
18483
+ )
18484
+ )
18485
+ );
18486
+ turn.activeModel = {
18487
+ output: [],
18488
+ span,
18489
+ startEventId,
18490
+ startTime: getCurrentUnixTimestamp()
18491
+ };
18492
+ }
18493
+ function recordAnthropicSessionFirstToken(state, event) {
18494
+ const model = state.activeTurn?.activeModel;
18495
+ if (!model || model.firstTokenTime !== void 0 || !isObject(event.delta) || !isObject(event.delta.content) || typeof event.delta.content.text !== "string") {
18496
+ return;
18497
+ }
18498
+ model.firstTokenTime = getCurrentUnixTimestamp();
18499
+ }
18500
+ function recordAnthropicSessionMessage(state, event) {
18501
+ const turn = ensureAnthropicSessionTurn(state);
18502
+ const content = processAttachmentsInInput(
18503
+ event.content
18504
+ );
18505
+ if (turn.activeModel) {
18506
+ turn.activeModel.output.push(...content);
18507
+ } else {
18508
+ const output = { role: "assistant", content };
18509
+ turn.lastOutput = output;
18510
+ state.history.push(output);
18511
+ }
18512
+ }
18513
+ function startAnthropicSessionTool(state, event) {
18514
+ const turn = ensureAnthropicSessionTurn(state);
18515
+ if (turn.activeModel) {
18516
+ turn.activeModel.output.push({
18517
+ type: "tool_use",
18518
+ id: event.id,
18519
+ name: event.name,
18520
+ input: event.input
18521
+ });
18522
+ }
18523
+ const existing = turn.activeTools.get(event.id);
18524
+ if (existing) {
18525
+ existing.span.end();
18526
+ turn.activeTools.delete(event.id);
18527
+ }
18528
+ const span = withCurrent(
18529
+ turn.span,
18530
+ () => startSpan(
18531
+ withSpanInstrumentationName(
18532
+ {
18533
+ event: { input: event.input },
18534
+ name: event.name,
18535
+ spanAttributes: { type: "tool" /* TOOL */ }
18536
+ },
18537
+ INSTRUMENTATION_NAMES.ANTHROPIC
18538
+ )
18539
+ )
18540
+ );
18541
+ const toolState = {
18542
+ approval: event.evaluated_permission === "allow" ? "approved" : event.evaluated_permission === "deny" ? "denied" : void 0,
18543
+ span
18544
+ };
18545
+ turn.activeTools.set(event.id, toolState);
18546
+ if (toolState.approval === "denied") {
18547
+ finalizeAnthropicSessionToolState(toolState);
18548
+ turn.activeTools.delete(event.id);
18549
+ }
18550
+ }
18551
+ function recordAnthropicSessionToolConfirmation(state, event) {
18552
+ if (typeof event.tool_use_id !== "string" || event.result !== "allow" && event.result !== "deny") {
18553
+ return;
18554
+ }
18555
+ const turn = state.activeTurn;
18556
+ const tool = turn?.activeTools.get(event.tool_use_id);
18557
+ if (!turn || !tool) {
18558
+ return;
18559
+ }
18560
+ tool.approval = event.result === "allow" ? "approved" : "denied";
18561
+ if (tool.approval === "denied") {
18562
+ finalizeAnthropicSessionToolState(tool);
18563
+ turn.activeTools.delete(event.tool_use_id);
18564
+ }
18565
+ }
18566
+ function finalizeAnthropicSessionTool(state, event) {
18567
+ const toolUseId = event.tool_use_id ?? event.mcp_tool_use_id ?? event.custom_tool_use_id;
18568
+ if (!toolUseId) {
18569
+ return;
18570
+ }
18571
+ const content = processAttachmentsInInput(event.content);
18572
+ const turn = state.activeTurn;
18573
+ const tool = turn?.activeTools.get(toolUseId);
18574
+ if (turn && tool) {
18575
+ finalizeAnthropicSessionToolState(tool, event, content);
18576
+ turn.activeTools.delete(toolUseId);
18577
+ }
18578
+ const toolResult = {
18579
+ type: "tool_result",
18580
+ tool_use_id: toolUseId,
18581
+ ...content !== void 0 ? { content } : {},
18582
+ ...event.is_error === true ? { is_error: true } : {}
18583
+ };
18584
+ state.history.push({
18585
+ role: "user",
18586
+ content: [toolResult]
18587
+ });
18588
+ }
18589
+ function finalizeAnthropicSessionToolState(state, event, content = event?.content) {
18590
+ safeAnthropicSessionLog(state.span, {
18591
+ ...event?.is_error === true ? {
18592
+ error: content ?? "Anthropic session tool execution failed"
18593
+ } : {},
18594
+ ...state.approval ? { metadata: { tool_approval: state.approval } } : {},
18595
+ ...content !== void 0 ? { output: content } : {}
18596
+ });
18597
+ state.span.end();
18598
+ }
18599
+ function finalizeAnthropicSessionModel(state, event) {
18600
+ const turn = state.activeTurn;
18601
+ const model = turn?.activeModel;
18602
+ if (!turn || !model || model.startEventId !== event.model_request_start_id) {
18603
+ return;
18604
+ }
18605
+ finalizeAnthropicSessionModelState(state, turn, event);
18606
+ }
18607
+ function finalizeAnthropicSessionModelState(state, turn, event) {
18608
+ const model = turn.activeModel;
18609
+ if (!model) {
18610
+ return;
18611
+ }
18612
+ turn.activeModel = void 0;
18613
+ const metrics = event ? parseAnthropicSessionModelUsage(event.model_usage) : {};
18614
+ if (model.firstTokenTime !== void 0) {
18615
+ metrics.time_to_first_token = model.firstTokenTime - model.startTime;
18616
+ }
18617
+ const output = model.output.length > 0 ? { role: "assistant", content: model.output } : void 0;
18618
+ safeAnthropicSessionLog(model.span, {
18619
+ ...event?.is_error ? { error: "Anthropic session model request failed" } : {},
18620
+ ...Object.keys(metrics).length > 0 ? { metrics } : {},
18621
+ ...output ? { output } : {}
18622
+ });
18623
+ model.span.end();
18624
+ if (output) {
18625
+ turn.lastOutput = output;
18626
+ state.history.push(output);
18627
+ }
18628
+ for (const key of [
18629
+ "prompt_tokens",
18630
+ "completion_tokens",
18631
+ "tokens",
18632
+ "prompt_cached_tokens",
18633
+ "prompt_cache_creation_tokens"
18634
+ ]) {
18635
+ const value = metrics[key];
18636
+ if (typeof value === "number") {
18637
+ turn.metrics[key] = (turn.metrics[key] ?? 0) + value;
18638
+ }
18639
+ }
18640
+ }
18641
+ function parseAnthropicSessionModelUsage(usage) {
18642
+ const finalized = finalizeAnthropicTokens({
18643
+ completion_tokens: usage.output_tokens,
18644
+ prompt_cache_creation_tokens: usage.cache_creation_input_tokens,
18645
+ prompt_cached_tokens: usage.cache_read_input_tokens,
18646
+ prompt_tokens: usage.input_tokens
18647
+ });
18648
+ return Object.fromEntries(
18649
+ Object.entries(finalized).filter(
18650
+ (entry) => entry[1] !== void 0
18651
+ )
18652
+ );
18653
+ }
18654
+ function finalizeAnthropicSessionTurn(state, error2) {
18655
+ const turn = state.activeTurn;
18656
+ if (!turn) {
18657
+ return;
18658
+ }
18659
+ state.activeTurn = void 0;
18660
+ finalizeAnthropicSessionModelState(state, turn);
18661
+ for (const tool of turn.activeTools.values()) {
18662
+ finalizeAnthropicSessionToolState(tool);
18663
+ }
18664
+ turn.activeTools.clear();
18665
+ safeAnthropicSessionLog(turn.span, {
18666
+ ...error2 !== void 0 ? { error: error2 instanceof Error ? error2 : toLoggedError(error2) } : {},
18667
+ ...turn.input.length > 0 ? { input: turn.input } : {},
18668
+ ...turn.lastOutput ? { output: turn.lastOutput } : {},
18669
+ ...Object.keys(turn.metrics).length > 0 ? { metrics: turn.metrics } : {}
18670
+ });
18671
+ turn.span.end();
18672
+ }
18673
+ function safeAnthropicSessionLog(span, event) {
18674
+ try {
18675
+ span.log(event);
18676
+ } catch (error2) {
18677
+ debugLogger.debug("Error logging Anthropic Sessions span:", error2);
18678
+ }
18679
+ }
18075
18680
  function parseMetricsFromUsage2(usage) {
18076
18681
  if (!usage) {
18077
18682
  return {};
@@ -19945,6 +20550,29 @@ var harnessAgentChannels = defineChannels(
19945
20550
  { instrumentationName: INSTRUMENTATION_NAMES.AI_SDK }
19946
20551
  );
19947
20552
 
20553
+ // src/instrumentation/plugins/cloudflare-think-context.ts
20554
+ var cloudflareThinkSpans = /* @__PURE__ */ new Map();
20555
+ function registerCloudflareThinkSpan(span) {
20556
+ if (span.spanId) {
20557
+ cloudflareThinkSpans.set(span.spanId, span);
20558
+ }
20559
+ }
20560
+ function unregisterCloudflareThinkSpan(span) {
20561
+ if (span.spanId) {
20562
+ cloudflareThinkSpans.delete(span.spanId);
20563
+ }
20564
+ }
20565
+ function currentCloudflareThinkSpan() {
20566
+ const parentSpanIds = _internalGetGlobalState()?.contextManager.getParentSpanIds()?.spanParents ?? [];
20567
+ for (const parentSpanId of parentSpanIds) {
20568
+ const span = cloudflareThinkSpans.get(parentSpanId);
20569
+ if (span) {
20570
+ return span;
20571
+ }
20572
+ }
20573
+ return void 0;
20574
+ }
20575
+
19948
20576
  // src/instrumentation/plugins/ai-sdk-plugin.ts
19949
20577
  var DEFAULT_DENY_OUTPUT_PATHS = [
19950
20578
  // v3
@@ -20051,6 +20679,7 @@ var AISDKPlugin = class extends BasePlugin {
20051
20679
  traceStreamingChannel(aiSDKChannels.streamText, {
20052
20680
  name: "streamText",
20053
20681
  type: "function" /* FUNCTION */,
20682
+ shouldTrace: () => currentCloudflareThinkSpan() === void 0,
20054
20683
  extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
20055
20684
  extractOutput: (result, endEvent) => processAISDKOutput(
20056
20685
  result,
@@ -20071,6 +20700,7 @@ var AISDKPlugin = class extends BasePlugin {
20071
20700
  traceSyncStreamChannel(aiSDKChannels.streamTextSync, {
20072
20701
  name: "streamText",
20073
20702
  type: "function" /* FUNCTION */,
20703
+ shouldTrace: () => currentCloudflareThinkSpan() === void 0,
20074
20704
  extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
20075
20705
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
20076
20706
  defaultDenyOutputPaths: denyOutputPaths,
@@ -21056,6 +21686,23 @@ function prepareAISDKWorkflowAgentStreamInput(params, event, span, defaultDenyOu
21056
21686
  metadata
21057
21687
  };
21058
21688
  }
21689
+ function prepareAISDKAgentCallInput(params, event, span, defaultDenyOutputPaths = DEFAULT_DENY_OUTPUT_PATHS) {
21690
+ const { input } = processAISDKWorkflowAgentCallInput(params);
21691
+ const metadata = extractMetadataFromCallParams(params, event.self);
21692
+ const childTracing = prepareAISDKChildTracing(
21693
+ params,
21694
+ event.self,
21695
+ span,
21696
+ defaultDenyOutputPaths,
21697
+ event.aiSDK,
21698
+ { agentOwner: true }
21699
+ );
21700
+ event.modelWrapped = childTracing.modelWrapped;
21701
+ if (childTracing.cleanup) {
21702
+ event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
21703
+ }
21704
+ return { input, metadata };
21705
+ }
21059
21706
  function prepareAISDKEmbedInput(params, self) {
21060
21707
  return {
21061
21708
  input: { ...params },
@@ -21079,6 +21726,18 @@ function extractTopLevelAISDKMetrics(result, event, startTime) {
21079
21726
  }
21080
21727
  return metrics;
21081
21728
  }
21729
+ async function extractResolvedAISDKTokenMetrics(result) {
21730
+ for (const field of ["totalUsage", "usage"]) {
21731
+ try {
21732
+ const usage = await Promise.resolve(result[field]);
21733
+ if (usage !== void 0) {
21734
+ return extractTokenMetrics({ [field]: usage });
21735
+ }
21736
+ } catch {
21737
+ }
21738
+ }
21739
+ return extractTokenMetrics(result);
21740
+ }
21082
21741
  function hasModelChildTracing(event) {
21083
21742
  return event?.modelWrapped === true || event?.__braintrust_ai_sdk_model_wrapped === true;
21084
21743
  }
@@ -21896,13 +22555,15 @@ function patchAISDKStreamingResult(args) {
21896
22555
  const {
21897
22556
  defaultDenyOutputPaths,
21898
22557
  endEvent,
22558
+ forceTopLevelMetrics,
21899
22559
  onComplete,
21900
22560
  onCancel,
21901
22561
  onError,
21902
22562
  result,
21903
22563
  resolvePromiseUsage,
21904
22564
  span,
21905
- startTime
22565
+ startTime,
22566
+ transformOutput
21906
22567
  } = args;
21907
22568
  if (!result || typeof result !== "object") {
21908
22569
  return false;
@@ -21955,8 +22616,8 @@ function patchAISDKStreamingResult(args) {
21955
22616
  }
21956
22617
  outputLogged = true;
21957
22618
  try {
21958
- const metrics = extractTopLevelAISDKMetrics(result, endEvent);
21959
- if (resolvePromiseUsage) {
22619
+ const metrics = forceTopLevelMetrics ? await extractResolvedAISDKTokenMetrics(result) : extractTopLevelAISDKMetrics(result, endEvent);
22620
+ if (resolvePromiseUsage && !forceTopLevelMetrics) {
21960
22621
  try {
21961
22622
  const resultRecord2 = result;
21962
22623
  const pendingUsage = resultRecord2.totalUsage ?? resultRecord2.usage;
@@ -21975,10 +22636,11 @@ function patchAISDKStreamingResult(args) {
21975
22636
  if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
21976
22637
  metrics.time_to_first_token = firstChunkTime - startTime;
21977
22638
  }
21978
- const output = await processAISDKStreamingOutput(
22639
+ const processedOutput = await processAISDKStreamingOutput(
21979
22640
  result,
21980
22641
  resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
21981
22642
  );
22643
+ const output = transformOutput ? transformOutput(processedOutput) : processedOutput;
21982
22644
  const metadata = buildResolvedMetadataPayload(result).metadata;
21983
22645
  try {
21984
22646
  span.log({
@@ -24400,6 +25062,295 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
24400
25062
  }
24401
25063
  };
24402
25064
 
25065
+ // src/instrumentation/plugins/cloudflare-think-channels.ts
25066
+ var cloudflareThinkChannels = defineChannels(
25067
+ "@cloudflare/think",
25068
+ {
25069
+ runInferenceLoop: channel({
25070
+ channelName: "Think.runInferenceLoop",
25071
+ kind: "async"
25072
+ })
25073
+ },
25074
+ { instrumentationName: INSTRUMENTATION_NAMES.CLOUDFLARE_THINK }
25075
+ );
25076
+
25077
+ // src/instrumentation/plugins/cloudflare-think-plugin.ts
25078
+ var THINK_STATE_ID = /* @__PURE__ */ Symbol.for("braintrust.cloudflare-think.state-id");
25079
+ var CloudflareThinkPlugin = class extends BasePlugin {
25080
+ statesBySpanId = /* @__PURE__ */ new Map();
25081
+ onEnable() {
25082
+ this.subscribeToThinkRuns();
25083
+ this.subscribeToAISDKStreamTextSync();
25084
+ this.subscribeToAISDKStreamTextAsync();
25085
+ }
25086
+ onDisable() {
25087
+ for (const unsubscribe of this.unsubscribers) {
25088
+ unsubscribe();
25089
+ }
25090
+ this.unsubscribers = [];
25091
+ for (const state of this.statesBySpanId.values()) {
25092
+ this.finishState(state);
25093
+ }
25094
+ this.statesBySpanId.clear();
25095
+ }
25096
+ subscribeToThinkRuns() {
25097
+ const channel2 = cloudflareThinkChannels.runInferenceLoop.tracingChannel();
25098
+ const states = /* @__PURE__ */ new WeakMap();
25099
+ const state = _internalGetGlobalState();
25100
+ const contextManager = state?.contextManager;
25101
+ const currentSpanStore = contextManager ? contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
25102
+ const ensureState2 = (event) => {
25103
+ if (isAutoInstrumentationSuppressed()) {
25104
+ return void 0;
25105
+ }
25106
+ const existing = states.get(event);
25107
+ if (existing) {
25108
+ return existing;
25109
+ }
25110
+ const span = startSpan(
25111
+ withSpanInstrumentationName(
25112
+ {
25113
+ name: "Think.runTurn",
25114
+ spanAttributes: { type: "task" /* TASK */ }
25115
+ },
25116
+ INSTRUMENTATION_NAMES.CLOUDFLARE_THINK
25117
+ )
25118
+ );
25119
+ const runState = {
25120
+ aiResultPatched: false,
25121
+ fallbackInput: extractFallbackInput(event.self?.messages),
25122
+ finalized: false,
25123
+ inputLogged: false,
25124
+ span,
25125
+ startTime: getCurrentUnixTimestamp()
25126
+ };
25127
+ const metadata = {
25128
+ braintrust: {
25129
+ integration_name: "cloudflare-think",
25130
+ sdk_language: "typescript"
25131
+ }
25132
+ };
25133
+ if (typeof event.moduleVersion === "string") {
25134
+ metadata["cloudflare_think.version"] = event.moduleVersion;
25135
+ }
25136
+ span.log({ metadata });
25137
+ states.set(event, runState);
25138
+ if (span.spanId) {
25139
+ this.statesBySpanId.set(span.spanId, runState);
25140
+ }
25141
+ registerCloudflareThinkSpan(span);
25142
+ return runState;
25143
+ };
25144
+ if (contextManager && currentSpanStore && channel2.start) {
25145
+ channel2.start.bindStore(currentSpanStore, (event) => {
25146
+ const runState = ensureState2(event);
25147
+ return runState ? contextManager.wrapSpanForStore(runState.span) : currentSpanStore.getStore();
25148
+ });
25149
+ this.unsubscribers.push(
25150
+ () => channel2.start?.unbindStore(currentSpanStore)
25151
+ );
25152
+ }
25153
+ const handlers = {
25154
+ start: (event) => {
25155
+ ensureState2(event);
25156
+ },
25157
+ asyncEnd: (event) => {
25158
+ const runState = states.get(event);
25159
+ states.delete(event);
25160
+ if (!runState || runState.finalized || runState.aiResultPatched) {
25161
+ return;
25162
+ }
25163
+ this.finishState(runState, void 0, event.result);
25164
+ },
25165
+ error: (event) => {
25166
+ const runState = states.get(event);
25167
+ states.delete(event);
25168
+ if (runState) {
25169
+ this.finishState(runState, event.error);
25170
+ }
25171
+ }
25172
+ };
25173
+ channel2.subscribe(handlers);
25174
+ this.unsubscribers.push(() => channel2.unsubscribe(handlers));
25175
+ }
25176
+ subscribeToAISDKStreamTextSync() {
25177
+ const channel2 = aiSDKChannels.streamTextSync.tracingChannel();
25178
+ const handlers = {
25179
+ start: (event) => {
25180
+ this.startAISDKStream(event);
25181
+ },
25182
+ end: (event) => {
25183
+ this.endAISDKStream(event);
25184
+ },
25185
+ error: (event) => {
25186
+ this.errorAISDKStream(event);
25187
+ }
25188
+ };
25189
+ channel2.subscribe(handlers);
25190
+ this.unsubscribers.push(() => channel2.unsubscribe(handlers));
25191
+ }
25192
+ subscribeToAISDKStreamTextAsync() {
25193
+ const channel2 = aiSDKChannels.streamText.tracingChannel();
25194
+ const handlers = {
25195
+ start: (event) => {
25196
+ this.startAISDKStream(event);
25197
+ },
25198
+ asyncEnd: (event) => {
25199
+ this.endAISDKStream(event);
25200
+ },
25201
+ error: (event) => {
25202
+ this.errorAISDKStream(event);
25203
+ }
25204
+ };
25205
+ channel2.subscribe(handlers);
25206
+ this.unsubscribers.push(() => channel2.unsubscribe(handlers));
25207
+ }
25208
+ startAISDKStream(event) {
25209
+ const runState = this.currentState();
25210
+ if (!runState || runState.finalized) {
25211
+ return;
25212
+ }
25213
+ try {
25214
+ const prepared = prepareAISDKAgentCallInput(
25215
+ event.arguments[0],
25216
+ event,
25217
+ runState.span
25218
+ );
25219
+ runState.span.log({
25220
+ input: extractThinkTaskInput(prepared.input),
25221
+ metadata: {
25222
+ ...prepared.metadata,
25223
+ braintrust: {
25224
+ integration_name: "cloudflare-think",
25225
+ sdk_language: "typescript"
25226
+ }
25227
+ }
25228
+ });
25229
+ runState.inputLogged = true;
25230
+ runState.aiEvent = event;
25231
+ Reflect.set(event, THINK_STATE_ID, runState.span.spanId);
25232
+ } catch (error2) {
25233
+ debugLogger.error(
25234
+ "Error preparing @cloudflare/think AI SDK tracing:",
25235
+ error2
25236
+ );
25237
+ }
25238
+ }
25239
+ endAISDKStream(event) {
25240
+ const runState = this.stateForAISDKEvent(event);
25241
+ if (!runState || runState.finalized) {
25242
+ return;
25243
+ }
25244
+ const patched = patchAISDKStreamingResult({
25245
+ defaultDenyOutputPaths: DEFAULT_DENY_OUTPUT_PATHS,
25246
+ endEvent: event,
25247
+ forceTopLevelMetrics: true,
25248
+ onComplete: () => this.releaseState(runState),
25249
+ result: event.result,
25250
+ span: runState.span,
25251
+ startTime: runState.startTime,
25252
+ transformOutput: extractThinkTaskOutput
25253
+ });
25254
+ runState.aiResultPatched = patched;
25255
+ if (!patched) {
25256
+ this.finishState(runState, void 0, event.result);
25257
+ }
25258
+ }
25259
+ errorAISDKStream(event) {
25260
+ const runState = this.stateForAISDKEvent(event);
25261
+ if (runState) {
25262
+ this.finishState(runState, event.error);
25263
+ }
25264
+ }
25265
+ currentState() {
25266
+ const parentIds = _internalGetGlobalState()?.contextManager.getParentSpanIds()?.spanParents ?? [];
25267
+ for (const parentId of parentIds) {
25268
+ const state = this.statesBySpanId.get(parentId);
25269
+ if (state) {
25270
+ return state;
25271
+ }
25272
+ }
25273
+ return void 0;
25274
+ }
25275
+ stateForAISDKEvent(event) {
25276
+ const spanId = Reflect.get(event, THINK_STATE_ID);
25277
+ return typeof spanId === "string" ? this.statesBySpanId.get(spanId) : void 0;
25278
+ }
25279
+ finishState(state, error2, output) {
25280
+ if (state.finalized) {
25281
+ return;
25282
+ }
25283
+ state.finalized = true;
25284
+ try {
25285
+ if (!state.inputLogged && state.fallbackInput !== void 0) {
25286
+ state.span.log({ input: state.fallbackInput });
25287
+ }
25288
+ if (error2 !== void 0) {
25289
+ state.span.log({ error: error2 });
25290
+ } else if (output !== void 0) {
25291
+ state.span.log({ output: extractThinkTaskOutput(output) });
25292
+ }
25293
+ finalizeAISDKChildTracing(state.aiEvent);
25294
+ state.span.end();
25295
+ } finally {
25296
+ this.releaseState(state);
25297
+ }
25298
+ }
25299
+ releaseState(state) {
25300
+ state.finalized = true;
25301
+ unregisterCloudflareThinkSpan(state.span);
25302
+ if (state.span.spanId) {
25303
+ this.statesBySpanId.delete(state.span.spanId);
25304
+ }
25305
+ }
25306
+ };
25307
+ function extractFallbackInput(messages) {
25308
+ if (!Array.isArray(messages) || messages.length === 0) {
25309
+ return void 0;
25310
+ }
25311
+ let latestUserIndex = -1;
25312
+ for (let index = messages.length - 1; index >= 0; index--) {
25313
+ if (messages[index]?.role === "user") {
25314
+ latestUserIndex = index;
25315
+ break;
25316
+ }
25317
+ }
25318
+ const selected = latestUserIndex >= 0 ? messages.slice(latestUserIndex) : [];
25319
+ return selected.map((message) => ({
25320
+ role: message.role,
25321
+ content: message.content ?? message.parts
25322
+ }));
25323
+ }
25324
+ function extractThinkTaskOutput(output) {
25325
+ if (!isObject(output)) {
25326
+ return output;
25327
+ }
25328
+ if (typeof output.text === "string") {
25329
+ return { role: "assistant", content: output.text };
25330
+ }
25331
+ if (output.object !== void 0) {
25332
+ return output.object;
25333
+ }
25334
+ return output;
25335
+ }
25336
+ function extractThinkTaskInput(input) {
25337
+ if (!isObject(input)) {
25338
+ return input;
25339
+ }
25340
+ const system = input.instructions ?? input.system;
25341
+ if (Array.isArray(input.messages)) {
25342
+ return system === void 0 ? input.messages : [{ role: "system", content: system }, ...input.messages];
25343
+ }
25344
+ if (Array.isArray(input.prompt)) {
25345
+ return system === void 0 ? input.prompt : [{ role: "system", content: system }, ...input.prompt];
25346
+ }
25347
+ if (typeof input.prompt === "string") {
25348
+ const messages = [{ role: "user", content: input.prompt }];
25349
+ return system === void 0 ? messages : [{ role: "system", content: system }, ...messages];
25350
+ }
25351
+ return input;
25352
+ }
25353
+
24403
25354
  // src/instrumentation/plugins/cursor-sdk-channels.ts
24404
25355
  var cursorSDKChannels = defineChannels(
24405
25356
  "@cursor/sdk",
@@ -30165,6 +31116,470 @@ function aggregateMistralStreamChunks(chunks) {
30165
31116
  };
30166
31117
  }
30167
31118
 
31119
+ // src/instrumentation/plugins/ollama-channels.ts
31120
+ var ollamaChannels = defineChannels(
31121
+ "ollama",
31122
+ {
31123
+ chat: channel({
31124
+ channelName: "chat",
31125
+ kind: "async"
31126
+ }),
31127
+ generate: channel({
31128
+ channelName: "generate",
31129
+ kind: "async"
31130
+ }),
31131
+ embed: channel({
31132
+ channelName: "embed",
31133
+ kind: "async"
31134
+ })
31135
+ },
31136
+ { instrumentationName: INSTRUMENTATION_NAMES.OLLAMA }
31137
+ );
31138
+
31139
+ // src/instrumentation/plugins/ollama-plugin.ts
31140
+ var OllamaPlugin = class extends BasePlugin {
31141
+ onEnable() {
31142
+ this.unsubscribers.push(
31143
+ traceStreamingChannel(ollamaChannels.chat, {
31144
+ name: "ollama.chat",
31145
+ type: "llm" /* LLM */,
31146
+ extractInput: extractOllamaChatInput,
31147
+ extractOutput: (result, event) => extractOllamaChatOutput(
31148
+ result,
31149
+ countOllamaToolCalls(event?.arguments?.[0]?.messages)
31150
+ ),
31151
+ extractMetadata: extractOllamaResponseMetadata,
31152
+ extractMetrics: extractOllamaMetrics,
31153
+ aggregateChunks: aggregateOllamaChatChunks
31154
+ }),
31155
+ traceStreamingChannel(ollamaChannels.generate, {
31156
+ name: "ollama.generate",
31157
+ type: "llm" /* LLM */,
31158
+ extractInput: extractOllamaGenerateInput,
31159
+ extractOutput: extractOllamaGenerateOutput,
31160
+ extractMetadata: extractOllamaResponseMetadata,
31161
+ extractMetrics: extractOllamaMetrics,
31162
+ aggregateChunks: aggregateOllamaGenerateChunks
31163
+ }),
31164
+ traceAsyncChannel(ollamaChannels.embed, {
31165
+ name: "ollama.embed",
31166
+ type: "llm" /* LLM */,
31167
+ extractInput: extractOllamaEmbedInput,
31168
+ extractOutput: extractOllamaEmbedOutput,
31169
+ extractMetadata: extractOllamaResponseMetadata,
31170
+ extractMetrics: extractOllamaMetrics
31171
+ })
31172
+ );
31173
+ }
31174
+ onDisable() {
31175
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
31176
+ }
31177
+ };
31178
+ function isNonNegativeNumber(value) {
31179
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
31180
+ }
31181
+ function normalizeFinishReason(response, hasToolCalls = false) {
31182
+ if (hasToolCalls) {
31183
+ return "tool_calls";
31184
+ }
31185
+ return response?.done_reason || "stop";
31186
+ }
31187
+ function stringifyArguments(value) {
31188
+ if (typeof value === "string") {
31189
+ return value;
31190
+ }
31191
+ try {
31192
+ return JSON.stringify(value ?? {});
31193
+ } catch {
31194
+ return String(value);
31195
+ }
31196
+ }
31197
+ function syntheticToolCallId(name, index) {
31198
+ const normalizedName = name.replace(/[^a-zA-Z0-9_-]/g, "_") || "tool";
31199
+ return `ollama_call_${normalizedName}_${index}`;
31200
+ }
31201
+ function normalizeToolCall(toolCall, index) {
31202
+ const name = toolCall.function?.name;
31203
+ if (typeof name !== "string" || name.length === 0) {
31204
+ return void 0;
31205
+ }
31206
+ return {
31207
+ id: typeof toolCall.id === "string" && toolCall.id.length > 0 ? toolCall.id : syntheticToolCallId(name, index),
31208
+ type: "function",
31209
+ function: {
31210
+ name,
31211
+ arguments: stringifyArguments(toolCall.function?.arguments)
31212
+ }
31213
+ };
31214
+ }
31215
+ function normalizeToolCalls(toolCalls, syntheticIdOffset = 0) {
31216
+ if (!Array.isArray(toolCalls)) {
31217
+ return [];
31218
+ }
31219
+ return toolCalls.flatMap((toolCall, index) => {
31220
+ const normalized = normalizeToolCall(toolCall, syntheticIdOffset + index);
31221
+ return normalized ? [normalized] : [];
31222
+ });
31223
+ }
31224
+ function countOllamaToolCalls(messages) {
31225
+ if (!Array.isArray(messages)) {
31226
+ return 0;
31227
+ }
31228
+ return messages.reduce(
31229
+ (count, message) => count + (isObject(message) && Array.isArray(message.tool_calls) ? message.tool_calls.length : 0),
31230
+ 0
31231
+ );
31232
+ }
31233
+ function imageBytes(value) {
31234
+ if (value instanceof Uint8Array) {
31235
+ return value;
31236
+ }
31237
+ if (value instanceof ArrayBuffer) {
31238
+ return new Uint8Array(value);
31239
+ }
31240
+ if (typeof value !== "string" || value.startsWith("data:")) {
31241
+ return void 0;
31242
+ }
31243
+ try {
31244
+ const decoded = atob(value.slice(0, 24));
31245
+ return Uint8Array.from(decoded, (character) => character.charCodeAt(0));
31246
+ } catch {
31247
+ return void 0;
31248
+ }
31249
+ }
31250
+ function inferImageMediaType(value) {
31251
+ if (typeof value === "string") {
31252
+ const dataUrlType = value.match(/^data:(image\/[^;]+);base64,/i)?.[1];
31253
+ if (dataUrlType) {
31254
+ return dataUrlType;
31255
+ }
31256
+ }
31257
+ const bytes = imageBytes(value);
31258
+ if (!bytes) {
31259
+ return void 0;
31260
+ }
31261
+ if (bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71) {
31262
+ return "image/png";
31263
+ }
31264
+ if (bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) {
31265
+ return "image/jpeg";
31266
+ }
31267
+ const signature = String.fromCharCode(...bytes.slice(0, 12));
31268
+ if (signature.startsWith("GIF87a") || signature.startsWith("GIF89a")) {
31269
+ return "image/gif";
31270
+ }
31271
+ if (signature.startsWith("RIFF") && signature.slice(8, 12) === "WEBP") {
31272
+ return "image/webp";
31273
+ }
31274
+ return void 0;
31275
+ }
31276
+ function normalizeTextAndImages(content, images) {
31277
+ if (!images || images.length === 0) {
31278
+ return { content };
31279
+ }
31280
+ const imageParts = [];
31281
+ const unrecognizedImages = [];
31282
+ for (const image of images) {
31283
+ let localImagePath;
31284
+ let localPathMediaType;
31285
+ if (typeof image === "string" && !/^data:/i.test(image) && !/^https?:\/\//i.test(image)) {
31286
+ const extension = image.match(/\.([a-z0-9]+)$/i)?.[1]?.toLowerCase();
31287
+ const extensionMediaType = {
31288
+ png: "image/png",
31289
+ jpg: "image/jpeg",
31290
+ jpeg: "image/jpeg",
31291
+ gif: "image/gif",
31292
+ webp: "image/webp"
31293
+ }[extension ?? ""];
31294
+ if (extensionMediaType && isomorph_default.statSync) {
31295
+ try {
31296
+ if (isomorph_default.statSync(image).isFile()) {
31297
+ localImagePath = image;
31298
+ localPathMediaType = extensionMediaType;
31299
+ }
31300
+ } catch {
31301
+ }
31302
+ }
31303
+ }
31304
+ const mediaType = localPathMediaType ?? inferImageMediaType(image);
31305
+ if (!mediaType) {
31306
+ unrecognizedImages.push(image);
31307
+ continue;
31308
+ }
31309
+ const processedImage = localImagePath ? new Attachment({
31310
+ data: localImagePath,
31311
+ filename: localImagePath.split(/[\\/]/).at(-1) ?? "image",
31312
+ contentType: mediaType
31313
+ }) : processInputAttachments({
31314
+ type: "image",
31315
+ image,
31316
+ mediaType
31317
+ }).image;
31318
+ imageParts.push({
31319
+ type: "image_url",
31320
+ image_url: { url: processedImage }
31321
+ });
31322
+ }
31323
+ return {
31324
+ content: imageParts.length > 0 ? [...content ? [{ type: "text", text: content }] : [], ...imageParts] : content,
31325
+ ...unrecognizedImages.length > 0 ? { unrecognizedImages } : {}
31326
+ };
31327
+ }
31328
+ function normalizeMessage(message, toolCallId, syntheticToolCallIdOffset = 0) {
31329
+ if (typeof message.role !== "string") {
31330
+ return void 0;
31331
+ }
31332
+ const toolCalls = normalizeToolCalls(
31333
+ message.tool_calls,
31334
+ syntheticToolCallIdOffset
31335
+ );
31336
+ if (message.role === "tool") {
31337
+ const toolName = typeof message.tool_name === "string" && message.tool_name.length > 0 ? message.tool_name : "tool";
31338
+ return {
31339
+ role: "tool",
31340
+ tool_call_id: toolCallId ?? syntheticToolCallId(toolName, 0),
31341
+ content: typeof message.content === "string" ? message.content : ""
31342
+ };
31343
+ }
31344
+ const normalizedContent = normalizeTextAndImages(
31345
+ typeof message.content === "string" ? message.content || (toolCalls.length > 0 ? null : "") : toolCalls.length > 0 ? null : "",
31346
+ message.images
31347
+ );
31348
+ return {
31349
+ role: message.role,
31350
+ content: normalizedContent.content,
31351
+ ...typeof message.thinking === "string" && message.thinking.length > 0 ? { reasoning: message.thinking } : {},
31352
+ ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {},
31353
+ ...normalizedContent.unrecognizedImages ? { images: normalizedContent.unrecognizedImages } : {}
31354
+ };
31355
+ }
31356
+ function normalizeMessages(messages) {
31357
+ if (!Array.isArray(messages)) {
31358
+ return [];
31359
+ }
31360
+ const pendingToolCallIds = /* @__PURE__ */ new Map();
31361
+ let syntheticToolCallIdOffset = 0;
31362
+ return messages.flatMap((message) => {
31363
+ if (!isObject(message)) {
31364
+ return [];
31365
+ }
31366
+ const ollamaMessage = message;
31367
+ let toolCallId;
31368
+ if (ollamaMessage.role === "tool") {
31369
+ const toolName = ollamaMessage.tool_name;
31370
+ if (typeof toolName === "string") {
31371
+ toolCallId = pendingToolCallIds.get(toolName)?.shift();
31372
+ }
31373
+ }
31374
+ const normalized = normalizeMessage(
31375
+ ollamaMessage,
31376
+ toolCallId,
31377
+ syntheticToolCallIdOffset
31378
+ );
31379
+ if (Array.isArray(ollamaMessage.tool_calls)) {
31380
+ syntheticToolCallIdOffset += ollamaMessage.tool_calls.length;
31381
+ }
31382
+ if (ollamaMessage.role === "assistant" && normalized) {
31383
+ const toolCalls = Array.isArray(normalized.tool_calls) ? normalized.tool_calls : [];
31384
+ for (const toolCall of toolCalls) {
31385
+ if (!isObject(toolCall) || !isObject(toolCall.function)) {
31386
+ continue;
31387
+ }
31388
+ const name = toolCall.function.name;
31389
+ const id = toolCall.id;
31390
+ if (typeof name === "string" && typeof id === "string") {
31391
+ const ids = pendingToolCallIds.get(name) ?? [];
31392
+ ids.push(id);
31393
+ pendingToolCallIds.set(name, ids);
31394
+ }
31395
+ }
31396
+ }
31397
+ return normalized ? [normalized] : [];
31398
+ });
31399
+ }
31400
+ function normalizeToolDefinition(tool) {
31401
+ const fn = tool.function;
31402
+ if (!fn || typeof fn.name !== "string" || fn.name.length === 0) {
31403
+ return void 0;
31404
+ }
31405
+ return {
31406
+ type: "function",
31407
+ function: {
31408
+ name: fn.name,
31409
+ ...typeof fn.description === "string" ? { description: fn.description } : {},
31410
+ ...isObject(fn.parameters) ? { parameters: fn.parameters } : {}
31411
+ }
31412
+ };
31413
+ }
31414
+ function extractToolsMetadata(tools) {
31415
+ if (!Array.isArray(tools)) {
31416
+ return {};
31417
+ }
31418
+ const normalized = tools.flatMap((tool) => {
31419
+ if (!isObject(tool)) {
31420
+ return [];
31421
+ }
31422
+ const definition = normalizeToolDefinition(tool);
31423
+ return definition ? [definition] : [];
31424
+ });
31425
+ return normalized.length > 0 ? { tools: normalized } : {};
31426
+ }
31427
+ function extractOptionsMetadata(options) {
31428
+ if (!isObject(options)) {
31429
+ return {};
31430
+ }
31431
+ const metadata = {};
31432
+ const mappings = [
31433
+ ["temperature", "temperature"],
31434
+ ["top_p", "top_p"],
31435
+ ["num_predict", "max_tokens"],
31436
+ ["frequency_penalty", "frequency_penalty"],
31437
+ ["presence_penalty", "presence_penalty"],
31438
+ ["stop", "stop"]
31439
+ ];
31440
+ for (const [source, target] of mappings) {
31441
+ if (options[source] !== void 0) {
31442
+ metadata[target] = options[source];
31443
+ }
31444
+ }
31445
+ return metadata;
31446
+ }
31447
+ function extractRequestMetadata(request) {
31448
+ return {
31449
+ provider: "ollama",
31450
+ ...typeof request?.model === "string" ? { model: request.model } : {},
31451
+ ...extractOptionsMetadata(request?.options),
31452
+ ...request?.format !== void 0 ? { response_format: request.format } : {}
31453
+ };
31454
+ }
31455
+ function extractOllamaChatInput([request]) {
31456
+ return {
31457
+ input: normalizeMessages(request.messages),
31458
+ metadata: {
31459
+ ...extractRequestMetadata(request),
31460
+ ...extractToolsMetadata(request.tools)
31461
+ }
31462
+ };
31463
+ }
31464
+ function extractOllamaGenerateInput([request]) {
31465
+ const normalizedPrompt = normalizeTextAndImages(
31466
+ typeof request.prompt === "string" ? request.prompt : "",
31467
+ Array.isArray(request.images) ? request.images : void 0
31468
+ );
31469
+ const input = [
31470
+ ...typeof request.system === "string" ? [{ role: "system", content: request.system }] : [],
31471
+ {
31472
+ role: "user",
31473
+ content: normalizedPrompt.content,
31474
+ ...typeof request.suffix === "string" ? { suffix: request.suffix } : {},
31475
+ ...normalizedPrompt.unrecognizedImages ? { images: normalizedPrompt.unrecognizedImages } : {}
31476
+ }
31477
+ ];
31478
+ return {
31479
+ input,
31480
+ metadata: extractRequestMetadata(request)
31481
+ };
31482
+ }
31483
+ function extractOllamaEmbedInput([request]) {
31484
+ return {
31485
+ input: request.input,
31486
+ metadata: extractRequestMetadata(request)
31487
+ };
31488
+ }
31489
+ function extractOllamaChatOutput(result, syntheticToolCallIdOffset = 0) {
31490
+ if (!isObject(result) || !isObject(result.message)) {
31491
+ return void 0;
31492
+ }
31493
+ const message = normalizeMessage(
31494
+ result.message,
31495
+ void 0,
31496
+ syntheticToolCallIdOffset
31497
+ );
31498
+ if (!message) {
31499
+ return void 0;
31500
+ }
31501
+ const toolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
31502
+ return [
31503
+ {
31504
+ index: 0,
31505
+ finish_reason: normalizeFinishReason(result, toolCalls.length > 0),
31506
+ message
31507
+ }
31508
+ ];
31509
+ }
31510
+ function extractOllamaGenerateOutput(result) {
31511
+ if (!isObject(result) || typeof result.response !== "string") {
31512
+ return void 0;
31513
+ }
31514
+ return [
31515
+ {
31516
+ index: 0,
31517
+ finish_reason: normalizeFinishReason(result),
31518
+ message: {
31519
+ role: "assistant",
31520
+ content: result.response,
31521
+ ...typeof result.thinking === "string" && result.thinking.length > 0 ? { reasoning: result.thinking } : {}
31522
+ }
31523
+ }
31524
+ ];
31525
+ }
31526
+ function extractOllamaEmbedOutput(result) {
31527
+ const embedding = Array.isArray(result?.embeddings) ? result.embeddings[0] : void 0;
31528
+ return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
31529
+ }
31530
+ function extractOllamaResponseMetadata(result) {
31531
+ return typeof result?.model === "string" ? { model: result.model } : void 0;
31532
+ }
31533
+ function extractOllamaMetrics(result) {
31534
+ const metrics = {};
31535
+ const promptTokens = result?.prompt_eval_count;
31536
+ const completionTokens = result?.eval_count;
31537
+ if (isNonNegativeNumber(promptTokens)) {
31538
+ metrics.prompt_tokens = promptTokens;
31539
+ }
31540
+ if (isNonNegativeNumber(completionTokens)) {
31541
+ metrics.completion_tokens = completionTokens;
31542
+ }
31543
+ if (isNonNegativeNumber(promptTokens) || isNonNegativeNumber(completionTokens)) {
31544
+ metrics.tokens = (isNonNegativeNumber(promptTokens) ? promptTokens : 0) + (isNonNegativeNumber(completionTokens) ? completionTokens : 0);
31545
+ }
31546
+ return metrics;
31547
+ }
31548
+ function aggregateOllamaChatChunks(chunks, _result, event, _startTime) {
31549
+ const last = chunks.at(-1);
31550
+ const message = {
31551
+ role: "assistant",
31552
+ content: chunks.map((chunk) => chunk.message?.content ?? "").join(""),
31553
+ thinking: chunks.map((chunk) => chunk.message?.thinking ?? "").join(""),
31554
+ tool_calls: chunks.flatMap((chunk) => chunk.message?.tool_calls ?? [])
31555
+ };
31556
+ const response = {
31557
+ ...last,
31558
+ message
31559
+ };
31560
+ return {
31561
+ output: extractOllamaChatOutput(
31562
+ response,
31563
+ countOllamaToolCalls(event?.arguments?.[0]?.messages)
31564
+ ),
31565
+ metrics: extractOllamaMetrics(last ?? {}),
31566
+ metadata: extractOllamaResponseMetadata(last ?? {})
31567
+ };
31568
+ }
31569
+ function aggregateOllamaGenerateChunks(chunks, _result, _event, _startTime) {
31570
+ const last = chunks.at(-1);
31571
+ const response = {
31572
+ ...last,
31573
+ response: chunks.map((chunk) => chunk.response ?? "").join(""),
31574
+ thinking: chunks.map((chunk) => chunk.thinking ?? "").join("")
31575
+ };
31576
+ return {
31577
+ output: extractOllamaGenerateOutput(response),
31578
+ metrics: extractOllamaMetrics(last ?? {}),
31579
+ metadata: extractOllamaResponseMetadata(last ?? {})
31580
+ };
31581
+ }
31582
+
30168
31583
  // src/instrumentation/plugins/google-adk-channels.ts
30169
31584
  var googleADKChannels = defineChannels(
30170
31585
  "@google/adk",
@@ -33918,7 +35333,7 @@ var FlueObserveBridge = class {
33918
35333
  return;
33919
35334
  }
33920
35335
  const state = this.operationsById.get(event.operationId) ?? this.startSyntheticOperation(event);
33921
- const output = operationOutput(event);
35336
+ const output = operationOutput(event) ?? outputFromAgentTurn(state.latestAgentOutput);
33922
35337
  const metadata = {
33923
35338
  ...state.metadata,
33924
35339
  ...extractEventMetadata(event),
@@ -33927,7 +35342,7 @@ var FlueObserveBridge = class {
33927
35342
  };
33928
35343
  this.finishPendingChildrenForOperation(event, output);
33929
35344
  safeLog3(state.span, {
33930
- ...event.isError ? { error: toLoggedError(event.error) } : {},
35345
+ ...event.isError ? { error: toLoggedError(event.errorInfo ?? event.error) } : {},
33931
35346
  metadata,
33932
35347
  metrics: durationMetrics2(event.durationMs),
33933
35348
  output
@@ -33958,7 +35373,7 @@ var FlueObserveBridge = class {
33958
35373
  };
33959
35374
  const parent = this.parentSpanForTurn(event);
33960
35375
  const span = startFlueSpan(parent, {
33961
- name: `llm:${model ?? event.purpose ?? "unknown"}`,
35376
+ name: "flue.turn",
33962
35377
  spanAttributes: { type: "llm" /* LLM */ },
33963
35378
  startTime: eventTime(event.timestamp),
33964
35379
  event: {
@@ -34002,6 +35417,12 @@ var FlueObserveBridge = class {
34002
35417
  },
34003
35418
  output
34004
35419
  });
35420
+ if (event.purpose === "agent" && event.operationId) {
35421
+ const operation = this.operationsById.get(event.operationId);
35422
+ if (operation) {
35423
+ operation.latestAgentOutput = output;
35424
+ }
35425
+ }
34005
35426
  safeEnd(state.span, eventTime(event.timestamp));
34006
35427
  this.turnsByKey.delete(key);
34007
35428
  }
@@ -34081,7 +35502,7 @@ var FlueObserveBridge = class {
34081
35502
  return;
34082
35503
  }
34083
35504
  safeLog3(state.span, {
34084
- ...event.isError ? { error: toLoggedError(event.result) } : {},
35505
+ ...event.isError ? { error: toLoggedError(event.errorInfo ?? event.result) } : {},
34085
35506
  metadata: {
34086
35507
  ...state.metadata,
34087
35508
  ...extractEventMetadata(event),
@@ -34127,6 +35548,7 @@ var FlueObserveBridge = class {
34127
35548
  ...event.usage ? { "flue.usage": event.usage } : {}
34128
35549
  };
34129
35550
  safeLog3(state.span, {
35551
+ ...event.isError ? { error: toLoggedError(event.errorInfo ?? event.error) } : {},
34130
35552
  metadata,
34131
35553
  metrics: {
34132
35554
  ...durationMetrics2(event.durationMs),
@@ -34231,7 +35653,7 @@ var FlueObserveBridge = class {
34231
35653
  ...event.purpose ? { "flue.turn_purpose": event.purpose } : {}
34232
35654
  };
34233
35655
  const span = startFlueSpan(this.parentSpanForEvent(event), {
34234
- name: `llm:${model ?? event.purpose ?? "unknown"}`,
35656
+ name: "flue.turn",
34235
35657
  spanAttributes: { type: "llm" /* LLM */ },
34236
35658
  startTime: eventTime(event.timestamp),
34237
35659
  event: { metadata }
@@ -34363,7 +35785,10 @@ function extractEventMetadata(event, ctx) {
34363
35785
  return {
34364
35786
  ...event.runId ? { "flue.run_id": event.runId } : {},
34365
35787
  ...event.instanceId ? { "flue.instance_id": event.instanceId } : {},
35788
+ ...event.submissionId ? { "flue.submission_id": event.submissionId } : {},
34366
35789
  ...event.dispatchId ? { "flue.dispatch_id": event.dispatchId } : {},
35790
+ ...event.agentName ? { "flue.agent_name": event.agentName } : {},
35791
+ ...event.conversationId ? { "flue.conversation_id": event.conversationId } : {},
34367
35792
  ...typeof event.eventIndex === "number" ? { "flue.event_index": event.eventIndex } : {},
34368
35793
  ...event.session ? { "flue.session": event.session } : {},
34369
35794
  ...event.parentSession ? { "flue.parent_session": event.parentSession } : {},
@@ -34392,7 +35817,7 @@ function flueTurnRequestInput(event) {
34392
35817
  return event.request?.input ?? event.input;
34393
35818
  }
34394
35819
  function flueTurnRequestModel(event) {
34395
- return event.request?.model ?? event.model;
35820
+ return event.request?.requestedModel ?? event.request?.model ?? event.model;
34396
35821
  }
34397
35822
  function flueTurnRequestProvider(event) {
34398
35823
  return event.request?.providerName ?? event.provider ?? event.request?.providerId;
@@ -34401,10 +35826,10 @@ function flueTurnRequestApi(event) {
34401
35826
  return event.request?.api ?? event.api;
34402
35827
  }
34403
35828
  function flueTurnRequestReasoning(event) {
34404
- return event.request?.reasoning ?? event.reasoning;
35829
+ return event.request?.reasoningLevel ?? event.request?.reasoning ?? event.reasoning;
34405
35830
  }
34406
35831
  function flueTurnModel(event) {
34407
- return event.request?.model ?? event.model;
35832
+ return event.response?.responseModel ?? event.request?.requestedModel ?? event.request?.model ?? event.model;
34408
35833
  }
34409
35834
  function flueTurnProvider(event) {
34410
35835
  return event.request?.providerName ?? event.provider ?? event.request?.providerId;
@@ -34419,7 +35844,7 @@ function flueTurnOutput(event) {
34419
35844
  return event.response?.output ?? event.output;
34420
35845
  }
34421
35846
  function flueTurnStopReason(event) {
34422
- return event.response?.stopReason ?? event.stopReason;
35847
+ return event.response?.finishReason ?? event.response?.stopReason ?? event.stopReason;
34423
35848
  }
34424
35849
  function flueTurnError(event) {
34425
35850
  return event.response?.error ?? event.response?.errorInfo?.message ?? event.error;
@@ -34434,6 +35859,9 @@ function flueToolInput(event) {
34434
35859
  return event.input;
34435
35860
  }
34436
35861
  function flueToolOutput(event) {
35862
+ if (Object.hasOwn(event, "effectiveResult")) {
35863
+ return event.effectiveResult;
35864
+ }
34437
35865
  return event.output !== void 0 ? event.output : event.result;
34438
35866
  }
34439
35867
  function flueToolError(event) {
@@ -34441,10 +35869,36 @@ function flueToolError(event) {
34441
35869
  }
34442
35870
  function operationOutput(event) {
34443
35871
  if (event.operationKind === "prompt" || event.operationKind === "skill") {
34444
- return llmResultFromOperationResult(event.result);
35872
+ return outputFromAgentInvocation(event.agentOutput) ?? llmResultFromOperationResult(event.result);
34445
35873
  }
34446
35874
  return event.result ?? (event.operationKind === "compact" ? { completed: true } : void 0);
34447
35875
  }
35876
+ function outputFromAgentInvocation(output) {
35877
+ if (output?.type === "text") {
35878
+ return output.text;
35879
+ }
35880
+ if (output?.type === "data") {
35881
+ return output.data;
35882
+ }
35883
+ return void 0;
35884
+ }
35885
+ function outputFromAgentTurn(output) {
35886
+ if (!isObjectLike(output)) {
35887
+ return output;
35888
+ }
35889
+ const content = Reflect.get(output, "content");
35890
+ if (!Array.isArray(content)) {
35891
+ return output;
35892
+ }
35893
+ const text = content.flatMap((part) => {
35894
+ if (!isObjectLike(part) || Reflect.get(part, "type") !== "text") {
35895
+ return [];
35896
+ }
35897
+ const value = Reflect.get(part, "text");
35898
+ return typeof value === "string" ? [value] : [];
35899
+ });
35900
+ return text.length > 0 ? text.join("") : output;
35901
+ }
34448
35902
  function llmResultFromOperationResult(result) {
34449
35903
  if (!isObjectLike(result)) {
34450
35904
  return result;
@@ -34543,7 +35997,7 @@ function safeEnd(span, endTime) {
34543
35997
  }
34544
35998
  }
34545
35999
  function logInstrumentationError3(label, error2) {
34546
- console.error(`Error in ${label} instrumentation:`, error2);
36000
+ debugLogger.debug(`Error in ${label} instrumentation:`, error2);
34547
36001
  }
34548
36002
 
34549
36003
  // src/wrappers/langchain/callback-handler.ts
@@ -35472,12 +36926,13 @@ var piCodingAgentChannels = defineChannels(
35472
36926
  );
35473
36927
 
35474
36928
  // src/instrumentation/plugins/pi-coding-agent-plugin.ts
35475
- var piStreamPatchStates = /* @__PURE__ */ new WeakMap();
36929
+ var piAgentPatchStates = /* @__PURE__ */ new WeakMap();
36930
+ var piAgentEventSubscriptions = /* @__PURE__ */ new WeakSet();
35476
36931
  var piPromptContextStore;
35477
36932
  var PiCodingAgentPlugin = class extends BasePlugin {
35478
36933
  activePromptStates = /* @__PURE__ */ new Set();
35479
36934
  onEnable() {
35480
- this.subscribeToPrompt();
36935
+ this.interceptPrompt();
35481
36936
  }
35482
36937
  onDisable() {
35483
36938
  for (const unsubscribe of this.unsubscribers) {
@@ -35485,65 +36940,74 @@ var PiCodingAgentPlugin = class extends BasePlugin {
35485
36940
  }
35486
36941
  this.unsubscribers = [];
35487
36942
  for (const state of [...this.activePromptStates]) {
35488
- void finalizePiPromptRun(state).catch((error2) => {
35489
- logInstrumentationError4("Pi Coding Agent disable cleanup", error2);
35490
- });
36943
+ finishPiPromptRun(state);
35491
36944
  }
35492
36945
  }
35493
- subscribeToPrompt() {
35494
- const channel2 = piCodingAgentChannels.prompt.tracingChannel();
35495
- const states = /* @__PURE__ */ new WeakMap();
35496
- const unbindAutoInstrumentationSuppression = bindAutoInstrumentationSuppressionToStart(channel2);
35497
- const handlers = {
35498
- start: (event) => {
35499
- const state = startPiPromptRun(event, (state2) => {
35500
- this.activePromptStates.delete(state2);
35501
- });
35502
- if (state) {
35503
- this.activePromptStates.add(state);
35504
- states.set(event, state);
35505
- }
35506
- },
35507
- asyncEnd: async (event) => {
35508
- const state = states.get(event);
35509
- if (!state) {
35510
- return;
35511
- }
35512
- states.delete(event);
35513
- state.promptCallEnded = true;
35514
- if (!state.finalized && state.deferCompletionUntilTurnEnd && !state.turnEnded) {
35515
- if (!state.sawStreamFn) {
35516
- state.queued = true;
35517
- if (!state.streamPatchState.queuedPromptStates.includes(state)) {
35518
- state.streamPatchState.queuedPromptStates.push(state);
35519
- }
36946
+ interceptPrompt() {
36947
+ this.unsubscribers.push(
36948
+ piCodingAgentChannels.prompt.intercept(
36949
+ (target, thisArg, args, additional) => {
36950
+ const invokeTarget = () => Reflect.apply(target, thisArg, args);
36951
+ let state;
36952
+ try {
36953
+ state = startPiPromptRun(
36954
+ {
36955
+ ...additional,
36956
+ arguments: args,
36957
+ self: thisArg
36958
+ },
36959
+ (finalizedState) => {
36960
+ this.activePromptStates.delete(finalizedState);
36961
+ }
36962
+ );
36963
+ } catch (error2) {
36964
+ logInstrumentationError4("Pi Coding Agent prompt start", error2);
35520
36965
  }
35521
- return;
35522
- }
35523
- await finalizePiPromptRun(state);
35524
- },
35525
- error: async (event) => {
35526
- const state = states.get(event);
35527
- if (!state) {
35528
- return;
36966
+ if (!state) {
36967
+ return runWithAutoInstrumentationSuppressed(invokeTarget);
36968
+ }
36969
+ this.activePromptStates.add(state);
36970
+ return promptContextStore().run(
36971
+ state,
36972
+ () => runWithAutoInstrumentationSuppressed(() => {
36973
+ let result;
36974
+ try {
36975
+ result = invokeTarget();
36976
+ } catch (error2) {
36977
+ finishPiPromptRun(state, error2);
36978
+ throw error2;
36979
+ }
36980
+ return Promise.resolve(result).then(
36981
+ (value) => {
36982
+ finishPiPromptCall(state);
36983
+ return value;
36984
+ },
36985
+ (error2) => {
36986
+ finishPiPromptRun(state, error2);
36987
+ throw error2;
36988
+ }
36989
+ );
36990
+ })
36991
+ );
35529
36992
  }
35530
- states.delete(event);
35531
- await finalizePiPromptRun(state, event.error);
35532
- }
35533
- };
35534
- channel2.subscribe(handlers);
35535
- this.unsubscribers.push(() => {
35536
- unbindAutoInstrumentationSuppression?.();
35537
- channel2.unsubscribe(handlers);
35538
- });
36993
+ )
36994
+ );
35539
36995
  }
35540
36996
  };
36997
+ function finishPiPromptCall(state) {
36998
+ state.promptCallEnded = true;
36999
+ if (state.deferCompletionUntilTurnEnd && !state.turnEnded) {
37000
+ return;
37001
+ }
37002
+ finishPiPromptRun(state);
37003
+ }
35541
37004
  function startPiPromptRun(event, onFinalize) {
35542
37005
  const session = extractSession(event);
35543
37006
  const agent = session?.agent;
35544
37007
  if (!session || !isPiAgent(agent)) {
35545
37008
  return void 0;
35546
37009
  }
37010
+ installPiAgentInstrumentation(agent);
35547
37011
  const metadata = {
35548
37012
  ...extractSessionMetadata(session),
35549
37013
  ...extractPromptOptionsMetadata(event.arguments[1]),
@@ -35565,9 +37029,7 @@ function startPiPromptRun(event, onFinalize) {
35565
37029
  INSTRUMENTATION_NAMES.PI_CODING_AGENT
35566
37030
  )
35567
37031
  );
35568
- const streamPatchState = installPiStreamPatch(agent);
35569
37032
  const options = event.arguments[1];
35570
- const promptText = event.arguments[0];
35571
37033
  const state = {
35572
37034
  activeLlmSpans: /* @__PURE__ */ new Set(),
35573
37035
  activeToolSpans: /* @__PURE__ */ new Map(),
@@ -35579,29 +37041,10 @@ function startPiPromptRun(event, onFinalize) {
35579
37041
  metrics: {},
35580
37042
  onFinalize,
35581
37043
  promptCallEnded: false,
35582
- ...typeof promptText === "string" ? { promptText } : {},
35583
- queued: false,
35584
37044
  span,
35585
- sawStreamFn: false,
35586
37045
  startTime: getCurrentUnixTimestamp(),
35587
- streamPatchState,
35588
37046
  turnEnded: false
35589
37047
  };
35590
- state.restorePromptContext = enterPiPromptContext(state);
35591
- streamPatchState.activePromptStates.add(state);
35592
- try {
35593
- state.unsubscribeAgent = agent.subscribe(async (agentEvent) => {
35594
- try {
35595
- await runWithAutoInstrumentationSuppressed(
35596
- () => handlePiAgentEvent(state, agentEvent)
35597
- );
35598
- } catch (error2) {
35599
- logInstrumentationError4("Pi Coding Agent event", error2);
35600
- }
35601
- });
35602
- } catch (error2) {
35603
- logInstrumentationError4("Pi Coding Agent event subscription", error2);
35604
- }
35605
37048
  return state;
35606
37049
  }
35607
37050
  function extractSession(event) {
@@ -35615,111 +37058,55 @@ function promptContextStore() {
35615
37058
  piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
35616
37059
  return piPromptContextStore;
35617
37060
  }
35618
- function currentPromptContextFrames() {
35619
- return promptContextStore().getStore()?.frames ?? [];
35620
- }
35621
37061
  function currentPiPromptState() {
35622
- const frames = currentPromptContextFrames();
35623
- return frames[frames.length - 1]?.state;
35624
- }
35625
- function enterPiPromptContext(state) {
35626
- const frame = {
35627
- id: /* @__PURE__ */ Symbol("braintrust.pi-coding-agent.prompt"),
35628
- state
35629
- };
35630
- promptContextStore().enterWith({
35631
- frames: [...currentPromptContextFrames(), frame]
35632
- });
35633
- return () => {
35634
- const frames = currentPromptContextFrames().filter(
35635
- (candidate) => candidate.id !== frame.id
35636
- );
35637
- promptContextStore().enterWith(frames.length > 0 ? { frames } : void 0);
35638
- };
35639
- }
35640
- function installPiStreamPatch(agent) {
35641
- const existing = piStreamPatchStates.get(agent);
35642
- if (existing) {
35643
- if (agent.streamFn !== existing.wrappedStreamFn) {
35644
- debugLogger.debug(
35645
- "Pi Coding Agent streamFn changed while Braintrust instrumentation was active; preserving existing patch state."
35646
- );
35647
- }
35648
- return existing;
35649
- }
35650
- const patchState = {
35651
- activePromptStates: /* @__PURE__ */ new Set(),
35652
- agent,
35653
- originalStreamFn: agent.streamFn,
35654
- queuedPromptStates: [],
35655
- wrappedStreamFn: agent.streamFn
35656
- };
35657
- patchState.wrappedStreamFn = makeSharedInstrumentedStreamFn(patchState);
35658
- agent.streamFn = patchState.wrappedStreamFn;
35659
- piStreamPatchStates.set(agent, patchState);
35660
- return patchState;
35661
- }
35662
- function resolveStreamPromptState(patchState, context2) {
35663
- let lastUserText;
35664
- if (Array.isArray(context2.messages)) {
35665
- for (let i = context2.messages.length - 1; i >= 0; i--) {
35666
- const message = context2.messages[i];
35667
- if (isPiUserMessage(message)) {
35668
- if (typeof message.content === "string") {
35669
- lastUserText = message.content;
35670
- } else {
35671
- lastUserText = message.content.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
35672
- }
35673
- break;
35674
- }
35675
- }
35676
- }
35677
- if (lastUserText !== void 0) {
35678
- const queuedMatch = patchState.queuedPromptStates.find(
35679
- (state) => state.promptText === lastUserText
35680
- );
35681
- if (queuedMatch) {
35682
- return queuedMatch;
35683
- }
35684
- const matches = [...patchState.activePromptStates].filter(
35685
- (state) => state.promptText === lastUserText
37062
+ return promptContextStore().getStore();
37063
+ }
37064
+ function installPiAgentInstrumentation(agent) {
37065
+ const existing = piAgentPatchStates.get(agent);
37066
+ if (!existing || agent.streamFn !== existing.wrappedStreamFn) {
37067
+ const patchState = {
37068
+ originalStreamFn: agent.streamFn,
37069
+ wrappedStreamFn: agent.streamFn
37070
+ };
37071
+ patchState.wrappedStreamFn = makeInstrumentedStreamFn(
37072
+ agent,
37073
+ patchState.originalStreamFn
35686
37074
  );
35687
- if (matches.length === 1) {
35688
- return matches[0];
35689
- }
37075
+ agent.streamFn = patchState.wrappedStreamFn;
37076
+ piAgentPatchStates.set(agent, patchState);
35690
37077
  }
35691
- const contextState = currentPiPromptState();
35692
- if (contextState && patchState.activePromptStates.has(contextState) && (!contextState.queued || lastUserText !== void 0 && contextState.promptText === lastUserText)) {
35693
- return contextState;
37078
+ if (piAgentEventSubscriptions.has(agent)) {
37079
+ return;
35694
37080
  }
35695
- if (patchState.activePromptStates.size === 1) {
35696
- return [...patchState.activePromptStates][0];
37081
+ try {
37082
+ agent.subscribe(async (event) => {
37083
+ const state = currentPiPromptState();
37084
+ if (!state || state.agent !== agent || state.finalized) {
37085
+ return;
37086
+ }
37087
+ try {
37088
+ await runWithAutoInstrumentationSuppressed(
37089
+ () => handlePiAgentEvent(state, event)
37090
+ );
37091
+ } catch (error2) {
37092
+ logInstrumentationError4("Pi Coding Agent event", error2);
37093
+ }
37094
+ });
37095
+ piAgentEventSubscriptions.add(agent);
37096
+ } catch (error2) {
37097
+ logInstrumentationError4("Pi Coding Agent event subscription", error2);
35697
37098
  }
35698
- return void 0;
35699
37099
  }
35700
- function makeSharedInstrumentedStreamFn(patchState) {
37100
+ function makeInstrumentedStreamFn(agent, originalStreamFn) {
35701
37101
  return async function instrumentedPiStreamFn(model, context2, options) {
35702
- const state = resolveStreamPromptState(patchState, context2);
35703
- if (!state) {
35704
- const invokeOriginal = () => Reflect.apply(patchState.originalStreamFn, this, [
35705
- model,
35706
- context2,
35707
- options
35708
- ]);
35709
- return patchState.activePromptStates.size > 0 ? runWithAutoInstrumentationSuppressed(invokeOriginal) : invokeOriginal();
37102
+ const invokeOriginal = () => Reflect.apply(originalStreamFn, this, [model, context2, options]);
37103
+ const state = currentPiPromptState();
37104
+ if (!state || state.agent !== agent || state.finalized) {
37105
+ return invokeOriginal();
35710
37106
  }
35711
- state.sawStreamFn = true;
35712
- removeQueuedPromptState(state);
35713
- state.streamPatchState.eventPromptState = state;
35714
37107
  const llmState = await startPiLlmSpan(state, model, context2, options);
35715
37108
  try {
35716
- const stream = await runWithAutoInstrumentationSuppressed(
35717
- () => Reflect.apply(patchState.originalStreamFn, this, [
35718
- model,
35719
- context2,
35720
- options
35721
- ])
35722
- );
37109
+ const stream = await runWithAutoInstrumentationSuppressed(invokeOriginal);
35723
37110
  return patchAssistantMessageStream(stream, state, llmState);
35724
37111
  } catch (error2) {
35725
37112
  finishPiLlmSpan(state, llmState, void 0, error2);
@@ -35866,13 +37253,6 @@ async function handlePiAgentEvent(state, event) {
35866
37253
  if (state.finalized) {
35867
37254
  return;
35868
37255
  }
35869
- const eventPromptState = state.streamPatchState.eventPromptState;
35870
- if (eventPromptState && eventPromptState !== state) {
35871
- return;
35872
- }
35873
- if (!eventPromptState && (state.queued || state.streamPatchState.activePromptStates.size > 1 && currentPiPromptState() !== state)) {
35874
- return;
35875
- }
35876
37256
  switch (event.type) {
35877
37257
  case "message_end":
35878
37258
  if (isPiAssistantMessage(event.message)) {
@@ -35887,11 +37267,8 @@ async function handlePiAgentEvent(state, event) {
35887
37267
  addMetrics(state.metrics, extractUsageMetrics2(event.message.usage));
35888
37268
  }
35889
37269
  }
35890
- if (state.streamPatchState.eventPromptState === state) {
35891
- state.streamPatchState.eventPromptState = void 0;
35892
- }
35893
37270
  if (state.promptCallEnded && state.deferCompletionUntilTurnEnd) {
35894
- await finalizePiPromptRun(state);
37271
+ finishPiPromptRun(state);
35895
37272
  }
35896
37273
  return;
35897
37274
  case "tool_execution_start":
@@ -35964,19 +37341,13 @@ function finishPiToolSpan(state, event) {
35964
37341
  }
35965
37342
  }
35966
37343
  }
35967
- async function finalizePiPromptRun(state, error2) {
37344
+ function finishPiPromptRun(state, error2) {
35968
37345
  if (state.finalized) {
35969
37346
  return;
35970
37347
  }
35971
37348
  state.finalized = true;
35972
37349
  state.onFinalize?.(state);
35973
- restorePiStreamFn(state);
35974
- try {
35975
- state.unsubscribeAgent?.();
35976
- } catch (unsubscribeError) {
35977
- logInstrumentationError4("Pi Coding Agent unsubscribe", unsubscribeError);
35978
- }
35979
- await finishOpenLlmSpans(state, error2);
37350
+ finishOpenLlmSpans(state, error2);
35980
37351
  finishOpenToolSpans(state, error2);
35981
37352
  const metadata = {
35982
37353
  ...state.metadata,
@@ -35993,34 +37364,14 @@ async function finalizePiPromptRun(state, error2) {
35993
37364
  output: state.output
35994
37365
  });
35995
37366
  } finally {
35996
- state.span.end();
35997
- }
35998
- }
35999
- function restorePiStreamFn(state) {
36000
- const patchState = state.streamPatchState;
36001
- patchState.activePromptStates.delete(state);
36002
- removeQueuedPromptState(state);
36003
- if (patchState.eventPromptState === state) {
36004
- patchState.eventPromptState = void 0;
36005
- }
36006
- state.restorePromptContext?.();
36007
- if (patchState.activePromptStates.size > 0) {
36008
- return;
36009
- }
36010
- if (patchState.agent.streamFn === patchState.wrappedStreamFn) {
36011
- patchState.agent.streamFn = patchState.originalStreamFn;
36012
- }
36013
- piStreamPatchStates.delete(patchState.agent);
36014
- }
36015
- function removeQueuedPromptState(state) {
36016
- state.queued = false;
36017
- const queuedPromptStates = state.streamPatchState.queuedPromptStates;
36018
- const index = queuedPromptStates.indexOf(state);
36019
- if (index >= 0) {
36020
- queuedPromptStates.splice(index, 1);
37367
+ try {
37368
+ state.span.end();
37369
+ } catch (endError) {
37370
+ logInstrumentationError4("Pi Coding Agent prompt end", endError);
37371
+ }
36021
37372
  }
36022
37373
  }
36023
- async function finishOpenLlmSpans(state, error2) {
37374
+ function finishOpenLlmSpans(state, error2) {
36024
37375
  for (const llmState of [...state.activeLlmSpans]) {
36025
37376
  finishPiLlmSpan(state, llmState, void 0, error2);
36026
37377
  }
@@ -36101,7 +37452,7 @@ function normalizeAssistantMessage(message) {
36101
37452
  (part) => part.type === "thinking" && !part.redacted ? [part.thinking] : []
36102
37453
  ).join("");
36103
37454
  const toolCalls = message.content.flatMap(
36104
- (part) => part.type === "toolCall" ? [normalizeToolCall(part)] : []
37455
+ (part) => part.type === "toolCall" ? [normalizeToolCall2(part)] : []
36105
37456
  );
36106
37457
  return {
36107
37458
  role: "assistant",
@@ -36136,13 +37487,13 @@ function normalizeUserContent(content) {
36136
37487
  return part;
36137
37488
  });
36138
37489
  }
36139
- function normalizeToolCall(toolCall) {
37490
+ function normalizeToolCall2(toolCall) {
36140
37491
  return {
36141
37492
  id: toolCall.id,
36142
37493
  type: "function",
36143
37494
  function: {
36144
37495
  name: toolCall.name,
36145
- arguments: stringifyArguments(toolCall.arguments)
37496
+ arguments: stringifyArguments2(toolCall.arguments)
36146
37497
  }
36147
37498
  };
36148
37499
  }
@@ -36325,7 +37676,7 @@ function cleanMetrics5(metrics) {
36325
37676
  }
36326
37677
  return cleaned;
36327
37678
  }
36328
- function stringifyArguments(value) {
37679
+ function stringifyArguments2(value) {
36329
37680
  if (typeof value === "string") {
36330
37681
  return value;
36331
37682
  }
@@ -37893,6 +39244,7 @@ var BraintrustPlugin = class extends BasePlugin {
37893
39244
  anthropicPlugin = null;
37894
39245
  aiSDKPlugin = null;
37895
39246
  claudeAgentSDKPlugin = null;
39247
+ cloudflareThinkPlugin = null;
37896
39248
  cursorSDKPlugin = null;
37897
39249
  openAIAgentsPlugin = null;
37898
39250
  googleGenAIPlugin = null;
@@ -37901,6 +39253,7 @@ var BraintrustPlugin = class extends BasePlugin {
37901
39253
  openRouterPlugin = null;
37902
39254
  openRouterAgentPlugin = null;
37903
39255
  mistralPlugin = null;
39256
+ ollamaPlugin = null;
37904
39257
  googleADKPlugin = null;
37905
39258
  coherePlugin = null;
37906
39259
  groqPlugin = null;
@@ -37940,6 +39293,10 @@ var BraintrustPlugin = class extends BasePlugin {
37940
39293
  this.claudeAgentSDKPlugin = new ClaudeAgentSDKPlugin();
37941
39294
  this.claudeAgentSDKPlugin.enable();
37942
39295
  }
39296
+ if (integrations.cloudflareThink !== false) {
39297
+ this.cloudflareThinkPlugin = new CloudflareThinkPlugin();
39298
+ this.cloudflareThinkPlugin.enable();
39299
+ }
37943
39300
  if (integrations.cursorSDK !== false && integrations.cursor !== false) {
37944
39301
  this.cursorSDKPlugin = new CursorSDKPlugin();
37945
39302
  this.cursorSDKPlugin.enable();
@@ -37970,6 +39327,10 @@ var BraintrustPlugin = class extends BasePlugin {
37970
39327
  this.mistralPlugin = new MistralPlugin();
37971
39328
  this.mistralPlugin.enable();
37972
39329
  }
39330
+ if (integrations.ollama !== false) {
39331
+ this.ollamaPlugin = new OllamaPlugin();
39332
+ this.ollamaPlugin.enable();
39333
+ }
37973
39334
  if (integrations.googleADK !== false) {
37974
39335
  this.googleADKPlugin = new GoogleADKPlugin();
37975
39336
  this.googleADKPlugin.enable();
@@ -38046,6 +39407,10 @@ var BraintrustPlugin = class extends BasePlugin {
38046
39407
  this.claudeAgentSDKPlugin.disable();
38047
39408
  this.claudeAgentSDKPlugin = null;
38048
39409
  }
39410
+ if (this.cloudflareThinkPlugin) {
39411
+ this.cloudflareThinkPlugin.disable();
39412
+ this.cloudflareThinkPlugin = null;
39413
+ }
38049
39414
  if (this.cursorSDKPlugin) {
38050
39415
  this.cursorSDKPlugin.disable();
38051
39416
  this.cursorSDKPlugin = null;
@@ -38078,6 +39443,10 @@ var BraintrustPlugin = class extends BasePlugin {
38078
39443
  this.mistralPlugin.disable();
38079
39444
  this.mistralPlugin = null;
38080
39445
  }
39446
+ if (this.ollamaPlugin) {
39447
+ this.ollamaPlugin.disable();
39448
+ this.ollamaPlugin = null;
39449
+ }
38081
39450
  if (this.googleADKPlugin) {
38082
39451
  this.googleADKPlugin.disable();
38083
39452
  this.googleADKPlugin = null;
@@ -38163,6 +39532,7 @@ var envIntegrationAliases = {
38163
39532
  cloudflareaichat: "cloudflareAIChat",
38164
39533
  "cloudflare-ai-chat": "cloudflareAIChat",
38165
39534
  "@cloudflare/ai-chat": "cloudflareAIChat",
39535
+ cloudflarethink: "cloudflareThink",
38166
39536
  cursor: "cursor",
38167
39537
  "cursor-sdk": "cursorSDK",
38168
39538
  cursorsdk: "cursorSDK",
@@ -38183,6 +39553,7 @@ var envIntegrationAliases = {
38183
39553
  openrouteragent: "openrouterAgent",
38184
39554
  "openrouter-agent": "openrouterAgent",
38185
39555
  mistral: "mistral",
39556
+ ollama: "ollama",
38186
39557
  googleadk: "googleADK",
38187
39558
  "google-adk": "googleADK",
38188
39559
  cohere: "cohere",
@@ -38218,6 +39589,7 @@ function getDefaultInstrumentationIntegrations() {
38218
39589
  huggingface: true,
38219
39590
  claudeAgentSDK: true,
38220
39591
  cloudflareAIChat: true,
39592
+ cloudflareThink: true,
38221
39593
  cursor: true,
38222
39594
  cursorSDK: true,
38223
39595
  flue: true,
@@ -38226,6 +39598,7 @@ function getDefaultInstrumentationIntegrations() {
38226
39598
  openrouter: true,
38227
39599
  openrouterAgent: true,
38228
39600
  mistral: true,
39601
+ ollama: true,
38229
39602
  cohere: true,
38230
39603
  groq: true,
38231
39604
  bedrock: true,