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
@@ -14,8 +14,10 @@ var GLOBAL_INSTRUMENTATION_HOOKS_KEY = "__braintrust_instrumentation_hooks";
14
14
  var GLOBAL_INSTRUMENTATION_HOOKS_PROTOCOL_VERSION = 1;
15
15
  var GLOBAL_INSTRUMENTATION_HOOKS_REGISTRY_BRAND = "braintrust.global-instrumentation-hooks.registry";
16
16
  var GLOBAL_INSTRUMENTATION_HOOK_BRAND = "braintrust.global-instrumentation-hooks.hook";
17
+ var GLOBAL_INVOCATION_HOOK_BRAND = "braintrust.global-instrumentation-hooks.invocation-hook";
17
18
  var registryBrand = Symbol.for(GLOBAL_INSTRUMENTATION_HOOKS_REGISTRY_BRAND);
18
19
  var hookBrand = Symbol.for(GLOBAL_INSTRUMENTATION_HOOK_BRAND);
20
+ var invocationHookBrand = Symbol.for(GLOBAL_INVOCATION_HOOK_BRAND);
19
21
  var errorReporter;
20
22
  function setGlobalHookErrorReporter(reporter) {
21
23
  const previousReporter = errorReporter;
@@ -160,12 +162,69 @@ var traceEvents = [
160
162
  "asyncEnd",
161
163
  "error"
162
164
  ];
165
+ function traceInvocation(hook, operator, target, thisArg, args, additional, callbackIndex = -1) {
166
+ const context = {
167
+ ...additional,
168
+ arguments: args,
169
+ self: thisArg
170
+ };
171
+ const invoke2 = () => hook.invoke(target, thisArg, args, additional);
172
+ if (operator === "traceCallback") {
173
+ return hook.traceCallback(invoke2, callbackIndex, context);
174
+ }
175
+ if (operator === "tracePromise") {
176
+ return hook.tracePromise(invoke2, context);
177
+ }
178
+ return hook.traceSync(invoke2, context);
179
+ }
180
+ var InvocationHook = class {
181
+ interceptors = [];
182
+ get hasInterceptors() {
183
+ return this.interceptors.length > 0;
184
+ }
185
+ intercept(interceptor) {
186
+ if (typeof interceptor !== "function") {
187
+ throw new TypeError("interceptor must be a function");
188
+ }
189
+ this.interceptors = [...this.interceptors, interceptor];
190
+ let active = true;
191
+ return () => {
192
+ if (!active) {
193
+ return;
194
+ }
195
+ active = false;
196
+ const index = this.interceptors.indexOf(interceptor);
197
+ if (index !== -1) {
198
+ this.interceptors = [
199
+ ...this.interceptors.slice(0, index),
200
+ ...this.interceptors.slice(index + 1)
201
+ ];
202
+ }
203
+ };
204
+ }
205
+ invoke(target, thisArg, args, additional) {
206
+ const interceptors = this.interceptors;
207
+ if (interceptors.length === 0) {
208
+ return Reflect.apply(target, thisArg, args);
209
+ }
210
+ let next = target;
211
+ for (let index = interceptors.length - 1; index >= 0; index -= 1) {
212
+ const interceptor = interceptors[index];
213
+ const downstream = next;
214
+ next = function(...nextArgs) {
215
+ return interceptor(downstream, this, nextArgs, additional);
216
+ };
217
+ }
218
+ return Reflect.apply(next, thisArg, args);
219
+ }
220
+ };
163
221
  var TracingHook = class {
164
222
  start;
165
223
  end;
166
224
  asyncStart;
167
225
  asyncEnd;
168
226
  error;
227
+ invocationHook = new InvocationHook();
169
228
  constructor(nameOrChannels) {
170
229
  Object.defineProperty(this, hookBrand, {
171
230
  configurable: false,
@@ -173,6 +232,12 @@ var TracingHook = class {
173
232
  value: GLOBAL_INSTRUMENTATION_HOOKS_PROTOCOL_VERSION,
174
233
  writable: false
175
234
  });
235
+ Object.defineProperty(this, invocationHookBrand, {
236
+ configurable: false,
237
+ enumerable: false,
238
+ value: GLOBAL_INSTRUMENTATION_HOOKS_PROTOCOL_VERSION,
239
+ writable: false
240
+ });
176
241
  if (typeof nameOrChannels === "string") {
177
242
  this.start = new HookChannel(`tracing:${nameOrChannels}:start`);
178
243
  this.end = new HookChannel(`tracing:${nameOrChannels}:end`);
@@ -190,6 +255,26 @@ var TracingHook = class {
190
255
  get hasSubscribers() {
191
256
  return this.start.hasSubscribers || this.end.hasSubscribers || this.asyncStart.hasSubscribers || this.asyncEnd.hasSubscribers || this.error.hasSubscribers;
192
257
  }
258
+ get hasInterceptors() {
259
+ return this.invocationHook.hasInterceptors;
260
+ }
261
+ intercept(interceptor) {
262
+ return this.invocationHook.intercept(interceptor);
263
+ }
264
+ invoke(target, thisArg, args, additional) {
265
+ return this.invocationHook.invoke(target, thisArg, args, additional);
266
+ }
267
+ traceInvocation(operator, target, thisArg, args, additional, callbackIndex = -1) {
268
+ return traceInvocation(
269
+ this,
270
+ operator,
271
+ target,
272
+ thisArg,
273
+ args,
274
+ additional,
275
+ callbackIndex
276
+ );
277
+ }
193
278
  subscribe(handlers) {
194
279
  for (const eventName of traceEvents) {
195
280
  const handler = handlers[eventName];
@@ -411,6 +496,60 @@ function hasTracingHookShape(value) {
411
496
  return false;
412
497
  }
413
498
  }
499
+ function hasInvocationHookShape(value) {
500
+ if (typeof value !== "object" && typeof value !== "function" || value === null) {
501
+ return false;
502
+ }
503
+ try {
504
+ const hook = value;
505
+ return value[invocationHookBrand] === GLOBAL_INSTRUMENTATION_HOOKS_PROTOCOL_VERSION && typeof hook.hasInterceptors === "boolean" && typeof hook.intercept === "function" && typeof hook.invoke === "function";
506
+ } catch {
507
+ return false;
508
+ }
509
+ }
510
+ function installInvocationHook(value) {
511
+ const hasInvocationHook = hasInvocationHookShape(value);
512
+ const invocationHook = new InvocationHook();
513
+ try {
514
+ if (!hasInvocationHook) {
515
+ Object.defineProperties(value, {
516
+ [invocationHookBrand]: {
517
+ configurable: false,
518
+ enumerable: false,
519
+ value: GLOBAL_INSTRUMENTATION_HOOKS_PROTOCOL_VERSION,
520
+ writable: false
521
+ },
522
+ hasInterceptors: {
523
+ configurable: false,
524
+ enumerable: false,
525
+ get: () => invocationHook.hasInterceptors
526
+ },
527
+ intercept: {
528
+ configurable: false,
529
+ enumerable: false,
530
+ value: invocationHook.intercept.bind(invocationHook),
531
+ writable: false
532
+ },
533
+ invoke: {
534
+ configurable: false,
535
+ enumerable: false,
536
+ value: invocationHook.invoke.bind(invocationHook),
537
+ writable: false
538
+ }
539
+ });
540
+ }
541
+ if (typeof value.traceInvocation !== "function") {
542
+ Object.defineProperty(value, "traceInvocation", {
543
+ configurable: false,
544
+ enumerable: false,
545
+ value: traceInvocation.bind(void 0, value),
546
+ writable: false
547
+ });
548
+ }
549
+ } catch (error) {
550
+ reportError(error);
551
+ }
552
+ }
414
553
  function isCompatibleTracingHook(value) {
415
554
  try {
416
555
  return value[hookBrand] === GLOBAL_INSTRUMENTATION_HOOKS_PROTOCOL_VERSION && hasTracingHookShape(value);
@@ -497,6 +636,7 @@ function newGlobalTracingChannel(nameOrChannels) {
497
636
  return inertTracingHook;
498
637
  }
499
638
  if (isCompatibleTracingHook(existing)) {
639
+ installInvocationHook(existing);
500
640
  return existing;
501
641
  }
502
642
  if (existing !== void 0) {
@@ -5559,6 +5699,7 @@ var INSTRUMENTATION_NAMES = {
5559
5699
  CLAUDE_AGENT_SDK: "claude-agent-sdk",
5560
5700
  CLOUDFLARE_AI_CHAT: "cloudflare-ai-chat",
5561
5701
  CLOUDFLARE_AGENTS: "cloudflare-agents",
5702
+ CLOUDFLARE_THINK: "cloudflare-think",
5562
5703
  COHERE: "cohere",
5563
5704
  CURSOR_SDK: "cursor-sdk",
5564
5705
  EVE: "eve",
@@ -5573,6 +5714,7 @@ var INSTRUMENTATION_NAMES = {
5573
5714
  LANGSMITH: "langsmith",
5574
5715
  MASTRA: "mastra",
5575
5716
  MISTRAL: "mistral",
5717
+ OLLAMA: "ollama",
5576
5718
  OPENAI: "openai",
5577
5719
  OPENAI_AGENTS: "openai-agents",
5578
5720
  OPENAI_CODEX: "openai-codex",
@@ -5584,7 +5726,7 @@ var INSTRUMENTATION_NAMES = {
5584
5726
  var INTERNAL_SPAN_INSTRUMENTATION_NAME = /* @__PURE__ */ Symbol.for(
5585
5727
  "braintrust.spanInstrumentationName"
5586
5728
  );
5587
- var SDK_VERSION = true ? "3.26.0" : "0.0.0";
5729
+ var SDK_VERSION = true ? "3.27.0" : "0.0.0";
5588
5730
  function withSpanInstrumentationName(args, instrumentationName) {
5589
5731
  return {
5590
5732
  ...args,
@@ -10905,6 +11047,34 @@ function patchStreamIfNeeded(stream, options) {
10905
11047
  );
10906
11048
  return stream;
10907
11049
  }
11050
+ const chunks = [];
11051
+ let completed = false;
11052
+ const notifyCancellation = async () => {
11053
+ try {
11054
+ await (options.onCancel ?? options.onComplete)(chunks);
11055
+ } catch (error) {
11056
+ debugLogger.error("Error in stream cancellation handler:", error);
11057
+ }
11058
+ };
11059
+ const patchAbortIfPresent = () => {
11060
+ try {
11061
+ if ("abort" in stream && typeof stream.abort === "function") {
11062
+ const originalAbort = stream.abort.bind(stream);
11063
+ stream.abort = (...args) => {
11064
+ try {
11065
+ return originalAbort(...args);
11066
+ } finally {
11067
+ if (!completed) {
11068
+ completed = true;
11069
+ void notifyCancellation();
11070
+ }
11071
+ }
11072
+ };
11073
+ }
11074
+ } catch (error) {
11075
+ debugLogger.warn("Failed to patch stream abort method:", error);
11076
+ }
11077
+ };
10908
11078
  if (hasAsyncIteratorMethods(stream) && isSelfAsyncIterator(stream)) {
10909
11079
  if ("__braintrust_patched_iterator_methods" in stream) {
10910
11080
  return stream;
@@ -10913,8 +11083,6 @@ function patchStreamIfNeeded(stream, options) {
10913
11083
  const originalNext = stream.next.bind(stream);
10914
11084
  const originalReturn = typeof stream.return === "function" ? stream.return.bind(stream) : null;
10915
11085
  const originalThrow = typeof stream.throw === "function" ? stream.throw.bind(stream) : null;
10916
- const chunks = [];
10917
- let completed = false;
10918
11086
  stream.next = async (...args) => {
10919
11087
  try {
10920
11088
  const result = await runNextWithWrapper(
@@ -10966,11 +11134,7 @@ function patchStreamIfNeeded(stream, options) {
10966
11134
  stream.return = async (...args) => {
10967
11135
  if (!completed) {
10968
11136
  completed = true;
10969
- try {
10970
- await options.onComplete(chunks);
10971
- } catch (error) {
10972
- console.error("Error in stream onComplete handler:", error);
10973
- }
11137
+ await notifyCancellation();
10974
11138
  }
10975
11139
  return originalReturn(...args);
10976
11140
  };
@@ -10995,6 +11159,7 @@ function patchStreamIfNeeded(stream, options) {
10995
11159
  Object.defineProperty(stream, "__braintrust_patched_iterator_methods", {
10996
11160
  value: true
10997
11161
  });
11162
+ patchAbortIfPresent();
10998
11163
  return stream;
10999
11164
  } catch (error) {
11000
11165
  console.warn("Failed to patch stream iterator methods:", error);
@@ -11008,8 +11173,6 @@ function patchStreamIfNeeded(stream, options) {
11008
11173
  const patchedIteratorFn = function() {
11009
11174
  const iterator = originalIteratorFn.call(this);
11010
11175
  const originalNext = iterator.next.bind(iterator);
11011
- const chunks = [];
11012
- let completed = false;
11013
11176
  iterator.next = async function(...args) {
11014
11177
  try {
11015
11178
  const result = await runNextWithWrapper(
@@ -11062,11 +11225,7 @@ function patchStreamIfNeeded(stream, options) {
11062
11225
  iterator.return = async function(...args) {
11063
11226
  if (!completed) {
11064
11227
  completed = true;
11065
- try {
11066
- await options.onComplete(chunks);
11067
- } catch (error) {
11068
- console.error("Error in stream onComplete handler:", error);
11069
- }
11228
+ await notifyCancellation();
11070
11229
  }
11071
11230
  return originalReturn(...args);
11072
11231
  };
@@ -11095,6 +11254,7 @@ function patchStreamIfNeeded(stream, options) {
11095
11254
  value: true
11096
11255
  });
11097
11256
  stream[Symbol.asyncIterator] = patchedIteratorFn;
11257
+ patchAbortIfPresent();
11098
11258
  return stream;
11099
11259
  } catch (error) {
11100
11260
  console.warn("Failed to patch stream:", error);
@@ -11843,6 +12003,33 @@ function traceStreamingChannel(channel2, config) {
11843
12003
  const { span, startTime } = spanData;
11844
12004
  if (isAsyncIterable(asyncEndEvent.result)) {
11845
12005
  let firstChunkTime;
12006
+ const handleStreamError = (error) => {
12007
+ try {
12008
+ span.log({ error });
12009
+ } catch (loggingError) {
12010
+ debugLogger.error(
12011
+ `Error logging failure for ${channelName}:`,
12012
+ loggingError
12013
+ );
12014
+ }
12015
+ try {
12016
+ span.end();
12017
+ } catch (endingError) {
12018
+ debugLogger.error(
12019
+ `Error ending span for ${channelName}:`,
12020
+ endingError
12021
+ );
12022
+ }
12023
+ states.delete(event);
12024
+ runStreamingErrorHook({
12025
+ channelName,
12026
+ config,
12027
+ error,
12028
+ event: asyncEndEvent,
12029
+ span,
12030
+ startTime
12031
+ });
12032
+ };
11846
12033
  patchStreamIfNeeded(asyncEndEvent.result, {
11847
12034
  onChunk: () => {
11848
12035
  if (firstChunkTime === void 0) {
@@ -11922,33 +12109,12 @@ function traceStreamingChannel(channel2, config) {
11922
12109
  });
11923
12110
  }
11924
12111
  },
11925
- onError: (error) => {
11926
- try {
11927
- span.log({ error });
11928
- } catch (loggingError) {
11929
- debugLogger.error(
11930
- `Error logging failure for ${channelName}:`,
11931
- loggingError
11932
- );
11933
- }
11934
- try {
11935
- span.end();
11936
- } catch (endingError) {
11937
- debugLogger.error(
11938
- `Error ending span for ${channelName}:`,
11939
- endingError
11940
- );
11941
- }
11942
- states.delete(event);
11943
- runStreamingErrorHook({
11944
- channelName,
11945
- config,
11946
- error,
11947
- event: asyncEndEvent,
11948
- span,
11949
- startTime
11950
- });
11951
- }
12112
+ onCancel: () => {
12113
+ const error = new Error("Stream cancelled before completion");
12114
+ error.name = "AbortError";
12115
+ handleStreamError(error);
12116
+ },
12117
+ onError: handleStreamError
11952
12118
  });
11953
12119
  return;
11954
12120
  }
@@ -12336,11 +12502,21 @@ function defineChannels(pkg, channels, options) {
12336
12502
  const tracingChannel2 = () => isomorph_default.newTracingChannel(
12337
12503
  fullChannelName
12338
12504
  );
12505
+ const intercept2 = (interceptor) => {
12506
+ const hook = tracingChannel2();
12507
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
12508
+ };
12509
+ };
12339
12510
  return [
12340
12511
  key,
12341
12512
  {
12342
12513
  ...asyncSpec,
12343
12514
  instrumentationName,
12515
+ intercept: intercept2,
12516
+ invoke: (target, thisArg, args, additional) => {
12517
+ const hook = tracingChannel2();
12518
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
12519
+ },
12344
12520
  tracingChannel: tracingChannel2,
12345
12521
  tracePromise: (fn, context) => tracingChannel2().tracePromise(
12346
12522
  fn,
@@ -12354,11 +12530,21 @@ function defineChannels(pkg, channels, options) {
12354
12530
  const tracingChannel = () => isomorph_default.newTracingChannel(
12355
12531
  fullChannelName
12356
12532
  );
12533
+ const intercept = (interceptor) => {
12534
+ const hook = tracingChannel();
12535
+ return typeof hook.intercept === "function" ? hook.intercept(interceptor) : () => {
12536
+ };
12537
+ };
12357
12538
  return [
12358
12539
  key,
12359
12540
  {
12360
12541
  ...syncSpec,
12361
12542
  instrumentationName,
12543
+ intercept,
12544
+ invoke: (target, thisArg, args, additional) => {
12545
+ const hook = tracingChannel();
12546
+ return typeof hook.invoke === "function" ? hook.invoke(target, thisArg, args, additional) : Reflect.apply(target, thisArg, args);
12547
+ },
12362
12548
  tracingChannel,
12363
12549
  traceSync: (fn, context) => tracingChannel().traceSync(
12364
12550
  fn,
@@ -13609,6 +13795,12 @@ function extractAnthropicCacheTokens(cacheReadTokens = 0, cacheCreationTokens =
13609
13795
  return cacheTokens;
13610
13796
  }
13611
13797
 
13798
+ // src/wrappers/anthropic-session-collector.ts
13799
+ var sessionStreamCollectors = /* @__PURE__ */ new WeakMap();
13800
+ function registerAnthropicSessionStreamCollector(stream, collector) {
13801
+ sessionStreamCollectors.set(stream, collector);
13802
+ }
13803
+
13612
13804
  // src/instrumentation/plugins/anthropic-channels.ts
13613
13805
  var anthropicChannels = defineChannels(
13614
13806
  "@anthropic-ai/sdk",
@@ -13624,6 +13816,14 @@ var anthropicChannels = defineChannels(
13624
13816
  betaMessagesToolRunner: channel({
13625
13817
  channelName: "beta.messages.toolRunner",
13626
13818
  kind: "sync-stream"
13819
+ }),
13820
+ betaSessionsEventsStream: channel({
13821
+ channelName: "beta.sessions.events.stream",
13822
+ kind: "async"
13823
+ }),
13824
+ betaSessionsThreadsEventsStream: channel({
13825
+ channelName: "beta.sessions.threads.events.stream",
13826
+ kind: "async"
13627
13827
  })
13628
13828
  },
13629
13829
  { instrumentationName: INSTRUMENTATION_NAMES.ANTHROPIC }
@@ -13637,6 +13837,7 @@ var AnthropicPlugin = class extends BasePlugin {
13637
13837
  onEnable() {
13638
13838
  this.subscribeToAnthropicChannels();
13639
13839
  this.subscribeToAnthropicToolRunner();
13840
+ this.subscribeToAnthropicSessionStreams();
13640
13841
  }
13641
13842
  onDisable() {
13642
13843
  this.unsubscribers = unsubscribeAll(this.unsubscribers);
@@ -13754,7 +13955,411 @@ var AnthropicPlugin = class extends BasePlugin {
13754
13955
  tracingChannel.unsubscribe(handlers);
13755
13956
  });
13756
13957
  }
13958
+ subscribeToAnthropicSessionStreams() {
13959
+ this.subscribeToAnthropicSessionStream(
13960
+ anthropicChannels.betaSessionsEventsStream,
13961
+ false
13962
+ );
13963
+ this.subscribeToAnthropicSessionStream(
13964
+ anthropicChannels.betaSessionsThreadsEventsStream,
13965
+ true
13966
+ );
13967
+ }
13968
+ subscribeToAnthropicSessionStream(channel2, isThread) {
13969
+ const tracingChannel = channel2.tracingChannel();
13970
+ const pending = /* @__PURE__ */ new WeakSet();
13971
+ const handlers = {
13972
+ start: (event) => {
13973
+ if (isAutoInstrumentationSuppressed()) {
13974
+ return;
13975
+ }
13976
+ pending.add(event);
13977
+ },
13978
+ asyncEnd: (event) => {
13979
+ if (!pending.delete(event)) {
13980
+ return;
13981
+ }
13982
+ const stream = event.result;
13983
+ if (!isAsyncIterable(stream)) {
13984
+ return;
13985
+ }
13986
+ registerAnthropicSessionStreamCollector(stream, () => {
13987
+ wrapAnthropicSessionEventStream(stream, isThread);
13988
+ });
13989
+ },
13990
+ error: (event) => {
13991
+ pending.delete(event);
13992
+ }
13993
+ };
13994
+ tracingChannel.subscribe(handlers);
13995
+ this.unsubscribers.push(() => tracingChannel.unsubscribe(handlers));
13996
+ }
13757
13997
  };
13998
+ function wrapAnthropicSessionEventStream(stream, isThread) {
13999
+ const state = {
14000
+ history: [],
14001
+ isThread,
14002
+ pendingInput: [],
14003
+ seenEventIds: /* @__PURE__ */ new Set()
14004
+ };
14005
+ patchStreamIfNeeded(stream, {
14006
+ onChunk: (event) => handleAnthropicSessionEvent(state, event),
14007
+ onComplete: () => finalizeAnthropicSessionTurn(state),
14008
+ onError: (error) => finalizeAnthropicSessionTurn(state, error)
14009
+ });
14010
+ return stream;
14011
+ }
14012
+ function handleAnthropicSessionEvent(state, event) {
14013
+ try {
14014
+ if (typeof event.id === "string" && event.type !== "event_delta" && state.seenEventIds.has(event.id)) {
14015
+ return;
14016
+ }
14017
+ if (typeof event.id === "string" && event.type !== "event_delta") {
14018
+ state.seenEventIds.add(event.id);
14019
+ }
14020
+ switch (event.type) {
14021
+ case "user.message":
14022
+ case "system.message":
14023
+ recordAnthropicSessionInput(
14024
+ state,
14025
+ event
14026
+ );
14027
+ break;
14028
+ case "session.status_running":
14029
+ if (!state.isThread) {
14030
+ ensureAnthropicSessionTurn(state);
14031
+ }
14032
+ break;
14033
+ case "session.thread_status_running":
14034
+ if (state.isThread) {
14035
+ ensureAnthropicSessionTurn(state);
14036
+ }
14037
+ break;
14038
+ case "span.model_request_start":
14039
+ if (typeof event.id === "string") {
14040
+ startAnthropicSessionModel(state, event.id);
14041
+ }
14042
+ break;
14043
+ case "event_delta":
14044
+ recordAnthropicSessionFirstToken(state, event);
14045
+ break;
14046
+ case "agent.message":
14047
+ recordAnthropicSessionMessage(
14048
+ state,
14049
+ event
14050
+ );
14051
+ break;
14052
+ case "agent.custom_tool_use":
14053
+ case "agent.mcp_tool_use":
14054
+ case "agent.tool_use":
14055
+ startAnthropicSessionTool(state, event);
14056
+ break;
14057
+ case "user.tool_confirmation":
14058
+ recordAnthropicSessionToolConfirmation(state, event);
14059
+ break;
14060
+ case "agent.mcp_tool_result":
14061
+ case "agent.tool_result":
14062
+ case "user.custom_tool_result":
14063
+ case "user.tool_result":
14064
+ finalizeAnthropicSessionTool(
14065
+ state,
14066
+ event
14067
+ );
14068
+ break;
14069
+ case "span.model_request_end":
14070
+ finalizeAnthropicSessionModel(
14071
+ state,
14072
+ event
14073
+ );
14074
+ break;
14075
+ case "session.status_idle":
14076
+ case "session.thread_status_idle": {
14077
+ if (event.type === "session.status_idle" && state.isThread || event.type === "session.thread_status_idle" && !state.isThread) {
14078
+ break;
14079
+ }
14080
+ const stopReason = isObject(event.stop_reason) ? event.stop_reason.type : void 0;
14081
+ if (stopReason === "end_turn") {
14082
+ finalizeAnthropicSessionTurn(state);
14083
+ } else if (stopReason === "retries_exhausted") {
14084
+ finalizeAnthropicSessionTurn(
14085
+ state,
14086
+ "Anthropic session retries exhausted"
14087
+ );
14088
+ }
14089
+ break;
14090
+ }
14091
+ case "session.error":
14092
+ finalizeAnthropicSessionTurn(state, event.error);
14093
+ break;
14094
+ case "session.deleted":
14095
+ case "session.status_terminated":
14096
+ case "session.thread_status_terminated":
14097
+ if (event.type === "session.deleted" || event.type === "session.status_terminated" && !state.isThread || event.type === "session.thread_status_terminated" && state.isThread) {
14098
+ finalizeAnthropicSessionTurn(state);
14099
+ }
14100
+ break;
14101
+ default:
14102
+ break;
14103
+ }
14104
+ } catch (error) {
14105
+ debugLogger.debug("Error processing Anthropic Sessions event:", error);
14106
+ }
14107
+ }
14108
+ function ensureAnthropicSessionTurn(state) {
14109
+ if (state.activeTurn) {
14110
+ return state.activeTurn;
14111
+ }
14112
+ const input = state.pendingInput;
14113
+ state.pendingInput = [];
14114
+ const span = startSpan(
14115
+ withSpanInstrumentationName(
14116
+ {
14117
+ event: {
14118
+ ...input.length > 0 ? { input } : {},
14119
+ metadata: { provider: "anthropic" }
14120
+ },
14121
+ name: state.isThread ? "anthropic.beta.sessions.thread.turn" : "anthropic.beta.sessions.turn",
14122
+ spanAttributes: { type: "task" /* TASK */ }
14123
+ },
14124
+ INSTRUMENTATION_NAMES.ANTHROPIC
14125
+ )
14126
+ );
14127
+ state.activeTurn = {
14128
+ activeTools: /* @__PURE__ */ new Map(),
14129
+ input: input.slice(),
14130
+ metrics: {},
14131
+ span
14132
+ };
14133
+ return state.activeTurn;
14134
+ }
14135
+ function recordAnthropicSessionInput(state, event) {
14136
+ const content = processAttachmentsInInput(event.content);
14137
+ const message = {
14138
+ role: event.type === "system.message" ? "system" : "user",
14139
+ content
14140
+ };
14141
+ state.history.push(message);
14142
+ if (state.activeTurn) {
14143
+ state.activeTurn.input.push(message);
14144
+ } else {
14145
+ state.pendingInput.push(message);
14146
+ }
14147
+ }
14148
+ function startAnthropicSessionModel(state, startEventId) {
14149
+ const turn = ensureAnthropicSessionTurn(state);
14150
+ if (turn.activeModel) {
14151
+ finalizeAnthropicSessionModelState(state, turn);
14152
+ }
14153
+ const span = withCurrent(
14154
+ turn.span,
14155
+ () => startSpan(
14156
+ withSpanInstrumentationName(
14157
+ {
14158
+ event: {
14159
+ ...state.history.length > 0 ? { input: state.history.slice() } : {},
14160
+ metadata: { provider: "anthropic" }
14161
+ },
14162
+ name: "anthropic.messages.create",
14163
+ spanAttributes: { type: "llm" /* LLM */ }
14164
+ },
14165
+ INSTRUMENTATION_NAMES.ANTHROPIC
14166
+ )
14167
+ )
14168
+ );
14169
+ turn.activeModel = {
14170
+ output: [],
14171
+ span,
14172
+ startEventId,
14173
+ startTime: getCurrentUnixTimestamp()
14174
+ };
14175
+ }
14176
+ function recordAnthropicSessionFirstToken(state, event) {
14177
+ const model = state.activeTurn?.activeModel;
14178
+ if (!model || model.firstTokenTime !== void 0 || !isObject(event.delta) || !isObject(event.delta.content) || typeof event.delta.content.text !== "string") {
14179
+ return;
14180
+ }
14181
+ model.firstTokenTime = getCurrentUnixTimestamp();
14182
+ }
14183
+ function recordAnthropicSessionMessage(state, event) {
14184
+ const turn = ensureAnthropicSessionTurn(state);
14185
+ const content = processAttachmentsInInput(
14186
+ event.content
14187
+ );
14188
+ if (turn.activeModel) {
14189
+ turn.activeModel.output.push(...content);
14190
+ } else {
14191
+ const output = { role: "assistant", content };
14192
+ turn.lastOutput = output;
14193
+ state.history.push(output);
14194
+ }
14195
+ }
14196
+ function startAnthropicSessionTool(state, event) {
14197
+ const turn = ensureAnthropicSessionTurn(state);
14198
+ if (turn.activeModel) {
14199
+ turn.activeModel.output.push({
14200
+ type: "tool_use",
14201
+ id: event.id,
14202
+ name: event.name,
14203
+ input: event.input
14204
+ });
14205
+ }
14206
+ const existing = turn.activeTools.get(event.id);
14207
+ if (existing) {
14208
+ existing.span.end();
14209
+ turn.activeTools.delete(event.id);
14210
+ }
14211
+ const span = withCurrent(
14212
+ turn.span,
14213
+ () => startSpan(
14214
+ withSpanInstrumentationName(
14215
+ {
14216
+ event: { input: event.input },
14217
+ name: event.name,
14218
+ spanAttributes: { type: "tool" /* TOOL */ }
14219
+ },
14220
+ INSTRUMENTATION_NAMES.ANTHROPIC
14221
+ )
14222
+ )
14223
+ );
14224
+ const toolState = {
14225
+ approval: event.evaluated_permission === "allow" ? "approved" : event.evaluated_permission === "deny" ? "denied" : void 0,
14226
+ span
14227
+ };
14228
+ turn.activeTools.set(event.id, toolState);
14229
+ if (toolState.approval === "denied") {
14230
+ finalizeAnthropicSessionToolState(toolState);
14231
+ turn.activeTools.delete(event.id);
14232
+ }
14233
+ }
14234
+ function recordAnthropicSessionToolConfirmation(state, event) {
14235
+ if (typeof event.tool_use_id !== "string" || event.result !== "allow" && event.result !== "deny") {
14236
+ return;
14237
+ }
14238
+ const turn = state.activeTurn;
14239
+ const tool = turn?.activeTools.get(event.tool_use_id);
14240
+ if (!turn || !tool) {
14241
+ return;
14242
+ }
14243
+ tool.approval = event.result === "allow" ? "approved" : "denied";
14244
+ if (tool.approval === "denied") {
14245
+ finalizeAnthropicSessionToolState(tool);
14246
+ turn.activeTools.delete(event.tool_use_id);
14247
+ }
14248
+ }
14249
+ function finalizeAnthropicSessionTool(state, event) {
14250
+ const toolUseId = event.tool_use_id ?? event.mcp_tool_use_id ?? event.custom_tool_use_id;
14251
+ if (!toolUseId) {
14252
+ return;
14253
+ }
14254
+ const content = processAttachmentsInInput(event.content);
14255
+ const turn = state.activeTurn;
14256
+ const tool = turn?.activeTools.get(toolUseId);
14257
+ if (turn && tool) {
14258
+ finalizeAnthropicSessionToolState(tool, event, content);
14259
+ turn.activeTools.delete(toolUseId);
14260
+ }
14261
+ const toolResult = {
14262
+ type: "tool_result",
14263
+ tool_use_id: toolUseId,
14264
+ ...content !== void 0 ? { content } : {},
14265
+ ...event.is_error === true ? { is_error: true } : {}
14266
+ };
14267
+ state.history.push({
14268
+ role: "user",
14269
+ content: [toolResult]
14270
+ });
14271
+ }
14272
+ function finalizeAnthropicSessionToolState(state, event, content = event?.content) {
14273
+ safeAnthropicSessionLog(state.span, {
14274
+ ...event?.is_error === true ? {
14275
+ error: content ?? "Anthropic session tool execution failed"
14276
+ } : {},
14277
+ ...state.approval ? { metadata: { tool_approval: state.approval } } : {},
14278
+ ...content !== void 0 ? { output: content } : {}
14279
+ });
14280
+ state.span.end();
14281
+ }
14282
+ function finalizeAnthropicSessionModel(state, event) {
14283
+ const turn = state.activeTurn;
14284
+ const model = turn?.activeModel;
14285
+ if (!turn || !model || model.startEventId !== event.model_request_start_id) {
14286
+ return;
14287
+ }
14288
+ finalizeAnthropicSessionModelState(state, turn, event);
14289
+ }
14290
+ function finalizeAnthropicSessionModelState(state, turn, event) {
14291
+ const model = turn.activeModel;
14292
+ if (!model) {
14293
+ return;
14294
+ }
14295
+ turn.activeModel = void 0;
14296
+ const metrics = event ? parseAnthropicSessionModelUsage(event.model_usage) : {};
14297
+ if (model.firstTokenTime !== void 0) {
14298
+ metrics.time_to_first_token = model.firstTokenTime - model.startTime;
14299
+ }
14300
+ const output = model.output.length > 0 ? { role: "assistant", content: model.output } : void 0;
14301
+ safeAnthropicSessionLog(model.span, {
14302
+ ...event?.is_error ? { error: "Anthropic session model request failed" } : {},
14303
+ ...Object.keys(metrics).length > 0 ? { metrics } : {},
14304
+ ...output ? { output } : {}
14305
+ });
14306
+ model.span.end();
14307
+ if (output) {
14308
+ turn.lastOutput = output;
14309
+ state.history.push(output);
14310
+ }
14311
+ for (const key of [
14312
+ "prompt_tokens",
14313
+ "completion_tokens",
14314
+ "tokens",
14315
+ "prompt_cached_tokens",
14316
+ "prompt_cache_creation_tokens"
14317
+ ]) {
14318
+ const value = metrics[key];
14319
+ if (typeof value === "number") {
14320
+ turn.metrics[key] = (turn.metrics[key] ?? 0) + value;
14321
+ }
14322
+ }
14323
+ }
14324
+ function parseAnthropicSessionModelUsage(usage) {
14325
+ const finalized = finalizeAnthropicTokens({
14326
+ completion_tokens: usage.output_tokens,
14327
+ prompt_cache_creation_tokens: usage.cache_creation_input_tokens,
14328
+ prompt_cached_tokens: usage.cache_read_input_tokens,
14329
+ prompt_tokens: usage.input_tokens
14330
+ });
14331
+ return Object.fromEntries(
14332
+ Object.entries(finalized).filter(
14333
+ (entry) => entry[1] !== void 0
14334
+ )
14335
+ );
14336
+ }
14337
+ function finalizeAnthropicSessionTurn(state, error) {
14338
+ const turn = state.activeTurn;
14339
+ if (!turn) {
14340
+ return;
14341
+ }
14342
+ state.activeTurn = void 0;
14343
+ finalizeAnthropicSessionModelState(state, turn);
14344
+ for (const tool of turn.activeTools.values()) {
14345
+ finalizeAnthropicSessionToolState(tool);
14346
+ }
14347
+ turn.activeTools.clear();
14348
+ safeAnthropicSessionLog(turn.span, {
14349
+ ...error !== void 0 ? { error: error instanceof Error ? error : toLoggedError(error) } : {},
14350
+ ...turn.input.length > 0 ? { input: turn.input } : {},
14351
+ ...turn.lastOutput ? { output: turn.lastOutput } : {},
14352
+ ...Object.keys(turn.metrics).length > 0 ? { metrics: turn.metrics } : {}
14353
+ });
14354
+ turn.span.end();
14355
+ }
14356
+ function safeAnthropicSessionLog(span, event) {
14357
+ try {
14358
+ span.log(event);
14359
+ } catch (error) {
14360
+ debugLogger.debug("Error logging Anthropic Sessions span:", error);
14361
+ }
14362
+ }
13758
14363
  function parseMetricsFromUsage2(usage) {
13759
14364
  if (!usage) {
13760
14365
  return {};
@@ -15628,6 +16233,29 @@ var harnessAgentChannels = defineChannels(
15628
16233
  { instrumentationName: INSTRUMENTATION_NAMES.AI_SDK }
15629
16234
  );
15630
16235
 
16236
+ // src/instrumentation/plugins/cloudflare-think-context.ts
16237
+ var cloudflareThinkSpans = /* @__PURE__ */ new Map();
16238
+ function registerCloudflareThinkSpan(span) {
16239
+ if (span.spanId) {
16240
+ cloudflareThinkSpans.set(span.spanId, span);
16241
+ }
16242
+ }
16243
+ function unregisterCloudflareThinkSpan(span) {
16244
+ if (span.spanId) {
16245
+ cloudflareThinkSpans.delete(span.spanId);
16246
+ }
16247
+ }
16248
+ function currentCloudflareThinkSpan() {
16249
+ const parentSpanIds = _internalGetGlobalState()?.contextManager.getParentSpanIds()?.spanParents ?? [];
16250
+ for (const parentSpanId of parentSpanIds) {
16251
+ const span = cloudflareThinkSpans.get(parentSpanId);
16252
+ if (span) {
16253
+ return span;
16254
+ }
16255
+ }
16256
+ return void 0;
16257
+ }
16258
+
15631
16259
  // src/instrumentation/plugins/ai-sdk-plugin.ts
15632
16260
  var DEFAULT_DENY_OUTPUT_PATHS = [
15633
16261
  // v3
@@ -15734,6 +16362,7 @@ var AISDKPlugin = class extends BasePlugin {
15734
16362
  traceStreamingChannel(aiSDKChannels.streamText, {
15735
16363
  name: "streamText",
15736
16364
  type: "function" /* FUNCTION */,
16365
+ shouldTrace: () => currentCloudflareThinkSpan() === void 0,
15737
16366
  extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
15738
16367
  extractOutput: (result, endEvent) => processAISDKOutput(
15739
16368
  result,
@@ -15754,6 +16383,7 @@ var AISDKPlugin = class extends BasePlugin {
15754
16383
  traceSyncStreamChannel(aiSDKChannels.streamTextSync, {
15755
16384
  name: "streamText",
15756
16385
  type: "function" /* FUNCTION */,
16386
+ shouldTrace: () => currentCloudflareThinkSpan() === void 0,
15757
16387
  extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
15758
16388
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
15759
16389
  defaultDenyOutputPaths: denyOutputPaths,
@@ -16739,6 +17369,23 @@ function prepareAISDKWorkflowAgentStreamInput(params, event, span, defaultDenyOu
16739
17369
  metadata
16740
17370
  };
16741
17371
  }
17372
+ function prepareAISDKAgentCallInput(params, event, span, defaultDenyOutputPaths = DEFAULT_DENY_OUTPUT_PATHS) {
17373
+ const { input } = processAISDKWorkflowAgentCallInput(params);
17374
+ const metadata = extractMetadataFromCallParams(params, event.self);
17375
+ const childTracing = prepareAISDKChildTracing(
17376
+ params,
17377
+ event.self,
17378
+ span,
17379
+ defaultDenyOutputPaths,
17380
+ event.aiSDK,
17381
+ { agentOwner: true }
17382
+ );
17383
+ event.modelWrapped = childTracing.modelWrapped;
17384
+ if (childTracing.cleanup) {
17385
+ event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
17386
+ }
17387
+ return { input, metadata };
17388
+ }
16742
17389
  function prepareAISDKEmbedInput(params, self) {
16743
17390
  return {
16744
17391
  input: { ...params },
@@ -16762,6 +17409,18 @@ function extractTopLevelAISDKMetrics(result, event, startTime) {
16762
17409
  }
16763
17410
  return metrics;
16764
17411
  }
17412
+ async function extractResolvedAISDKTokenMetrics(result) {
17413
+ for (const field of ["totalUsage", "usage"]) {
17414
+ try {
17415
+ const usage = await Promise.resolve(result[field]);
17416
+ if (usage !== void 0) {
17417
+ return extractTokenMetrics({ [field]: usage });
17418
+ }
17419
+ } catch {
17420
+ }
17421
+ }
17422
+ return extractTokenMetrics(result);
17423
+ }
16765
17424
  function hasModelChildTracing(event) {
16766
17425
  return event?.modelWrapped === true || event?.__braintrust_ai_sdk_model_wrapped === true;
16767
17426
  }
@@ -17579,13 +18238,15 @@ function patchAISDKStreamingResult(args) {
17579
18238
  const {
17580
18239
  defaultDenyOutputPaths,
17581
18240
  endEvent,
18241
+ forceTopLevelMetrics,
17582
18242
  onComplete,
17583
18243
  onCancel,
17584
18244
  onError,
17585
18245
  result,
17586
18246
  resolvePromiseUsage,
17587
18247
  span,
17588
- startTime
18248
+ startTime,
18249
+ transformOutput
17589
18250
  } = args;
17590
18251
  if (!result || typeof result !== "object") {
17591
18252
  return false;
@@ -17638,8 +18299,8 @@ function patchAISDKStreamingResult(args) {
17638
18299
  }
17639
18300
  outputLogged = true;
17640
18301
  try {
17641
- const metrics = extractTopLevelAISDKMetrics(result, endEvent);
17642
- if (resolvePromiseUsage) {
18302
+ const metrics = forceTopLevelMetrics ? await extractResolvedAISDKTokenMetrics(result) : extractTopLevelAISDKMetrics(result, endEvent);
18303
+ if (resolvePromiseUsage && !forceTopLevelMetrics) {
17643
18304
  try {
17644
18305
  const resultRecord2 = result;
17645
18306
  const pendingUsage = resultRecord2.totalUsage ?? resultRecord2.usage;
@@ -17658,10 +18319,11 @@ function patchAISDKStreamingResult(args) {
17658
18319
  if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
17659
18320
  metrics.time_to_first_token = firstChunkTime - startTime;
17660
18321
  }
17661
- const output = await processAISDKStreamingOutput(
18322
+ const processedOutput = await processAISDKStreamingOutput(
17662
18323
  result,
17663
18324
  resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
17664
18325
  );
18326
+ const output = transformOutput ? transformOutput(processedOutput) : processedOutput;
17665
18327
  const metadata = buildResolvedMetadataPayload(result).metadata;
17666
18328
  try {
17667
18329
  span.log({
@@ -20083,6 +20745,295 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
20083
20745
  }
20084
20746
  };
20085
20747
 
20748
+ // src/instrumentation/plugins/cloudflare-think-channels.ts
20749
+ var cloudflareThinkChannels = defineChannels(
20750
+ "@cloudflare/think",
20751
+ {
20752
+ runInferenceLoop: channel({
20753
+ channelName: "Think.runInferenceLoop",
20754
+ kind: "async"
20755
+ })
20756
+ },
20757
+ { instrumentationName: INSTRUMENTATION_NAMES.CLOUDFLARE_THINK }
20758
+ );
20759
+
20760
+ // src/instrumentation/plugins/cloudflare-think-plugin.ts
20761
+ var THINK_STATE_ID = /* @__PURE__ */ Symbol.for("braintrust.cloudflare-think.state-id");
20762
+ var CloudflareThinkPlugin = class extends BasePlugin {
20763
+ statesBySpanId = /* @__PURE__ */ new Map();
20764
+ onEnable() {
20765
+ this.subscribeToThinkRuns();
20766
+ this.subscribeToAISDKStreamTextSync();
20767
+ this.subscribeToAISDKStreamTextAsync();
20768
+ }
20769
+ onDisable() {
20770
+ for (const unsubscribe of this.unsubscribers) {
20771
+ unsubscribe();
20772
+ }
20773
+ this.unsubscribers = [];
20774
+ for (const state of this.statesBySpanId.values()) {
20775
+ this.finishState(state);
20776
+ }
20777
+ this.statesBySpanId.clear();
20778
+ }
20779
+ subscribeToThinkRuns() {
20780
+ const channel2 = cloudflareThinkChannels.runInferenceLoop.tracingChannel();
20781
+ const states = /* @__PURE__ */ new WeakMap();
20782
+ const state = _internalGetGlobalState();
20783
+ const contextManager = state?.contextManager;
20784
+ const currentSpanStore = contextManager ? contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
20785
+ const ensureState2 = (event) => {
20786
+ if (isAutoInstrumentationSuppressed()) {
20787
+ return void 0;
20788
+ }
20789
+ const existing = states.get(event);
20790
+ if (existing) {
20791
+ return existing;
20792
+ }
20793
+ const span = startSpan(
20794
+ withSpanInstrumentationName(
20795
+ {
20796
+ name: "Think.runTurn",
20797
+ spanAttributes: { type: "task" /* TASK */ }
20798
+ },
20799
+ INSTRUMENTATION_NAMES.CLOUDFLARE_THINK
20800
+ )
20801
+ );
20802
+ const runState = {
20803
+ aiResultPatched: false,
20804
+ fallbackInput: extractFallbackInput(event.self?.messages),
20805
+ finalized: false,
20806
+ inputLogged: false,
20807
+ span,
20808
+ startTime: getCurrentUnixTimestamp()
20809
+ };
20810
+ const metadata = {
20811
+ braintrust: {
20812
+ integration_name: "cloudflare-think",
20813
+ sdk_language: "typescript"
20814
+ }
20815
+ };
20816
+ if (typeof event.moduleVersion === "string") {
20817
+ metadata["cloudflare_think.version"] = event.moduleVersion;
20818
+ }
20819
+ span.log({ metadata });
20820
+ states.set(event, runState);
20821
+ if (span.spanId) {
20822
+ this.statesBySpanId.set(span.spanId, runState);
20823
+ }
20824
+ registerCloudflareThinkSpan(span);
20825
+ return runState;
20826
+ };
20827
+ if (contextManager && currentSpanStore && channel2.start) {
20828
+ channel2.start.bindStore(currentSpanStore, (event) => {
20829
+ const runState = ensureState2(event);
20830
+ return runState ? contextManager.wrapSpanForStore(runState.span) : currentSpanStore.getStore();
20831
+ });
20832
+ this.unsubscribers.push(
20833
+ () => channel2.start?.unbindStore(currentSpanStore)
20834
+ );
20835
+ }
20836
+ const handlers = {
20837
+ start: (event) => {
20838
+ ensureState2(event);
20839
+ },
20840
+ asyncEnd: (event) => {
20841
+ const runState = states.get(event);
20842
+ states.delete(event);
20843
+ if (!runState || runState.finalized || runState.aiResultPatched) {
20844
+ return;
20845
+ }
20846
+ this.finishState(runState, void 0, event.result);
20847
+ },
20848
+ error: (event) => {
20849
+ const runState = states.get(event);
20850
+ states.delete(event);
20851
+ if (runState) {
20852
+ this.finishState(runState, event.error);
20853
+ }
20854
+ }
20855
+ };
20856
+ channel2.subscribe(handlers);
20857
+ this.unsubscribers.push(() => channel2.unsubscribe(handlers));
20858
+ }
20859
+ subscribeToAISDKStreamTextSync() {
20860
+ const channel2 = aiSDKChannels.streamTextSync.tracingChannel();
20861
+ const handlers = {
20862
+ start: (event) => {
20863
+ this.startAISDKStream(event);
20864
+ },
20865
+ end: (event) => {
20866
+ this.endAISDKStream(event);
20867
+ },
20868
+ error: (event) => {
20869
+ this.errorAISDKStream(event);
20870
+ }
20871
+ };
20872
+ channel2.subscribe(handlers);
20873
+ this.unsubscribers.push(() => channel2.unsubscribe(handlers));
20874
+ }
20875
+ subscribeToAISDKStreamTextAsync() {
20876
+ const channel2 = aiSDKChannels.streamText.tracingChannel();
20877
+ const handlers = {
20878
+ start: (event) => {
20879
+ this.startAISDKStream(event);
20880
+ },
20881
+ asyncEnd: (event) => {
20882
+ this.endAISDKStream(event);
20883
+ },
20884
+ error: (event) => {
20885
+ this.errorAISDKStream(event);
20886
+ }
20887
+ };
20888
+ channel2.subscribe(handlers);
20889
+ this.unsubscribers.push(() => channel2.unsubscribe(handlers));
20890
+ }
20891
+ startAISDKStream(event) {
20892
+ const runState = this.currentState();
20893
+ if (!runState || runState.finalized) {
20894
+ return;
20895
+ }
20896
+ try {
20897
+ const prepared = prepareAISDKAgentCallInput(
20898
+ event.arguments[0],
20899
+ event,
20900
+ runState.span
20901
+ );
20902
+ runState.span.log({
20903
+ input: extractThinkTaskInput(prepared.input),
20904
+ metadata: {
20905
+ ...prepared.metadata,
20906
+ braintrust: {
20907
+ integration_name: "cloudflare-think",
20908
+ sdk_language: "typescript"
20909
+ }
20910
+ }
20911
+ });
20912
+ runState.inputLogged = true;
20913
+ runState.aiEvent = event;
20914
+ Reflect.set(event, THINK_STATE_ID, runState.span.spanId);
20915
+ } catch (error) {
20916
+ debugLogger.error(
20917
+ "Error preparing @cloudflare/think AI SDK tracing:",
20918
+ error
20919
+ );
20920
+ }
20921
+ }
20922
+ endAISDKStream(event) {
20923
+ const runState = this.stateForAISDKEvent(event);
20924
+ if (!runState || runState.finalized) {
20925
+ return;
20926
+ }
20927
+ const patched = patchAISDKStreamingResult({
20928
+ defaultDenyOutputPaths: DEFAULT_DENY_OUTPUT_PATHS,
20929
+ endEvent: event,
20930
+ forceTopLevelMetrics: true,
20931
+ onComplete: () => this.releaseState(runState),
20932
+ result: event.result,
20933
+ span: runState.span,
20934
+ startTime: runState.startTime,
20935
+ transformOutput: extractThinkTaskOutput
20936
+ });
20937
+ runState.aiResultPatched = patched;
20938
+ if (!patched) {
20939
+ this.finishState(runState, void 0, event.result);
20940
+ }
20941
+ }
20942
+ errorAISDKStream(event) {
20943
+ const runState = this.stateForAISDKEvent(event);
20944
+ if (runState) {
20945
+ this.finishState(runState, event.error);
20946
+ }
20947
+ }
20948
+ currentState() {
20949
+ const parentIds = _internalGetGlobalState()?.contextManager.getParentSpanIds()?.spanParents ?? [];
20950
+ for (const parentId of parentIds) {
20951
+ const state = this.statesBySpanId.get(parentId);
20952
+ if (state) {
20953
+ return state;
20954
+ }
20955
+ }
20956
+ return void 0;
20957
+ }
20958
+ stateForAISDKEvent(event) {
20959
+ const spanId = Reflect.get(event, THINK_STATE_ID);
20960
+ return typeof spanId === "string" ? this.statesBySpanId.get(spanId) : void 0;
20961
+ }
20962
+ finishState(state, error, output) {
20963
+ if (state.finalized) {
20964
+ return;
20965
+ }
20966
+ state.finalized = true;
20967
+ try {
20968
+ if (!state.inputLogged && state.fallbackInput !== void 0) {
20969
+ state.span.log({ input: state.fallbackInput });
20970
+ }
20971
+ if (error !== void 0) {
20972
+ state.span.log({ error });
20973
+ } else if (output !== void 0) {
20974
+ state.span.log({ output: extractThinkTaskOutput(output) });
20975
+ }
20976
+ finalizeAISDKChildTracing(state.aiEvent);
20977
+ state.span.end();
20978
+ } finally {
20979
+ this.releaseState(state);
20980
+ }
20981
+ }
20982
+ releaseState(state) {
20983
+ state.finalized = true;
20984
+ unregisterCloudflareThinkSpan(state.span);
20985
+ if (state.span.spanId) {
20986
+ this.statesBySpanId.delete(state.span.spanId);
20987
+ }
20988
+ }
20989
+ };
20990
+ function extractFallbackInput(messages) {
20991
+ if (!Array.isArray(messages) || messages.length === 0) {
20992
+ return void 0;
20993
+ }
20994
+ let latestUserIndex = -1;
20995
+ for (let index = messages.length - 1; index >= 0; index--) {
20996
+ if (messages[index]?.role === "user") {
20997
+ latestUserIndex = index;
20998
+ break;
20999
+ }
21000
+ }
21001
+ const selected = latestUserIndex >= 0 ? messages.slice(latestUserIndex) : [];
21002
+ return selected.map((message) => ({
21003
+ role: message.role,
21004
+ content: message.content ?? message.parts
21005
+ }));
21006
+ }
21007
+ function extractThinkTaskOutput(output) {
21008
+ if (!isObject(output)) {
21009
+ return output;
21010
+ }
21011
+ if (typeof output.text === "string") {
21012
+ return { role: "assistant", content: output.text };
21013
+ }
21014
+ if (output.object !== void 0) {
21015
+ return output.object;
21016
+ }
21017
+ return output;
21018
+ }
21019
+ function extractThinkTaskInput(input) {
21020
+ if (!isObject(input)) {
21021
+ return input;
21022
+ }
21023
+ const system = input.instructions ?? input.system;
21024
+ if (Array.isArray(input.messages)) {
21025
+ return system === void 0 ? input.messages : [{ role: "system", content: system }, ...input.messages];
21026
+ }
21027
+ if (Array.isArray(input.prompt)) {
21028
+ return system === void 0 ? input.prompt : [{ role: "system", content: system }, ...input.prompt];
21029
+ }
21030
+ if (typeof input.prompt === "string") {
21031
+ const messages = [{ role: "user", content: input.prompt }];
21032
+ return system === void 0 ? messages : [{ role: "system", content: system }, ...messages];
21033
+ }
21034
+ return input;
21035
+ }
21036
+
20086
21037
  // src/instrumentation/plugins/cursor-sdk-channels.ts
20087
21038
  var cursorSDKChannels = defineChannels(
20088
21039
  "@cursor/sdk",
@@ -25848,6 +26799,470 @@ function aggregateMistralStreamChunks(chunks) {
25848
26799
  };
25849
26800
  }
25850
26801
 
26802
+ // src/instrumentation/plugins/ollama-channels.ts
26803
+ var ollamaChannels = defineChannels(
26804
+ "ollama",
26805
+ {
26806
+ chat: channel({
26807
+ channelName: "chat",
26808
+ kind: "async"
26809
+ }),
26810
+ generate: channel({
26811
+ channelName: "generate",
26812
+ kind: "async"
26813
+ }),
26814
+ embed: channel({
26815
+ channelName: "embed",
26816
+ kind: "async"
26817
+ })
26818
+ },
26819
+ { instrumentationName: INSTRUMENTATION_NAMES.OLLAMA }
26820
+ );
26821
+
26822
+ // src/instrumentation/plugins/ollama-plugin.ts
26823
+ var OllamaPlugin = class extends BasePlugin {
26824
+ onEnable() {
26825
+ this.unsubscribers.push(
26826
+ traceStreamingChannel(ollamaChannels.chat, {
26827
+ name: "ollama.chat",
26828
+ type: "llm" /* LLM */,
26829
+ extractInput: extractOllamaChatInput,
26830
+ extractOutput: (result, event) => extractOllamaChatOutput(
26831
+ result,
26832
+ countOllamaToolCalls(event?.arguments?.[0]?.messages)
26833
+ ),
26834
+ extractMetadata: extractOllamaResponseMetadata,
26835
+ extractMetrics: extractOllamaMetrics,
26836
+ aggregateChunks: aggregateOllamaChatChunks
26837
+ }),
26838
+ traceStreamingChannel(ollamaChannels.generate, {
26839
+ name: "ollama.generate",
26840
+ type: "llm" /* LLM */,
26841
+ extractInput: extractOllamaGenerateInput,
26842
+ extractOutput: extractOllamaGenerateOutput,
26843
+ extractMetadata: extractOllamaResponseMetadata,
26844
+ extractMetrics: extractOllamaMetrics,
26845
+ aggregateChunks: aggregateOllamaGenerateChunks
26846
+ }),
26847
+ traceAsyncChannel(ollamaChannels.embed, {
26848
+ name: "ollama.embed",
26849
+ type: "llm" /* LLM */,
26850
+ extractInput: extractOllamaEmbedInput,
26851
+ extractOutput: extractOllamaEmbedOutput,
26852
+ extractMetadata: extractOllamaResponseMetadata,
26853
+ extractMetrics: extractOllamaMetrics
26854
+ })
26855
+ );
26856
+ }
26857
+ onDisable() {
26858
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
26859
+ }
26860
+ };
26861
+ function isNonNegativeNumber(value) {
26862
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
26863
+ }
26864
+ function normalizeFinishReason(response, hasToolCalls = false) {
26865
+ if (hasToolCalls) {
26866
+ return "tool_calls";
26867
+ }
26868
+ return response?.done_reason || "stop";
26869
+ }
26870
+ function stringifyArguments(value) {
26871
+ if (typeof value === "string") {
26872
+ return value;
26873
+ }
26874
+ try {
26875
+ return JSON.stringify(value ?? {});
26876
+ } catch {
26877
+ return String(value);
26878
+ }
26879
+ }
26880
+ function syntheticToolCallId(name, index) {
26881
+ const normalizedName = name.replace(/[^a-zA-Z0-9_-]/g, "_") || "tool";
26882
+ return `ollama_call_${normalizedName}_${index}`;
26883
+ }
26884
+ function normalizeToolCall(toolCall, index) {
26885
+ const name = toolCall.function?.name;
26886
+ if (typeof name !== "string" || name.length === 0) {
26887
+ return void 0;
26888
+ }
26889
+ return {
26890
+ id: typeof toolCall.id === "string" && toolCall.id.length > 0 ? toolCall.id : syntheticToolCallId(name, index),
26891
+ type: "function",
26892
+ function: {
26893
+ name,
26894
+ arguments: stringifyArguments(toolCall.function?.arguments)
26895
+ }
26896
+ };
26897
+ }
26898
+ function normalizeToolCalls(toolCalls, syntheticIdOffset = 0) {
26899
+ if (!Array.isArray(toolCalls)) {
26900
+ return [];
26901
+ }
26902
+ return toolCalls.flatMap((toolCall, index) => {
26903
+ const normalized = normalizeToolCall(toolCall, syntheticIdOffset + index);
26904
+ return normalized ? [normalized] : [];
26905
+ });
26906
+ }
26907
+ function countOllamaToolCalls(messages) {
26908
+ if (!Array.isArray(messages)) {
26909
+ return 0;
26910
+ }
26911
+ return messages.reduce(
26912
+ (count, message) => count + (isObject(message) && Array.isArray(message.tool_calls) ? message.tool_calls.length : 0),
26913
+ 0
26914
+ );
26915
+ }
26916
+ function imageBytes(value) {
26917
+ if (value instanceof Uint8Array) {
26918
+ return value;
26919
+ }
26920
+ if (value instanceof ArrayBuffer) {
26921
+ return new Uint8Array(value);
26922
+ }
26923
+ if (typeof value !== "string" || value.startsWith("data:")) {
26924
+ return void 0;
26925
+ }
26926
+ try {
26927
+ const decoded = atob(value.slice(0, 24));
26928
+ return Uint8Array.from(decoded, (character) => character.charCodeAt(0));
26929
+ } catch {
26930
+ return void 0;
26931
+ }
26932
+ }
26933
+ function inferImageMediaType(value) {
26934
+ if (typeof value === "string") {
26935
+ const dataUrlType = value.match(/^data:(image\/[^;]+);base64,/i)?.[1];
26936
+ if (dataUrlType) {
26937
+ return dataUrlType;
26938
+ }
26939
+ }
26940
+ const bytes = imageBytes(value);
26941
+ if (!bytes) {
26942
+ return void 0;
26943
+ }
26944
+ if (bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71) {
26945
+ return "image/png";
26946
+ }
26947
+ if (bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) {
26948
+ return "image/jpeg";
26949
+ }
26950
+ const signature = String.fromCharCode(...bytes.slice(0, 12));
26951
+ if (signature.startsWith("GIF87a") || signature.startsWith("GIF89a")) {
26952
+ return "image/gif";
26953
+ }
26954
+ if (signature.startsWith("RIFF") && signature.slice(8, 12) === "WEBP") {
26955
+ return "image/webp";
26956
+ }
26957
+ return void 0;
26958
+ }
26959
+ function normalizeTextAndImages(content, images) {
26960
+ if (!images || images.length === 0) {
26961
+ return { content };
26962
+ }
26963
+ const imageParts = [];
26964
+ const unrecognizedImages = [];
26965
+ for (const image of images) {
26966
+ let localImagePath;
26967
+ let localPathMediaType;
26968
+ if (typeof image === "string" && !/^data:/i.test(image) && !/^https?:\/\//i.test(image)) {
26969
+ const extension = image.match(/\.([a-z0-9]+)$/i)?.[1]?.toLowerCase();
26970
+ const extensionMediaType = {
26971
+ png: "image/png",
26972
+ jpg: "image/jpeg",
26973
+ jpeg: "image/jpeg",
26974
+ gif: "image/gif",
26975
+ webp: "image/webp"
26976
+ }[extension ?? ""];
26977
+ if (extensionMediaType && isomorph_default.statSync) {
26978
+ try {
26979
+ if (isomorph_default.statSync(image).isFile()) {
26980
+ localImagePath = image;
26981
+ localPathMediaType = extensionMediaType;
26982
+ }
26983
+ } catch {
26984
+ }
26985
+ }
26986
+ }
26987
+ const mediaType = localPathMediaType ?? inferImageMediaType(image);
26988
+ if (!mediaType) {
26989
+ unrecognizedImages.push(image);
26990
+ continue;
26991
+ }
26992
+ const processedImage = localImagePath ? new Attachment({
26993
+ data: localImagePath,
26994
+ filename: localImagePath.split(/[\\/]/).at(-1) ?? "image",
26995
+ contentType: mediaType
26996
+ }) : processInputAttachments({
26997
+ type: "image",
26998
+ image,
26999
+ mediaType
27000
+ }).image;
27001
+ imageParts.push({
27002
+ type: "image_url",
27003
+ image_url: { url: processedImage }
27004
+ });
27005
+ }
27006
+ return {
27007
+ content: imageParts.length > 0 ? [...content ? [{ type: "text", text: content }] : [], ...imageParts] : content,
27008
+ ...unrecognizedImages.length > 0 ? { unrecognizedImages } : {}
27009
+ };
27010
+ }
27011
+ function normalizeMessage(message, toolCallId, syntheticToolCallIdOffset = 0) {
27012
+ if (typeof message.role !== "string") {
27013
+ return void 0;
27014
+ }
27015
+ const toolCalls = normalizeToolCalls(
27016
+ message.tool_calls,
27017
+ syntheticToolCallIdOffset
27018
+ );
27019
+ if (message.role === "tool") {
27020
+ const toolName = typeof message.tool_name === "string" && message.tool_name.length > 0 ? message.tool_name : "tool";
27021
+ return {
27022
+ role: "tool",
27023
+ tool_call_id: toolCallId ?? syntheticToolCallId(toolName, 0),
27024
+ content: typeof message.content === "string" ? message.content : ""
27025
+ };
27026
+ }
27027
+ const normalizedContent = normalizeTextAndImages(
27028
+ typeof message.content === "string" ? message.content || (toolCalls.length > 0 ? null : "") : toolCalls.length > 0 ? null : "",
27029
+ message.images
27030
+ );
27031
+ return {
27032
+ role: message.role,
27033
+ content: normalizedContent.content,
27034
+ ...typeof message.thinking === "string" && message.thinking.length > 0 ? { reasoning: message.thinking } : {},
27035
+ ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {},
27036
+ ...normalizedContent.unrecognizedImages ? { images: normalizedContent.unrecognizedImages } : {}
27037
+ };
27038
+ }
27039
+ function normalizeMessages(messages) {
27040
+ if (!Array.isArray(messages)) {
27041
+ return [];
27042
+ }
27043
+ const pendingToolCallIds = /* @__PURE__ */ new Map();
27044
+ let syntheticToolCallIdOffset = 0;
27045
+ return messages.flatMap((message) => {
27046
+ if (!isObject(message)) {
27047
+ return [];
27048
+ }
27049
+ const ollamaMessage = message;
27050
+ let toolCallId;
27051
+ if (ollamaMessage.role === "tool") {
27052
+ const toolName = ollamaMessage.tool_name;
27053
+ if (typeof toolName === "string") {
27054
+ toolCallId = pendingToolCallIds.get(toolName)?.shift();
27055
+ }
27056
+ }
27057
+ const normalized = normalizeMessage(
27058
+ ollamaMessage,
27059
+ toolCallId,
27060
+ syntheticToolCallIdOffset
27061
+ );
27062
+ if (Array.isArray(ollamaMessage.tool_calls)) {
27063
+ syntheticToolCallIdOffset += ollamaMessage.tool_calls.length;
27064
+ }
27065
+ if (ollamaMessage.role === "assistant" && normalized) {
27066
+ const toolCalls = Array.isArray(normalized.tool_calls) ? normalized.tool_calls : [];
27067
+ for (const toolCall of toolCalls) {
27068
+ if (!isObject(toolCall) || !isObject(toolCall.function)) {
27069
+ continue;
27070
+ }
27071
+ const name = toolCall.function.name;
27072
+ const id = toolCall.id;
27073
+ if (typeof name === "string" && typeof id === "string") {
27074
+ const ids = pendingToolCallIds.get(name) ?? [];
27075
+ ids.push(id);
27076
+ pendingToolCallIds.set(name, ids);
27077
+ }
27078
+ }
27079
+ }
27080
+ return normalized ? [normalized] : [];
27081
+ });
27082
+ }
27083
+ function normalizeToolDefinition(tool) {
27084
+ const fn = tool.function;
27085
+ if (!fn || typeof fn.name !== "string" || fn.name.length === 0) {
27086
+ return void 0;
27087
+ }
27088
+ return {
27089
+ type: "function",
27090
+ function: {
27091
+ name: fn.name,
27092
+ ...typeof fn.description === "string" ? { description: fn.description } : {},
27093
+ ...isObject(fn.parameters) ? { parameters: fn.parameters } : {}
27094
+ }
27095
+ };
27096
+ }
27097
+ function extractToolsMetadata(tools) {
27098
+ if (!Array.isArray(tools)) {
27099
+ return {};
27100
+ }
27101
+ const normalized = tools.flatMap((tool) => {
27102
+ if (!isObject(tool)) {
27103
+ return [];
27104
+ }
27105
+ const definition = normalizeToolDefinition(tool);
27106
+ return definition ? [definition] : [];
27107
+ });
27108
+ return normalized.length > 0 ? { tools: normalized } : {};
27109
+ }
27110
+ function extractOptionsMetadata(options) {
27111
+ if (!isObject(options)) {
27112
+ return {};
27113
+ }
27114
+ const metadata = {};
27115
+ const mappings = [
27116
+ ["temperature", "temperature"],
27117
+ ["top_p", "top_p"],
27118
+ ["num_predict", "max_tokens"],
27119
+ ["frequency_penalty", "frequency_penalty"],
27120
+ ["presence_penalty", "presence_penalty"],
27121
+ ["stop", "stop"]
27122
+ ];
27123
+ for (const [source, target] of mappings) {
27124
+ if (options[source] !== void 0) {
27125
+ metadata[target] = options[source];
27126
+ }
27127
+ }
27128
+ return metadata;
27129
+ }
27130
+ function extractRequestMetadata(request) {
27131
+ return {
27132
+ provider: "ollama",
27133
+ ...typeof request?.model === "string" ? { model: request.model } : {},
27134
+ ...extractOptionsMetadata(request?.options),
27135
+ ...request?.format !== void 0 ? { response_format: request.format } : {}
27136
+ };
27137
+ }
27138
+ function extractOllamaChatInput([request]) {
27139
+ return {
27140
+ input: normalizeMessages(request.messages),
27141
+ metadata: {
27142
+ ...extractRequestMetadata(request),
27143
+ ...extractToolsMetadata(request.tools)
27144
+ }
27145
+ };
27146
+ }
27147
+ function extractOllamaGenerateInput([request]) {
27148
+ const normalizedPrompt = normalizeTextAndImages(
27149
+ typeof request.prompt === "string" ? request.prompt : "",
27150
+ Array.isArray(request.images) ? request.images : void 0
27151
+ );
27152
+ const input = [
27153
+ ...typeof request.system === "string" ? [{ role: "system", content: request.system }] : [],
27154
+ {
27155
+ role: "user",
27156
+ content: normalizedPrompt.content,
27157
+ ...typeof request.suffix === "string" ? { suffix: request.suffix } : {},
27158
+ ...normalizedPrompt.unrecognizedImages ? { images: normalizedPrompt.unrecognizedImages } : {}
27159
+ }
27160
+ ];
27161
+ return {
27162
+ input,
27163
+ metadata: extractRequestMetadata(request)
27164
+ };
27165
+ }
27166
+ function extractOllamaEmbedInput([request]) {
27167
+ return {
27168
+ input: request.input,
27169
+ metadata: extractRequestMetadata(request)
27170
+ };
27171
+ }
27172
+ function extractOllamaChatOutput(result, syntheticToolCallIdOffset = 0) {
27173
+ if (!isObject(result) || !isObject(result.message)) {
27174
+ return void 0;
27175
+ }
27176
+ const message = normalizeMessage(
27177
+ result.message,
27178
+ void 0,
27179
+ syntheticToolCallIdOffset
27180
+ );
27181
+ if (!message) {
27182
+ return void 0;
27183
+ }
27184
+ const toolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
27185
+ return [
27186
+ {
27187
+ index: 0,
27188
+ finish_reason: normalizeFinishReason(result, toolCalls.length > 0),
27189
+ message
27190
+ }
27191
+ ];
27192
+ }
27193
+ function extractOllamaGenerateOutput(result) {
27194
+ if (!isObject(result) || typeof result.response !== "string") {
27195
+ return void 0;
27196
+ }
27197
+ return [
27198
+ {
27199
+ index: 0,
27200
+ finish_reason: normalizeFinishReason(result),
27201
+ message: {
27202
+ role: "assistant",
27203
+ content: result.response,
27204
+ ...typeof result.thinking === "string" && result.thinking.length > 0 ? { reasoning: result.thinking } : {}
27205
+ }
27206
+ }
27207
+ ];
27208
+ }
27209
+ function extractOllamaEmbedOutput(result) {
27210
+ const embedding = Array.isArray(result?.embeddings) ? result.embeddings[0] : void 0;
27211
+ return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
27212
+ }
27213
+ function extractOllamaResponseMetadata(result) {
27214
+ return typeof result?.model === "string" ? { model: result.model } : void 0;
27215
+ }
27216
+ function extractOllamaMetrics(result) {
27217
+ const metrics = {};
27218
+ const promptTokens = result?.prompt_eval_count;
27219
+ const completionTokens = result?.eval_count;
27220
+ if (isNonNegativeNumber(promptTokens)) {
27221
+ metrics.prompt_tokens = promptTokens;
27222
+ }
27223
+ if (isNonNegativeNumber(completionTokens)) {
27224
+ metrics.completion_tokens = completionTokens;
27225
+ }
27226
+ if (isNonNegativeNumber(promptTokens) || isNonNegativeNumber(completionTokens)) {
27227
+ metrics.tokens = (isNonNegativeNumber(promptTokens) ? promptTokens : 0) + (isNonNegativeNumber(completionTokens) ? completionTokens : 0);
27228
+ }
27229
+ return metrics;
27230
+ }
27231
+ function aggregateOllamaChatChunks(chunks, _result, event, _startTime) {
27232
+ const last = chunks.at(-1);
27233
+ const message = {
27234
+ role: "assistant",
27235
+ content: chunks.map((chunk) => chunk.message?.content ?? "").join(""),
27236
+ thinking: chunks.map((chunk) => chunk.message?.thinking ?? "").join(""),
27237
+ tool_calls: chunks.flatMap((chunk) => chunk.message?.tool_calls ?? [])
27238
+ };
27239
+ const response = {
27240
+ ...last,
27241
+ message
27242
+ };
27243
+ return {
27244
+ output: extractOllamaChatOutput(
27245
+ response,
27246
+ countOllamaToolCalls(event?.arguments?.[0]?.messages)
27247
+ ),
27248
+ metrics: extractOllamaMetrics(last ?? {}),
27249
+ metadata: extractOllamaResponseMetadata(last ?? {})
27250
+ };
27251
+ }
27252
+ function aggregateOllamaGenerateChunks(chunks, _result, _event, _startTime) {
27253
+ const last = chunks.at(-1);
27254
+ const response = {
27255
+ ...last,
27256
+ response: chunks.map((chunk) => chunk.response ?? "").join(""),
27257
+ thinking: chunks.map((chunk) => chunk.thinking ?? "").join("")
27258
+ };
27259
+ return {
27260
+ output: extractOllamaGenerateOutput(response),
27261
+ metrics: extractOllamaMetrics(last ?? {}),
27262
+ metadata: extractOllamaResponseMetadata(last ?? {})
27263
+ };
27264
+ }
27265
+
25851
27266
  // src/instrumentation/plugins/google-adk-channels.ts
25852
27267
  var googleADKChannels = defineChannels(
25853
27268
  "@google/adk",
@@ -29601,7 +31016,7 @@ var FlueObserveBridge = class {
29601
31016
  return;
29602
31017
  }
29603
31018
  const state = this.operationsById.get(event.operationId) ?? this.startSyntheticOperation(event);
29604
- const output = operationOutput(event);
31019
+ const output = operationOutput(event) ?? outputFromAgentTurn(state.latestAgentOutput);
29605
31020
  const metadata = {
29606
31021
  ...state.metadata,
29607
31022
  ...extractEventMetadata(event),
@@ -29610,7 +31025,7 @@ var FlueObserveBridge = class {
29610
31025
  };
29611
31026
  this.finishPendingChildrenForOperation(event, output);
29612
31027
  safeLog3(state.span, {
29613
- ...event.isError ? { error: toLoggedError(event.error) } : {},
31028
+ ...event.isError ? { error: toLoggedError(event.errorInfo ?? event.error) } : {},
29614
31029
  metadata,
29615
31030
  metrics: durationMetrics2(event.durationMs),
29616
31031
  output
@@ -29641,7 +31056,7 @@ var FlueObserveBridge = class {
29641
31056
  };
29642
31057
  const parent = this.parentSpanForTurn(event);
29643
31058
  const span = startFlueSpan(parent, {
29644
- name: `llm:${model ?? event.purpose ?? "unknown"}`,
31059
+ name: "flue.turn",
29645
31060
  spanAttributes: { type: "llm" /* LLM */ },
29646
31061
  startTime: eventTime(event.timestamp),
29647
31062
  event: {
@@ -29685,6 +31100,12 @@ var FlueObserveBridge = class {
29685
31100
  },
29686
31101
  output
29687
31102
  });
31103
+ if (event.purpose === "agent" && event.operationId) {
31104
+ const operation = this.operationsById.get(event.operationId);
31105
+ if (operation) {
31106
+ operation.latestAgentOutput = output;
31107
+ }
31108
+ }
29688
31109
  safeEnd(state.span, eventTime(event.timestamp));
29689
31110
  this.turnsByKey.delete(key);
29690
31111
  }
@@ -29764,7 +31185,7 @@ var FlueObserveBridge = class {
29764
31185
  return;
29765
31186
  }
29766
31187
  safeLog3(state.span, {
29767
- ...event.isError ? { error: toLoggedError(event.result) } : {},
31188
+ ...event.isError ? { error: toLoggedError(event.errorInfo ?? event.result) } : {},
29768
31189
  metadata: {
29769
31190
  ...state.metadata,
29770
31191
  ...extractEventMetadata(event),
@@ -29810,6 +31231,7 @@ var FlueObserveBridge = class {
29810
31231
  ...event.usage ? { "flue.usage": event.usage } : {}
29811
31232
  };
29812
31233
  safeLog3(state.span, {
31234
+ ...event.isError ? { error: toLoggedError(event.errorInfo ?? event.error) } : {},
29813
31235
  metadata,
29814
31236
  metrics: {
29815
31237
  ...durationMetrics2(event.durationMs),
@@ -29914,7 +31336,7 @@ var FlueObserveBridge = class {
29914
31336
  ...event.purpose ? { "flue.turn_purpose": event.purpose } : {}
29915
31337
  };
29916
31338
  const span = startFlueSpan(this.parentSpanForEvent(event), {
29917
- name: `llm:${model ?? event.purpose ?? "unknown"}`,
31339
+ name: "flue.turn",
29918
31340
  spanAttributes: { type: "llm" /* LLM */ },
29919
31341
  startTime: eventTime(event.timestamp),
29920
31342
  event: { metadata }
@@ -30046,7 +31468,10 @@ function extractEventMetadata(event, ctx) {
30046
31468
  return {
30047
31469
  ...event.runId ? { "flue.run_id": event.runId } : {},
30048
31470
  ...event.instanceId ? { "flue.instance_id": event.instanceId } : {},
31471
+ ...event.submissionId ? { "flue.submission_id": event.submissionId } : {},
30049
31472
  ...event.dispatchId ? { "flue.dispatch_id": event.dispatchId } : {},
31473
+ ...event.agentName ? { "flue.agent_name": event.agentName } : {},
31474
+ ...event.conversationId ? { "flue.conversation_id": event.conversationId } : {},
30050
31475
  ...typeof event.eventIndex === "number" ? { "flue.event_index": event.eventIndex } : {},
30051
31476
  ...event.session ? { "flue.session": event.session } : {},
30052
31477
  ...event.parentSession ? { "flue.parent_session": event.parentSession } : {},
@@ -30075,7 +31500,7 @@ function flueTurnRequestInput(event) {
30075
31500
  return event.request?.input ?? event.input;
30076
31501
  }
30077
31502
  function flueTurnRequestModel(event) {
30078
- return event.request?.model ?? event.model;
31503
+ return event.request?.requestedModel ?? event.request?.model ?? event.model;
30079
31504
  }
30080
31505
  function flueTurnRequestProvider(event) {
30081
31506
  return event.request?.providerName ?? event.provider ?? event.request?.providerId;
@@ -30084,10 +31509,10 @@ function flueTurnRequestApi(event) {
30084
31509
  return event.request?.api ?? event.api;
30085
31510
  }
30086
31511
  function flueTurnRequestReasoning(event) {
30087
- return event.request?.reasoning ?? event.reasoning;
31512
+ return event.request?.reasoningLevel ?? event.request?.reasoning ?? event.reasoning;
30088
31513
  }
30089
31514
  function flueTurnModel(event) {
30090
- return event.request?.model ?? event.model;
31515
+ return event.response?.responseModel ?? event.request?.requestedModel ?? event.request?.model ?? event.model;
30091
31516
  }
30092
31517
  function flueTurnProvider(event) {
30093
31518
  return event.request?.providerName ?? event.provider ?? event.request?.providerId;
@@ -30102,7 +31527,7 @@ function flueTurnOutput(event) {
30102
31527
  return event.response?.output ?? event.output;
30103
31528
  }
30104
31529
  function flueTurnStopReason(event) {
30105
- return event.response?.stopReason ?? event.stopReason;
31530
+ return event.response?.finishReason ?? event.response?.stopReason ?? event.stopReason;
30106
31531
  }
30107
31532
  function flueTurnError(event) {
30108
31533
  return event.response?.error ?? event.response?.errorInfo?.message ?? event.error;
@@ -30117,6 +31542,9 @@ function flueToolInput(event) {
30117
31542
  return event.input;
30118
31543
  }
30119
31544
  function flueToolOutput(event) {
31545
+ if (Object.hasOwn(event, "effectiveResult")) {
31546
+ return event.effectiveResult;
31547
+ }
30120
31548
  return event.output !== void 0 ? event.output : event.result;
30121
31549
  }
30122
31550
  function flueToolError(event) {
@@ -30124,10 +31552,36 @@ function flueToolError(event) {
30124
31552
  }
30125
31553
  function operationOutput(event) {
30126
31554
  if (event.operationKind === "prompt" || event.operationKind === "skill") {
30127
- return llmResultFromOperationResult(event.result);
31555
+ return outputFromAgentInvocation(event.agentOutput) ?? llmResultFromOperationResult(event.result);
30128
31556
  }
30129
31557
  return event.result ?? (event.operationKind === "compact" ? { completed: true } : void 0);
30130
31558
  }
31559
+ function outputFromAgentInvocation(output) {
31560
+ if (output?.type === "text") {
31561
+ return output.text;
31562
+ }
31563
+ if (output?.type === "data") {
31564
+ return output.data;
31565
+ }
31566
+ return void 0;
31567
+ }
31568
+ function outputFromAgentTurn(output) {
31569
+ if (!isObjectLike(output)) {
31570
+ return output;
31571
+ }
31572
+ const content = Reflect.get(output, "content");
31573
+ if (!Array.isArray(content)) {
31574
+ return output;
31575
+ }
31576
+ const text = content.flatMap((part) => {
31577
+ if (!isObjectLike(part) || Reflect.get(part, "type") !== "text") {
31578
+ return [];
31579
+ }
31580
+ const value = Reflect.get(part, "text");
31581
+ return typeof value === "string" ? [value] : [];
31582
+ });
31583
+ return text.length > 0 ? text.join("") : output;
31584
+ }
30131
31585
  function llmResultFromOperationResult(result) {
30132
31586
  if (!isObjectLike(result)) {
30133
31587
  return result;
@@ -30226,7 +31680,7 @@ function safeEnd(span, endTime) {
30226
31680
  }
30227
31681
  }
30228
31682
  function logInstrumentationError3(label, error) {
30229
- console.error(`Error in ${label} instrumentation:`, error);
31683
+ debugLogger.debug(`Error in ${label} instrumentation:`, error);
30230
31684
  }
30231
31685
 
30232
31686
  // src/wrappers/langchain/callback-handler.ts
@@ -31155,12 +32609,13 @@ var piCodingAgentChannels = defineChannels(
31155
32609
  );
31156
32610
 
31157
32611
  // src/instrumentation/plugins/pi-coding-agent-plugin.ts
31158
- var piStreamPatchStates = /* @__PURE__ */ new WeakMap();
32612
+ var piAgentPatchStates = /* @__PURE__ */ new WeakMap();
32613
+ var piAgentEventSubscriptions = /* @__PURE__ */ new WeakSet();
31159
32614
  var piPromptContextStore;
31160
32615
  var PiCodingAgentPlugin = class extends BasePlugin {
31161
32616
  activePromptStates = /* @__PURE__ */ new Set();
31162
32617
  onEnable() {
31163
- this.subscribeToPrompt();
32618
+ this.interceptPrompt();
31164
32619
  }
31165
32620
  onDisable() {
31166
32621
  for (const unsubscribe of this.unsubscribers) {
@@ -31168,65 +32623,74 @@ var PiCodingAgentPlugin = class extends BasePlugin {
31168
32623
  }
31169
32624
  this.unsubscribers = [];
31170
32625
  for (const state of [...this.activePromptStates]) {
31171
- void finalizePiPromptRun(state).catch((error) => {
31172
- logInstrumentationError4("Pi Coding Agent disable cleanup", error);
31173
- });
32626
+ finishPiPromptRun(state);
31174
32627
  }
31175
32628
  }
31176
- subscribeToPrompt() {
31177
- const channel2 = piCodingAgentChannels.prompt.tracingChannel();
31178
- const states = /* @__PURE__ */ new WeakMap();
31179
- const unbindAutoInstrumentationSuppression = bindAutoInstrumentationSuppressionToStart(channel2);
31180
- const handlers = {
31181
- start: (event) => {
31182
- const state = startPiPromptRun(event, (state2) => {
31183
- this.activePromptStates.delete(state2);
31184
- });
31185
- if (state) {
31186
- this.activePromptStates.add(state);
31187
- states.set(event, state);
31188
- }
31189
- },
31190
- asyncEnd: async (event) => {
31191
- const state = states.get(event);
31192
- if (!state) {
31193
- return;
31194
- }
31195
- states.delete(event);
31196
- state.promptCallEnded = true;
31197
- if (!state.finalized && state.deferCompletionUntilTurnEnd && !state.turnEnded) {
31198
- if (!state.sawStreamFn) {
31199
- state.queued = true;
31200
- if (!state.streamPatchState.queuedPromptStates.includes(state)) {
31201
- state.streamPatchState.queuedPromptStates.push(state);
31202
- }
32629
+ interceptPrompt() {
32630
+ this.unsubscribers.push(
32631
+ piCodingAgentChannels.prompt.intercept(
32632
+ (target, thisArg, args, additional) => {
32633
+ const invokeTarget = () => Reflect.apply(target, thisArg, args);
32634
+ let state;
32635
+ try {
32636
+ state = startPiPromptRun(
32637
+ {
32638
+ ...additional,
32639
+ arguments: args,
32640
+ self: thisArg
32641
+ },
32642
+ (finalizedState) => {
32643
+ this.activePromptStates.delete(finalizedState);
32644
+ }
32645
+ );
32646
+ } catch (error) {
32647
+ logInstrumentationError4("Pi Coding Agent prompt start", error);
31203
32648
  }
31204
- return;
31205
- }
31206
- await finalizePiPromptRun(state);
31207
- },
31208
- error: async (event) => {
31209
- const state = states.get(event);
31210
- if (!state) {
31211
- return;
32649
+ if (!state) {
32650
+ return runWithAutoInstrumentationSuppressed(invokeTarget);
32651
+ }
32652
+ this.activePromptStates.add(state);
32653
+ return promptContextStore().run(
32654
+ state,
32655
+ () => runWithAutoInstrumentationSuppressed(() => {
32656
+ let result;
32657
+ try {
32658
+ result = invokeTarget();
32659
+ } catch (error) {
32660
+ finishPiPromptRun(state, error);
32661
+ throw error;
32662
+ }
32663
+ return Promise.resolve(result).then(
32664
+ (value) => {
32665
+ finishPiPromptCall(state);
32666
+ return value;
32667
+ },
32668
+ (error) => {
32669
+ finishPiPromptRun(state, error);
32670
+ throw error;
32671
+ }
32672
+ );
32673
+ })
32674
+ );
31212
32675
  }
31213
- states.delete(event);
31214
- await finalizePiPromptRun(state, event.error);
31215
- }
31216
- };
31217
- channel2.subscribe(handlers);
31218
- this.unsubscribers.push(() => {
31219
- unbindAutoInstrumentationSuppression?.();
31220
- channel2.unsubscribe(handlers);
31221
- });
32676
+ )
32677
+ );
31222
32678
  }
31223
32679
  };
32680
+ function finishPiPromptCall(state) {
32681
+ state.promptCallEnded = true;
32682
+ if (state.deferCompletionUntilTurnEnd && !state.turnEnded) {
32683
+ return;
32684
+ }
32685
+ finishPiPromptRun(state);
32686
+ }
31224
32687
  function startPiPromptRun(event, onFinalize) {
31225
32688
  const session = extractSession(event);
31226
32689
  const agent = session?.agent;
31227
32690
  if (!session || !isPiAgent(agent)) {
31228
32691
  return void 0;
31229
32692
  }
32693
+ installPiAgentInstrumentation(agent);
31230
32694
  const metadata = {
31231
32695
  ...extractSessionMetadata(session),
31232
32696
  ...extractPromptOptionsMetadata(event.arguments[1]),
@@ -31248,9 +32712,7 @@ function startPiPromptRun(event, onFinalize) {
31248
32712
  INSTRUMENTATION_NAMES.PI_CODING_AGENT
31249
32713
  )
31250
32714
  );
31251
- const streamPatchState = installPiStreamPatch(agent);
31252
32715
  const options = event.arguments[1];
31253
- const promptText = event.arguments[0];
31254
32716
  const state = {
31255
32717
  activeLlmSpans: /* @__PURE__ */ new Set(),
31256
32718
  activeToolSpans: /* @__PURE__ */ new Map(),
@@ -31262,29 +32724,10 @@ function startPiPromptRun(event, onFinalize) {
31262
32724
  metrics: {},
31263
32725
  onFinalize,
31264
32726
  promptCallEnded: false,
31265
- ...typeof promptText === "string" ? { promptText } : {},
31266
- queued: false,
31267
32727
  span,
31268
- sawStreamFn: false,
31269
32728
  startTime: getCurrentUnixTimestamp(),
31270
- streamPatchState,
31271
32729
  turnEnded: false
31272
32730
  };
31273
- state.restorePromptContext = enterPiPromptContext(state);
31274
- streamPatchState.activePromptStates.add(state);
31275
- try {
31276
- state.unsubscribeAgent = agent.subscribe(async (agentEvent) => {
31277
- try {
31278
- await runWithAutoInstrumentationSuppressed(
31279
- () => handlePiAgentEvent(state, agentEvent)
31280
- );
31281
- } catch (error) {
31282
- logInstrumentationError4("Pi Coding Agent event", error);
31283
- }
31284
- });
31285
- } catch (error) {
31286
- logInstrumentationError4("Pi Coding Agent event subscription", error);
31287
- }
31288
32731
  return state;
31289
32732
  }
31290
32733
  function extractSession(event) {
@@ -31298,111 +32741,55 @@ function promptContextStore() {
31298
32741
  piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
31299
32742
  return piPromptContextStore;
31300
32743
  }
31301
- function currentPromptContextFrames() {
31302
- return promptContextStore().getStore()?.frames ?? [];
31303
- }
31304
32744
  function currentPiPromptState() {
31305
- const frames = currentPromptContextFrames();
31306
- return frames[frames.length - 1]?.state;
31307
- }
31308
- function enterPiPromptContext(state) {
31309
- const frame = {
31310
- id: /* @__PURE__ */ Symbol("braintrust.pi-coding-agent.prompt"),
31311
- state
31312
- };
31313
- promptContextStore().enterWith({
31314
- frames: [...currentPromptContextFrames(), frame]
31315
- });
31316
- return () => {
31317
- const frames = currentPromptContextFrames().filter(
31318
- (candidate) => candidate.id !== frame.id
31319
- );
31320
- promptContextStore().enterWith(frames.length > 0 ? { frames } : void 0);
31321
- };
31322
- }
31323
- function installPiStreamPatch(agent) {
31324
- const existing = piStreamPatchStates.get(agent);
31325
- if (existing) {
31326
- if (agent.streamFn !== existing.wrappedStreamFn) {
31327
- debugLogger.debug(
31328
- "Pi Coding Agent streamFn changed while Braintrust instrumentation was active; preserving existing patch state."
31329
- );
31330
- }
31331
- return existing;
31332
- }
31333
- const patchState = {
31334
- activePromptStates: /* @__PURE__ */ new Set(),
31335
- agent,
31336
- originalStreamFn: agent.streamFn,
31337
- queuedPromptStates: [],
31338
- wrappedStreamFn: agent.streamFn
31339
- };
31340
- patchState.wrappedStreamFn = makeSharedInstrumentedStreamFn(patchState);
31341
- agent.streamFn = patchState.wrappedStreamFn;
31342
- piStreamPatchStates.set(agent, patchState);
31343
- return patchState;
31344
- }
31345
- function resolveStreamPromptState(patchState, context) {
31346
- let lastUserText;
31347
- if (Array.isArray(context.messages)) {
31348
- for (let i = context.messages.length - 1; i >= 0; i--) {
31349
- const message = context.messages[i];
31350
- if (isPiUserMessage(message)) {
31351
- if (typeof message.content === "string") {
31352
- lastUserText = message.content;
31353
- } else {
31354
- lastUserText = message.content.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
31355
- }
31356
- break;
31357
- }
31358
- }
31359
- }
31360
- if (lastUserText !== void 0) {
31361
- const queuedMatch = patchState.queuedPromptStates.find(
31362
- (state) => state.promptText === lastUserText
31363
- );
31364
- if (queuedMatch) {
31365
- return queuedMatch;
31366
- }
31367
- const matches = [...patchState.activePromptStates].filter(
31368
- (state) => state.promptText === lastUserText
32745
+ return promptContextStore().getStore();
32746
+ }
32747
+ function installPiAgentInstrumentation(agent) {
32748
+ const existing = piAgentPatchStates.get(agent);
32749
+ if (!existing || agent.streamFn !== existing.wrappedStreamFn) {
32750
+ const patchState = {
32751
+ originalStreamFn: agent.streamFn,
32752
+ wrappedStreamFn: agent.streamFn
32753
+ };
32754
+ patchState.wrappedStreamFn = makeInstrumentedStreamFn(
32755
+ agent,
32756
+ patchState.originalStreamFn
31369
32757
  );
31370
- if (matches.length === 1) {
31371
- return matches[0];
31372
- }
32758
+ agent.streamFn = patchState.wrappedStreamFn;
32759
+ piAgentPatchStates.set(agent, patchState);
31373
32760
  }
31374
- const contextState = currentPiPromptState();
31375
- if (contextState && patchState.activePromptStates.has(contextState) && (!contextState.queued || lastUserText !== void 0 && contextState.promptText === lastUserText)) {
31376
- return contextState;
32761
+ if (piAgentEventSubscriptions.has(agent)) {
32762
+ return;
31377
32763
  }
31378
- if (patchState.activePromptStates.size === 1) {
31379
- return [...patchState.activePromptStates][0];
32764
+ try {
32765
+ agent.subscribe(async (event) => {
32766
+ const state = currentPiPromptState();
32767
+ if (!state || state.agent !== agent || state.finalized) {
32768
+ return;
32769
+ }
32770
+ try {
32771
+ await runWithAutoInstrumentationSuppressed(
32772
+ () => handlePiAgentEvent(state, event)
32773
+ );
32774
+ } catch (error) {
32775
+ logInstrumentationError4("Pi Coding Agent event", error);
32776
+ }
32777
+ });
32778
+ piAgentEventSubscriptions.add(agent);
32779
+ } catch (error) {
32780
+ logInstrumentationError4("Pi Coding Agent event subscription", error);
31380
32781
  }
31381
- return void 0;
31382
32782
  }
31383
- function makeSharedInstrumentedStreamFn(patchState) {
32783
+ function makeInstrumentedStreamFn(agent, originalStreamFn) {
31384
32784
  return async function instrumentedPiStreamFn(model, context, options) {
31385
- const state = resolveStreamPromptState(patchState, context);
31386
- if (!state) {
31387
- const invokeOriginal = () => Reflect.apply(patchState.originalStreamFn, this, [
31388
- model,
31389
- context,
31390
- options
31391
- ]);
31392
- return patchState.activePromptStates.size > 0 ? runWithAutoInstrumentationSuppressed(invokeOriginal) : invokeOriginal();
32785
+ const invokeOriginal = () => Reflect.apply(originalStreamFn, this, [model, context, options]);
32786
+ const state = currentPiPromptState();
32787
+ if (!state || state.agent !== agent || state.finalized) {
32788
+ return invokeOriginal();
31393
32789
  }
31394
- state.sawStreamFn = true;
31395
- removeQueuedPromptState(state);
31396
- state.streamPatchState.eventPromptState = state;
31397
32790
  const llmState = await startPiLlmSpan(state, model, context, options);
31398
32791
  try {
31399
- const stream = await runWithAutoInstrumentationSuppressed(
31400
- () => Reflect.apply(patchState.originalStreamFn, this, [
31401
- model,
31402
- context,
31403
- options
31404
- ])
31405
- );
32792
+ const stream = await runWithAutoInstrumentationSuppressed(invokeOriginal);
31406
32793
  return patchAssistantMessageStream(stream, state, llmState);
31407
32794
  } catch (error) {
31408
32795
  finishPiLlmSpan(state, llmState, void 0, error);
@@ -31549,13 +32936,6 @@ async function handlePiAgentEvent(state, event) {
31549
32936
  if (state.finalized) {
31550
32937
  return;
31551
32938
  }
31552
- const eventPromptState = state.streamPatchState.eventPromptState;
31553
- if (eventPromptState && eventPromptState !== state) {
31554
- return;
31555
- }
31556
- if (!eventPromptState && (state.queued || state.streamPatchState.activePromptStates.size > 1 && currentPiPromptState() !== state)) {
31557
- return;
31558
- }
31559
32939
  switch (event.type) {
31560
32940
  case "message_end":
31561
32941
  if (isPiAssistantMessage(event.message)) {
@@ -31570,11 +32950,8 @@ async function handlePiAgentEvent(state, event) {
31570
32950
  addMetrics(state.metrics, extractUsageMetrics2(event.message.usage));
31571
32951
  }
31572
32952
  }
31573
- if (state.streamPatchState.eventPromptState === state) {
31574
- state.streamPatchState.eventPromptState = void 0;
31575
- }
31576
32953
  if (state.promptCallEnded && state.deferCompletionUntilTurnEnd) {
31577
- await finalizePiPromptRun(state);
32954
+ finishPiPromptRun(state);
31578
32955
  }
31579
32956
  return;
31580
32957
  case "tool_execution_start":
@@ -31647,19 +33024,13 @@ function finishPiToolSpan(state, event) {
31647
33024
  }
31648
33025
  }
31649
33026
  }
31650
- async function finalizePiPromptRun(state, error) {
33027
+ function finishPiPromptRun(state, error) {
31651
33028
  if (state.finalized) {
31652
33029
  return;
31653
33030
  }
31654
33031
  state.finalized = true;
31655
33032
  state.onFinalize?.(state);
31656
- restorePiStreamFn(state);
31657
- try {
31658
- state.unsubscribeAgent?.();
31659
- } catch (unsubscribeError) {
31660
- logInstrumentationError4("Pi Coding Agent unsubscribe", unsubscribeError);
31661
- }
31662
- await finishOpenLlmSpans(state, error);
33033
+ finishOpenLlmSpans(state, error);
31663
33034
  finishOpenToolSpans(state, error);
31664
33035
  const metadata = {
31665
33036
  ...state.metadata,
@@ -31676,34 +33047,14 @@ async function finalizePiPromptRun(state, error) {
31676
33047
  output: state.output
31677
33048
  });
31678
33049
  } finally {
31679
- state.span.end();
31680
- }
31681
- }
31682
- function restorePiStreamFn(state) {
31683
- const patchState = state.streamPatchState;
31684
- patchState.activePromptStates.delete(state);
31685
- removeQueuedPromptState(state);
31686
- if (patchState.eventPromptState === state) {
31687
- patchState.eventPromptState = void 0;
31688
- }
31689
- state.restorePromptContext?.();
31690
- if (patchState.activePromptStates.size > 0) {
31691
- return;
31692
- }
31693
- if (patchState.agent.streamFn === patchState.wrappedStreamFn) {
31694
- patchState.agent.streamFn = patchState.originalStreamFn;
31695
- }
31696
- piStreamPatchStates.delete(patchState.agent);
31697
- }
31698
- function removeQueuedPromptState(state) {
31699
- state.queued = false;
31700
- const queuedPromptStates = state.streamPatchState.queuedPromptStates;
31701
- const index = queuedPromptStates.indexOf(state);
31702
- if (index >= 0) {
31703
- queuedPromptStates.splice(index, 1);
33050
+ try {
33051
+ state.span.end();
33052
+ } catch (endError) {
33053
+ logInstrumentationError4("Pi Coding Agent prompt end", endError);
33054
+ }
31704
33055
  }
31705
33056
  }
31706
- async function finishOpenLlmSpans(state, error) {
33057
+ function finishOpenLlmSpans(state, error) {
31707
33058
  for (const llmState of [...state.activeLlmSpans]) {
31708
33059
  finishPiLlmSpan(state, llmState, void 0, error);
31709
33060
  }
@@ -31784,7 +33135,7 @@ function normalizeAssistantMessage(message) {
31784
33135
  (part) => part.type === "thinking" && !part.redacted ? [part.thinking] : []
31785
33136
  ).join("");
31786
33137
  const toolCalls = message.content.flatMap(
31787
- (part) => part.type === "toolCall" ? [normalizeToolCall(part)] : []
33138
+ (part) => part.type === "toolCall" ? [normalizeToolCall2(part)] : []
31788
33139
  );
31789
33140
  return {
31790
33141
  role: "assistant",
@@ -31819,13 +33170,13 @@ function normalizeUserContent(content) {
31819
33170
  return part;
31820
33171
  });
31821
33172
  }
31822
- function normalizeToolCall(toolCall) {
33173
+ function normalizeToolCall2(toolCall) {
31823
33174
  return {
31824
33175
  id: toolCall.id,
31825
33176
  type: "function",
31826
33177
  function: {
31827
33178
  name: toolCall.name,
31828
- arguments: stringifyArguments(toolCall.arguments)
33179
+ arguments: stringifyArguments2(toolCall.arguments)
31829
33180
  }
31830
33181
  };
31831
33182
  }
@@ -32008,7 +33359,7 @@ function cleanMetrics5(metrics) {
32008
33359
  }
32009
33360
  return cleaned;
32010
33361
  }
32011
- function stringifyArguments(value) {
33362
+ function stringifyArguments2(value) {
32012
33363
  if (typeof value === "string") {
32013
33364
  return value;
32014
33365
  }
@@ -33576,6 +34927,7 @@ var BraintrustPlugin = class extends BasePlugin {
33576
34927
  anthropicPlugin = null;
33577
34928
  aiSDKPlugin = null;
33578
34929
  claudeAgentSDKPlugin = null;
34930
+ cloudflareThinkPlugin = null;
33579
34931
  cursorSDKPlugin = null;
33580
34932
  openAIAgentsPlugin = null;
33581
34933
  googleGenAIPlugin = null;
@@ -33584,6 +34936,7 @@ var BraintrustPlugin = class extends BasePlugin {
33584
34936
  openRouterPlugin = null;
33585
34937
  openRouterAgentPlugin = null;
33586
34938
  mistralPlugin = null;
34939
+ ollamaPlugin = null;
33587
34940
  googleADKPlugin = null;
33588
34941
  coherePlugin = null;
33589
34942
  groqPlugin = null;
@@ -33623,6 +34976,10 @@ var BraintrustPlugin = class extends BasePlugin {
33623
34976
  this.claudeAgentSDKPlugin = new ClaudeAgentSDKPlugin();
33624
34977
  this.claudeAgentSDKPlugin.enable();
33625
34978
  }
34979
+ if (integrations.cloudflareThink !== false) {
34980
+ this.cloudflareThinkPlugin = new CloudflareThinkPlugin();
34981
+ this.cloudflareThinkPlugin.enable();
34982
+ }
33626
34983
  if (integrations.cursorSDK !== false && integrations.cursor !== false) {
33627
34984
  this.cursorSDKPlugin = new CursorSDKPlugin();
33628
34985
  this.cursorSDKPlugin.enable();
@@ -33653,6 +35010,10 @@ var BraintrustPlugin = class extends BasePlugin {
33653
35010
  this.mistralPlugin = new MistralPlugin();
33654
35011
  this.mistralPlugin.enable();
33655
35012
  }
35013
+ if (integrations.ollama !== false) {
35014
+ this.ollamaPlugin = new OllamaPlugin();
35015
+ this.ollamaPlugin.enable();
35016
+ }
33656
35017
  if (integrations.googleADK !== false) {
33657
35018
  this.googleADKPlugin = new GoogleADKPlugin();
33658
35019
  this.googleADKPlugin.enable();
@@ -33729,6 +35090,10 @@ var BraintrustPlugin = class extends BasePlugin {
33729
35090
  this.claudeAgentSDKPlugin.disable();
33730
35091
  this.claudeAgentSDKPlugin = null;
33731
35092
  }
35093
+ if (this.cloudflareThinkPlugin) {
35094
+ this.cloudflareThinkPlugin.disable();
35095
+ this.cloudflareThinkPlugin = null;
35096
+ }
33732
35097
  if (this.cursorSDKPlugin) {
33733
35098
  this.cursorSDKPlugin.disable();
33734
35099
  this.cursorSDKPlugin = null;
@@ -33761,6 +35126,10 @@ var BraintrustPlugin = class extends BasePlugin {
33761
35126
  this.mistralPlugin.disable();
33762
35127
  this.mistralPlugin = null;
33763
35128
  }
35129
+ if (this.ollamaPlugin) {
35130
+ this.ollamaPlugin.disable();
35131
+ this.ollamaPlugin = null;
35132
+ }
33764
35133
  if (this.googleADKPlugin) {
33765
35134
  this.googleADKPlugin.disable();
33766
35135
  this.googleADKPlugin = null;
@@ -33846,6 +35215,7 @@ var envIntegrationAliases = {
33846
35215
  cloudflareaichat: "cloudflareAIChat",
33847
35216
  "cloudflare-ai-chat": "cloudflareAIChat",
33848
35217
  "@cloudflare/ai-chat": "cloudflareAIChat",
35218
+ cloudflarethink: "cloudflareThink",
33849
35219
  cursor: "cursor",
33850
35220
  "cursor-sdk": "cursorSDK",
33851
35221
  cursorsdk: "cursorSDK",
@@ -33866,6 +35236,7 @@ var envIntegrationAliases = {
33866
35236
  openrouteragent: "openrouterAgent",
33867
35237
  "openrouter-agent": "openrouterAgent",
33868
35238
  mistral: "mistral",
35239
+ ollama: "ollama",
33869
35240
  googleadk: "googleADK",
33870
35241
  "google-adk": "googleADK",
33871
35242
  cohere: "cohere",
@@ -33901,6 +35272,7 @@ function getDefaultInstrumentationIntegrations() {
33901
35272
  huggingface: true,
33902
35273
  claudeAgentSDK: true,
33903
35274
  cloudflareAIChat: true,
35275
+ cloudflareThink: true,
33904
35276
  cursor: true,
33905
35277
  cursorSDK: true,
33906
35278
  flue: true,
@@ -33909,6 +35281,7 @@ function getDefaultInstrumentationIntegrations() {
33909
35281
  openrouter: true,
33910
35282
  openrouterAgent: true,
33911
35283
  mistral: true,
35284
+ ollama: true,
33912
35285
  cohere: true,
33913
35286
  groq: true,
33914
35287
  bedrock: true,