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
@@ -7,6 +7,7 @@ import {
7
7
  claudeAgentSDKChannels,
8
8
  cloudflareAIChatChannels,
9
9
  cloudflareAgentsChannels,
10
+ cloudflareThinkChannels,
10
11
  cohereChannels,
11
12
  cursorSDKChannels,
12
13
  detectSpanOriginEnvironment,
@@ -31,6 +32,7 @@ import {
31
32
  langSmithChannels,
32
33
  mergeSpanOriginContext,
33
34
  mistralChannels,
35
+ ollamaChannels,
34
36
  openAIAgentsCoreChannels,
35
37
  openAIChannels,
36
38
  openAICodexChannels,
@@ -44,7 +46,7 @@ import {
44
46
  smithyCoreChannels,
45
47
  strandsAgentSDKChannels,
46
48
  withSpanInstrumentationName
47
- } from "./chunk-PN7EWK66.mjs";
49
+ } from "./chunk-CZM5JIQL.mjs";
48
50
 
49
51
  // src/id-gen.ts
50
52
  import { v4 as uuidv4 } from "uuid";
@@ -10883,6 +10885,25 @@ function getCachedMetricFromHeaders(headers) {
10883
10885
  return parseCachedHeader(headers.get(LEGACY_CACHED_HEADER));
10884
10886
  }
10885
10887
 
10888
+ // src/wrappers/anthropic-session-collector.ts
10889
+ var sessionStreamCollectors = /* @__PURE__ */ new WeakMap();
10890
+ function registerAnthropicSessionStreamCollector(stream, collector) {
10891
+ sessionStreamCollectors.set(stream, collector);
10892
+ }
10893
+ function collectAnthropicSession(stream) {
10894
+ const collector = sessionStreamCollectors.get(stream);
10895
+ if (!collector) {
10896
+ return stream;
10897
+ }
10898
+ sessionStreamCollectors.delete(stream);
10899
+ try {
10900
+ collector();
10901
+ } catch (error) {
10902
+ debugLogger.error("Failed to collect Anthropic Sessions stream:", error);
10903
+ }
10904
+ return stream;
10905
+ }
10906
+
10886
10907
  // src/instrumentation/core/stream-patcher.ts
10887
10908
  function isAsyncIterable(value) {
10888
10909
  return value !== null && typeof value === "object" && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
@@ -10907,6 +10928,34 @@ function patchStreamIfNeeded(stream, options) {
10907
10928
  );
10908
10929
  return stream;
10909
10930
  }
10931
+ const chunks = [];
10932
+ let completed = false;
10933
+ const notifyCancellation = async () => {
10934
+ try {
10935
+ await (options.onCancel ?? options.onComplete)(chunks);
10936
+ } catch (error) {
10937
+ debugLogger.error("Error in stream cancellation handler:", error);
10938
+ }
10939
+ };
10940
+ const patchAbortIfPresent = () => {
10941
+ try {
10942
+ if ("abort" in stream && typeof stream.abort === "function") {
10943
+ const originalAbort = stream.abort.bind(stream);
10944
+ stream.abort = (...args) => {
10945
+ try {
10946
+ return originalAbort(...args);
10947
+ } finally {
10948
+ if (!completed) {
10949
+ completed = true;
10950
+ void notifyCancellation();
10951
+ }
10952
+ }
10953
+ };
10954
+ }
10955
+ } catch (error) {
10956
+ debugLogger.warn("Failed to patch stream abort method:", error);
10957
+ }
10958
+ };
10910
10959
  if (hasAsyncIteratorMethods(stream) && isSelfAsyncIterator(stream)) {
10911
10960
  if ("__braintrust_patched_iterator_methods" in stream) {
10912
10961
  return stream;
@@ -10915,8 +10964,6 @@ function patchStreamIfNeeded(stream, options) {
10915
10964
  const originalNext = stream.next.bind(stream);
10916
10965
  const originalReturn = typeof stream.return === "function" ? stream.return.bind(stream) : null;
10917
10966
  const originalThrow = typeof stream.throw === "function" ? stream.throw.bind(stream) : null;
10918
- const chunks = [];
10919
- let completed = false;
10920
10967
  stream.next = async (...args) => {
10921
10968
  try {
10922
10969
  const result = await runNextWithWrapper(
@@ -10968,11 +11015,7 @@ function patchStreamIfNeeded(stream, options) {
10968
11015
  stream.return = async (...args) => {
10969
11016
  if (!completed) {
10970
11017
  completed = true;
10971
- try {
10972
- await options.onComplete(chunks);
10973
- } catch (error) {
10974
- console.error("Error in stream onComplete handler:", error);
10975
- }
11018
+ await notifyCancellation();
10976
11019
  }
10977
11020
  return originalReturn(...args);
10978
11021
  };
@@ -10997,6 +11040,7 @@ function patchStreamIfNeeded(stream, options) {
10997
11040
  Object.defineProperty(stream, "__braintrust_patched_iterator_methods", {
10998
11041
  value: true
10999
11042
  });
11043
+ patchAbortIfPresent();
11000
11044
  return stream;
11001
11045
  } catch (error) {
11002
11046
  console.warn("Failed to patch stream iterator methods:", error);
@@ -11010,8 +11054,6 @@ function patchStreamIfNeeded(stream, options) {
11010
11054
  const patchedIteratorFn = function() {
11011
11055
  const iterator = originalIteratorFn.call(this);
11012
11056
  const originalNext = iterator.next.bind(iterator);
11013
- const chunks = [];
11014
- let completed = false;
11015
11057
  iterator.next = async function(...args) {
11016
11058
  try {
11017
11059
  const result = await runNextWithWrapper(
@@ -11064,11 +11106,7 @@ function patchStreamIfNeeded(stream, options) {
11064
11106
  iterator.return = async function(...args) {
11065
11107
  if (!completed) {
11066
11108
  completed = true;
11067
- try {
11068
- await options.onComplete(chunks);
11069
- } catch (error) {
11070
- console.error("Error in stream onComplete handler:", error);
11071
- }
11109
+ await notifyCancellation();
11072
11110
  }
11073
11111
  return originalReturn(...args);
11074
11112
  };
@@ -11097,6 +11135,7 @@ function patchStreamIfNeeded(stream, options) {
11097
11135
  value: true
11098
11136
  });
11099
11137
  stream[Symbol.asyncIterator] = patchedIteratorFn;
11138
+ patchAbortIfPresent();
11100
11139
  return stream;
11101
11140
  } catch (error) {
11102
11141
  console.warn("Failed to patch stream:", error);
@@ -11845,6 +11884,33 @@ function traceStreamingChannel(channel, config) {
11845
11884
  const { span, startTime } = spanData;
11846
11885
  if (isAsyncIterable(asyncEndEvent.result)) {
11847
11886
  let firstChunkTime;
11887
+ const handleStreamError = (error) => {
11888
+ try {
11889
+ span.log({ error });
11890
+ } catch (loggingError) {
11891
+ debugLogger.error(
11892
+ `Error logging failure for ${channelName}:`,
11893
+ loggingError
11894
+ );
11895
+ }
11896
+ try {
11897
+ span.end();
11898
+ } catch (endingError) {
11899
+ debugLogger.error(
11900
+ `Error ending span for ${channelName}:`,
11901
+ endingError
11902
+ );
11903
+ }
11904
+ states.delete(event);
11905
+ runStreamingErrorHook({
11906
+ channelName,
11907
+ config,
11908
+ error,
11909
+ event: asyncEndEvent,
11910
+ span,
11911
+ startTime
11912
+ });
11913
+ };
11848
11914
  patchStreamIfNeeded(asyncEndEvent.result, {
11849
11915
  onChunk: () => {
11850
11916
  if (firstChunkTime === void 0) {
@@ -11924,33 +11990,12 @@ function traceStreamingChannel(channel, config) {
11924
11990
  });
11925
11991
  }
11926
11992
  },
11927
- onError: (error) => {
11928
- try {
11929
- span.log({ error });
11930
- } catch (loggingError) {
11931
- debugLogger.error(
11932
- `Error logging failure for ${channelName}:`,
11933
- loggingError
11934
- );
11935
- }
11936
- try {
11937
- span.end();
11938
- } catch (endingError) {
11939
- debugLogger.error(
11940
- `Error ending span for ${channelName}:`,
11941
- endingError
11942
- );
11943
- }
11944
- states.delete(event);
11945
- runStreamingErrorHook({
11946
- channelName,
11947
- config,
11948
- error,
11949
- event: asyncEndEvent,
11950
- span,
11951
- startTime
11952
- });
11953
- }
11993
+ onCancel: () => {
11994
+ const error = new Error("Stream cancelled before completion");
11995
+ error.name = "AbortError";
11996
+ handleStreamError(error);
11997
+ },
11998
+ onError: handleStreamError
11954
11999
  });
11955
12000
  return;
11956
12001
  }
@@ -12710,6 +12755,29 @@ function currentWorkflowAgentWrapperSpan() {
12710
12755
  return void 0;
12711
12756
  }
12712
12757
 
12758
+ // src/instrumentation/plugins/cloudflare-think-context.ts
12759
+ var cloudflareThinkSpans = /* @__PURE__ */ new Map();
12760
+ function registerCloudflareThinkSpan(span) {
12761
+ if (span.spanId) {
12762
+ cloudflareThinkSpans.set(span.spanId, span);
12763
+ }
12764
+ }
12765
+ function unregisterCloudflareThinkSpan(span) {
12766
+ if (span.spanId) {
12767
+ cloudflareThinkSpans.delete(span.spanId);
12768
+ }
12769
+ }
12770
+ function currentCloudflareThinkSpan() {
12771
+ const parentSpanIds = _internalGetGlobalState()?.contextManager.getParentSpanIds()?.spanParents ?? [];
12772
+ for (const parentSpanId of parentSpanIds) {
12773
+ const span = cloudflareThinkSpans.get(parentSpanId);
12774
+ if (span) {
12775
+ return span;
12776
+ }
12777
+ }
12778
+ return void 0;
12779
+ }
12780
+
12713
12781
  // src/vendor-sdk-types/ai-sdk-v7-telemetry.ts
12714
12782
  var BRAINTRUST_AI_SDK_V7_OPERATION_KEY = /* @__PURE__ */ Symbol.for(
12715
12783
  "braintrust.ai-sdk.v7.telemetry-operation-key"
@@ -12821,6 +12889,7 @@ var AISDKPlugin = class extends BasePlugin {
12821
12889
  traceStreamingChannel(aiSDKChannels.streamText, {
12822
12890
  name: "streamText",
12823
12891
  type: "function" /* FUNCTION */,
12892
+ shouldTrace: () => currentCloudflareThinkSpan() === void 0,
12824
12893
  extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
12825
12894
  extractOutput: (result, endEvent) => processAISDKOutput(
12826
12895
  result,
@@ -12841,6 +12910,7 @@ var AISDKPlugin = class extends BasePlugin {
12841
12910
  traceSyncStreamChannel(aiSDKChannels.streamTextSync, {
12842
12911
  name: "streamText",
12843
12912
  type: "function" /* FUNCTION */,
12913
+ shouldTrace: () => currentCloudflareThinkSpan() === void 0,
12844
12914
  extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths),
12845
12915
  patchResult: ({ endEvent, result, span, startTime }) => patchAISDKStreamingResult({
12846
12916
  defaultDenyOutputPaths: denyOutputPaths,
@@ -13826,6 +13896,23 @@ function prepareAISDKWorkflowAgentStreamInput(params, event, span, defaultDenyOu
13826
13896
  metadata
13827
13897
  };
13828
13898
  }
13899
+ function prepareAISDKAgentCallInput(params, event, span, defaultDenyOutputPaths = DEFAULT_DENY_OUTPUT_PATHS) {
13900
+ const { input } = processAISDKWorkflowAgentCallInput(params);
13901
+ const metadata = extractMetadataFromCallParams(params, event.self);
13902
+ const childTracing = prepareAISDKChildTracing(
13903
+ params,
13904
+ event.self,
13905
+ span,
13906
+ defaultDenyOutputPaths,
13907
+ event.aiSDK,
13908
+ { agentOwner: true }
13909
+ );
13910
+ event.modelWrapped = childTracing.modelWrapped;
13911
+ if (childTracing.cleanup) {
13912
+ event.__braintrust_ai_sdk_cleanup = childTracing.cleanup;
13913
+ }
13914
+ return { input, metadata };
13915
+ }
13829
13916
  function prepareAISDKEmbedInput(params, self) {
13830
13917
  return {
13831
13918
  input: { ...params },
@@ -13849,6 +13936,18 @@ function extractTopLevelAISDKMetrics(result, event, startTime) {
13849
13936
  }
13850
13937
  return metrics;
13851
13938
  }
13939
+ async function extractResolvedAISDKTokenMetrics(result) {
13940
+ for (const field of ["totalUsage", "usage"]) {
13941
+ try {
13942
+ const usage = await Promise.resolve(result[field]);
13943
+ if (usage !== void 0) {
13944
+ return extractTokenMetrics({ [field]: usage });
13945
+ }
13946
+ } catch {
13947
+ }
13948
+ }
13949
+ return extractTokenMetrics(result);
13950
+ }
13852
13951
  function hasModelChildTracing(event) {
13853
13952
  return event?.modelWrapped === true || event?.__braintrust_ai_sdk_model_wrapped === true;
13854
13953
  }
@@ -14666,13 +14765,15 @@ function patchAISDKStreamingResult(args) {
14666
14765
  const {
14667
14766
  defaultDenyOutputPaths,
14668
14767
  endEvent,
14768
+ forceTopLevelMetrics,
14669
14769
  onComplete,
14670
14770
  onCancel,
14671
14771
  onError,
14672
14772
  result,
14673
14773
  resolvePromiseUsage,
14674
14774
  span,
14675
- startTime
14775
+ startTime,
14776
+ transformOutput
14676
14777
  } = args;
14677
14778
  if (!result || typeof result !== "object") {
14678
14779
  return false;
@@ -14725,8 +14826,8 @@ function patchAISDKStreamingResult(args) {
14725
14826
  }
14726
14827
  outputLogged = true;
14727
14828
  try {
14728
- const metrics = extractTopLevelAISDKMetrics(result, endEvent);
14729
- if (resolvePromiseUsage) {
14829
+ const metrics = forceTopLevelMetrics ? await extractResolvedAISDKTokenMetrics(result) : extractTopLevelAISDKMetrics(result, endEvent);
14830
+ if (resolvePromiseUsage && !forceTopLevelMetrics) {
14730
14831
  try {
14731
14832
  const resultRecord2 = result;
14732
14833
  const pendingUsage = resultRecord2.totalUsage ?? resultRecord2.usage;
@@ -14745,10 +14846,11 @@ function patchAISDKStreamingResult(args) {
14745
14846
  if (metrics.time_to_first_token === void 0 && firstChunkTime !== void 0) {
14746
14847
  metrics.time_to_first_token = firstChunkTime - startTime;
14747
14848
  }
14748
- const output = await processAISDKStreamingOutput(
14849
+ const processedOutput = await processAISDKStreamingOutput(
14749
14850
  result,
14750
14851
  resolveDenyOutputPaths(endEvent, defaultDenyOutputPaths)
14751
14852
  );
14853
+ const output = transformOutput ? transformOutput(processedOutput) : processedOutput;
14752
14854
  const metadata = buildResolvedMetadataPayload(result).metadata;
14753
14855
  try {
14754
14856
  span.log({
@@ -16915,7 +17017,7 @@ var FlueObserveBridge = class {
16915
17017
  return;
16916
17018
  }
16917
17019
  const state = this.operationsById.get(event.operationId) ?? this.startSyntheticOperation(event);
16918
- const output = operationOutput(event);
17020
+ const output = operationOutput(event) ?? outputFromAgentTurn(state.latestAgentOutput);
16919
17021
  const metadata = {
16920
17022
  ...state.metadata,
16921
17023
  ...extractEventMetadata(event),
@@ -16924,7 +17026,7 @@ var FlueObserveBridge = class {
16924
17026
  };
16925
17027
  this.finishPendingChildrenForOperation(event, output);
16926
17028
  safeLog(state.span, {
16927
- ...event.isError ? { error: toLoggedError(event.error) } : {},
17029
+ ...event.isError ? { error: toLoggedError(event.errorInfo ?? event.error) } : {},
16928
17030
  metadata,
16929
17031
  metrics: durationMetrics(event.durationMs),
16930
17032
  output
@@ -16955,7 +17057,7 @@ var FlueObserveBridge = class {
16955
17057
  };
16956
17058
  const parent = this.parentSpanForTurn(event);
16957
17059
  const span = startFlueSpan(parent, {
16958
- name: `llm:${model ?? event.purpose ?? "unknown"}`,
17060
+ name: "flue.turn",
16959
17061
  spanAttributes: { type: "llm" /* LLM */ },
16960
17062
  startTime: eventTime(event.timestamp),
16961
17063
  event: {
@@ -16999,6 +17101,12 @@ var FlueObserveBridge = class {
16999
17101
  },
17000
17102
  output
17001
17103
  });
17104
+ if (event.purpose === "agent" && event.operationId) {
17105
+ const operation = this.operationsById.get(event.operationId);
17106
+ if (operation) {
17107
+ operation.latestAgentOutput = output;
17108
+ }
17109
+ }
17002
17110
  safeEnd(state.span, eventTime(event.timestamp));
17003
17111
  this.turnsByKey.delete(key);
17004
17112
  }
@@ -17078,7 +17186,7 @@ var FlueObserveBridge = class {
17078
17186
  return;
17079
17187
  }
17080
17188
  safeLog(state.span, {
17081
- ...event.isError ? { error: toLoggedError(event.result) } : {},
17189
+ ...event.isError ? { error: toLoggedError(event.errorInfo ?? event.result) } : {},
17082
17190
  metadata: {
17083
17191
  ...state.metadata,
17084
17192
  ...extractEventMetadata(event),
@@ -17124,6 +17232,7 @@ var FlueObserveBridge = class {
17124
17232
  ...event.usage ? { "flue.usage": event.usage } : {}
17125
17233
  };
17126
17234
  safeLog(state.span, {
17235
+ ...event.isError ? { error: toLoggedError(event.errorInfo ?? event.error) } : {},
17127
17236
  metadata,
17128
17237
  metrics: {
17129
17238
  ...durationMetrics(event.durationMs),
@@ -17228,7 +17337,7 @@ var FlueObserveBridge = class {
17228
17337
  ...event.purpose ? { "flue.turn_purpose": event.purpose } : {}
17229
17338
  };
17230
17339
  const span = startFlueSpan(this.parentSpanForEvent(event), {
17231
- name: `llm:${model ?? event.purpose ?? "unknown"}`,
17340
+ name: "flue.turn",
17232
17341
  spanAttributes: { type: "llm" /* LLM */ },
17233
17342
  startTime: eventTime(event.timestamp),
17234
17343
  event: { metadata }
@@ -17360,7 +17469,10 @@ function extractEventMetadata(event, ctx) {
17360
17469
  return {
17361
17470
  ...event.runId ? { "flue.run_id": event.runId } : {},
17362
17471
  ...event.instanceId ? { "flue.instance_id": event.instanceId } : {},
17472
+ ...event.submissionId ? { "flue.submission_id": event.submissionId } : {},
17363
17473
  ...event.dispatchId ? { "flue.dispatch_id": event.dispatchId } : {},
17474
+ ...event.agentName ? { "flue.agent_name": event.agentName } : {},
17475
+ ...event.conversationId ? { "flue.conversation_id": event.conversationId } : {},
17364
17476
  ...typeof event.eventIndex === "number" ? { "flue.event_index": event.eventIndex } : {},
17365
17477
  ...event.session ? { "flue.session": event.session } : {},
17366
17478
  ...event.parentSession ? { "flue.parent_session": event.parentSession } : {},
@@ -17389,7 +17501,7 @@ function flueTurnRequestInput(event) {
17389
17501
  return event.request?.input ?? event.input;
17390
17502
  }
17391
17503
  function flueTurnRequestModel(event) {
17392
- return event.request?.model ?? event.model;
17504
+ return event.request?.requestedModel ?? event.request?.model ?? event.model;
17393
17505
  }
17394
17506
  function flueTurnRequestProvider(event) {
17395
17507
  return event.request?.providerName ?? event.provider ?? event.request?.providerId;
@@ -17398,10 +17510,10 @@ function flueTurnRequestApi(event) {
17398
17510
  return event.request?.api ?? event.api;
17399
17511
  }
17400
17512
  function flueTurnRequestReasoning(event) {
17401
- return event.request?.reasoning ?? event.reasoning;
17513
+ return event.request?.reasoningLevel ?? event.request?.reasoning ?? event.reasoning;
17402
17514
  }
17403
17515
  function flueTurnModel(event) {
17404
- return event.request?.model ?? event.model;
17516
+ return event.response?.responseModel ?? event.request?.requestedModel ?? event.request?.model ?? event.model;
17405
17517
  }
17406
17518
  function flueTurnProvider(event) {
17407
17519
  return event.request?.providerName ?? event.provider ?? event.request?.providerId;
@@ -17416,7 +17528,7 @@ function flueTurnOutput(event) {
17416
17528
  return event.response?.output ?? event.output;
17417
17529
  }
17418
17530
  function flueTurnStopReason(event) {
17419
- return event.response?.stopReason ?? event.stopReason;
17531
+ return event.response?.finishReason ?? event.response?.stopReason ?? event.stopReason;
17420
17532
  }
17421
17533
  function flueTurnError(event) {
17422
17534
  return event.response?.error ?? event.response?.errorInfo?.message ?? event.error;
@@ -17431,6 +17543,9 @@ function flueToolInput(event) {
17431
17543
  return event.input;
17432
17544
  }
17433
17545
  function flueToolOutput(event) {
17546
+ if (Object.hasOwn(event, "effectiveResult")) {
17547
+ return event.effectiveResult;
17548
+ }
17434
17549
  return event.output !== void 0 ? event.output : event.result;
17435
17550
  }
17436
17551
  function flueToolError(event) {
@@ -17438,10 +17553,36 @@ function flueToolError(event) {
17438
17553
  }
17439
17554
  function operationOutput(event) {
17440
17555
  if (event.operationKind === "prompt" || event.operationKind === "skill") {
17441
- return llmResultFromOperationResult(event.result);
17556
+ return outputFromAgentInvocation(event.agentOutput) ?? llmResultFromOperationResult(event.result);
17442
17557
  }
17443
17558
  return event.result ?? (event.operationKind === "compact" ? { completed: true } : void 0);
17444
17559
  }
17560
+ function outputFromAgentInvocation(output) {
17561
+ if (output?.type === "text") {
17562
+ return output.text;
17563
+ }
17564
+ if (output?.type === "data") {
17565
+ return output.data;
17566
+ }
17567
+ return void 0;
17568
+ }
17569
+ function outputFromAgentTurn(output) {
17570
+ if (!isObjectLike(output)) {
17571
+ return output;
17572
+ }
17573
+ const content = Reflect.get(output, "content");
17574
+ if (!Array.isArray(content)) {
17575
+ return output;
17576
+ }
17577
+ const text = content.flatMap((part) => {
17578
+ if (!isObjectLike(part) || Reflect.get(part, "type") !== "text") {
17579
+ return [];
17580
+ }
17581
+ const value = Reflect.get(part, "text");
17582
+ return typeof value === "string" ? [value] : [];
17583
+ });
17584
+ return text.length > 0 ? text.join("") : output;
17585
+ }
17445
17586
  function llmResultFromOperationResult(result) {
17446
17587
  if (!isObjectLike(result)) {
17447
17588
  return result;
@@ -17540,7 +17681,7 @@ function safeEnd(span, endTime) {
17540
17681
  }
17541
17682
  }
17542
17683
  function logInstrumentationError(label, error) {
17543
- console.error(`Error in ${label} instrumentation:`, error);
17684
+ debugLogger.debug(`Error in ${label} instrumentation:`, error);
17544
17685
  }
17545
17686
 
17546
17687
  // src/wrappers/langchain/callback-handler.ts
@@ -19037,6 +19178,7 @@ var AnthropicPlugin = class extends BasePlugin {
19037
19178
  onEnable() {
19038
19179
  this.subscribeToAnthropicChannels();
19039
19180
  this.subscribeToAnthropicToolRunner();
19181
+ this.subscribeToAnthropicSessionStreams();
19040
19182
  }
19041
19183
  onDisable() {
19042
19184
  this.unsubscribers = unsubscribeAll(this.unsubscribers);
@@ -19154,7 +19296,411 @@ var AnthropicPlugin = class extends BasePlugin {
19154
19296
  tracingChannel.unsubscribe(handlers);
19155
19297
  });
19156
19298
  }
19299
+ subscribeToAnthropicSessionStreams() {
19300
+ this.subscribeToAnthropicSessionStream(
19301
+ anthropicChannels.betaSessionsEventsStream,
19302
+ false
19303
+ );
19304
+ this.subscribeToAnthropicSessionStream(
19305
+ anthropicChannels.betaSessionsThreadsEventsStream,
19306
+ true
19307
+ );
19308
+ }
19309
+ subscribeToAnthropicSessionStream(channel, isThread) {
19310
+ const tracingChannel = channel.tracingChannel();
19311
+ const pending = /* @__PURE__ */ new WeakSet();
19312
+ const handlers = {
19313
+ start: (event) => {
19314
+ if (isAutoInstrumentationSuppressed()) {
19315
+ return;
19316
+ }
19317
+ pending.add(event);
19318
+ },
19319
+ asyncEnd: (event) => {
19320
+ if (!pending.delete(event)) {
19321
+ return;
19322
+ }
19323
+ const stream = event.result;
19324
+ if (!isAsyncIterable(stream)) {
19325
+ return;
19326
+ }
19327
+ registerAnthropicSessionStreamCollector(stream, () => {
19328
+ wrapAnthropicSessionEventStream(stream, isThread);
19329
+ });
19330
+ },
19331
+ error: (event) => {
19332
+ pending.delete(event);
19333
+ }
19334
+ };
19335
+ tracingChannel.subscribe(handlers);
19336
+ this.unsubscribers.push(() => tracingChannel.unsubscribe(handlers));
19337
+ }
19157
19338
  };
19339
+ function wrapAnthropicSessionEventStream(stream, isThread) {
19340
+ const state = {
19341
+ history: [],
19342
+ isThread,
19343
+ pendingInput: [],
19344
+ seenEventIds: /* @__PURE__ */ new Set()
19345
+ };
19346
+ patchStreamIfNeeded(stream, {
19347
+ onChunk: (event) => handleAnthropicSessionEvent(state, event),
19348
+ onComplete: () => finalizeAnthropicSessionTurn(state),
19349
+ onError: (error) => finalizeAnthropicSessionTurn(state, error)
19350
+ });
19351
+ return stream;
19352
+ }
19353
+ function handleAnthropicSessionEvent(state, event) {
19354
+ try {
19355
+ if (typeof event.id === "string" && event.type !== "event_delta" && state.seenEventIds.has(event.id)) {
19356
+ return;
19357
+ }
19358
+ if (typeof event.id === "string" && event.type !== "event_delta") {
19359
+ state.seenEventIds.add(event.id);
19360
+ }
19361
+ switch (event.type) {
19362
+ case "user.message":
19363
+ case "system.message":
19364
+ recordAnthropicSessionInput(
19365
+ state,
19366
+ event
19367
+ );
19368
+ break;
19369
+ case "session.status_running":
19370
+ if (!state.isThread) {
19371
+ ensureAnthropicSessionTurn(state);
19372
+ }
19373
+ break;
19374
+ case "session.thread_status_running":
19375
+ if (state.isThread) {
19376
+ ensureAnthropicSessionTurn(state);
19377
+ }
19378
+ break;
19379
+ case "span.model_request_start":
19380
+ if (typeof event.id === "string") {
19381
+ startAnthropicSessionModel(state, event.id);
19382
+ }
19383
+ break;
19384
+ case "event_delta":
19385
+ recordAnthropicSessionFirstToken(state, event);
19386
+ break;
19387
+ case "agent.message":
19388
+ recordAnthropicSessionMessage(
19389
+ state,
19390
+ event
19391
+ );
19392
+ break;
19393
+ case "agent.custom_tool_use":
19394
+ case "agent.mcp_tool_use":
19395
+ case "agent.tool_use":
19396
+ startAnthropicSessionTool(state, event);
19397
+ break;
19398
+ case "user.tool_confirmation":
19399
+ recordAnthropicSessionToolConfirmation(state, event);
19400
+ break;
19401
+ case "agent.mcp_tool_result":
19402
+ case "agent.tool_result":
19403
+ case "user.custom_tool_result":
19404
+ case "user.tool_result":
19405
+ finalizeAnthropicSessionTool(
19406
+ state,
19407
+ event
19408
+ );
19409
+ break;
19410
+ case "span.model_request_end":
19411
+ finalizeAnthropicSessionModel(
19412
+ state,
19413
+ event
19414
+ );
19415
+ break;
19416
+ case "session.status_idle":
19417
+ case "session.thread_status_idle": {
19418
+ if (event.type === "session.status_idle" && state.isThread || event.type === "session.thread_status_idle" && !state.isThread) {
19419
+ break;
19420
+ }
19421
+ const stopReason = isObject(event.stop_reason) ? event.stop_reason.type : void 0;
19422
+ if (stopReason === "end_turn") {
19423
+ finalizeAnthropicSessionTurn(state);
19424
+ } else if (stopReason === "retries_exhausted") {
19425
+ finalizeAnthropicSessionTurn(
19426
+ state,
19427
+ "Anthropic session retries exhausted"
19428
+ );
19429
+ }
19430
+ break;
19431
+ }
19432
+ case "session.error":
19433
+ finalizeAnthropicSessionTurn(state, event.error);
19434
+ break;
19435
+ case "session.deleted":
19436
+ case "session.status_terminated":
19437
+ case "session.thread_status_terminated":
19438
+ if (event.type === "session.deleted" || event.type === "session.status_terminated" && !state.isThread || event.type === "session.thread_status_terminated" && state.isThread) {
19439
+ finalizeAnthropicSessionTurn(state);
19440
+ }
19441
+ break;
19442
+ default:
19443
+ break;
19444
+ }
19445
+ } catch (error) {
19446
+ debugLogger.debug("Error processing Anthropic Sessions event:", error);
19447
+ }
19448
+ }
19449
+ function ensureAnthropicSessionTurn(state) {
19450
+ if (state.activeTurn) {
19451
+ return state.activeTurn;
19452
+ }
19453
+ const input = state.pendingInput;
19454
+ state.pendingInput = [];
19455
+ const span = startSpan(
19456
+ withSpanInstrumentationName(
19457
+ {
19458
+ event: {
19459
+ ...input.length > 0 ? { input } : {},
19460
+ metadata: { provider: "anthropic" }
19461
+ },
19462
+ name: state.isThread ? "anthropic.beta.sessions.thread.turn" : "anthropic.beta.sessions.turn",
19463
+ spanAttributes: { type: "task" /* TASK */ }
19464
+ },
19465
+ INSTRUMENTATION_NAMES.ANTHROPIC
19466
+ )
19467
+ );
19468
+ state.activeTurn = {
19469
+ activeTools: /* @__PURE__ */ new Map(),
19470
+ input: input.slice(),
19471
+ metrics: {},
19472
+ span
19473
+ };
19474
+ return state.activeTurn;
19475
+ }
19476
+ function recordAnthropicSessionInput(state, event) {
19477
+ const content = processAttachmentsInInput(event.content);
19478
+ const message = {
19479
+ role: event.type === "system.message" ? "system" : "user",
19480
+ content
19481
+ };
19482
+ state.history.push(message);
19483
+ if (state.activeTurn) {
19484
+ state.activeTurn.input.push(message);
19485
+ } else {
19486
+ state.pendingInput.push(message);
19487
+ }
19488
+ }
19489
+ function startAnthropicSessionModel(state, startEventId) {
19490
+ const turn = ensureAnthropicSessionTurn(state);
19491
+ if (turn.activeModel) {
19492
+ finalizeAnthropicSessionModelState(state, turn);
19493
+ }
19494
+ const span = withCurrent(
19495
+ turn.span,
19496
+ () => startSpan(
19497
+ withSpanInstrumentationName(
19498
+ {
19499
+ event: {
19500
+ ...state.history.length > 0 ? { input: state.history.slice() } : {},
19501
+ metadata: { provider: "anthropic" }
19502
+ },
19503
+ name: "anthropic.messages.create",
19504
+ spanAttributes: { type: "llm" /* LLM */ }
19505
+ },
19506
+ INSTRUMENTATION_NAMES.ANTHROPIC
19507
+ )
19508
+ )
19509
+ );
19510
+ turn.activeModel = {
19511
+ output: [],
19512
+ span,
19513
+ startEventId,
19514
+ startTime: getCurrentUnixTimestamp()
19515
+ };
19516
+ }
19517
+ function recordAnthropicSessionFirstToken(state, event) {
19518
+ const model = state.activeTurn?.activeModel;
19519
+ if (!model || model.firstTokenTime !== void 0 || !isObject(event.delta) || !isObject(event.delta.content) || typeof event.delta.content.text !== "string") {
19520
+ return;
19521
+ }
19522
+ model.firstTokenTime = getCurrentUnixTimestamp();
19523
+ }
19524
+ function recordAnthropicSessionMessage(state, event) {
19525
+ const turn = ensureAnthropicSessionTurn(state);
19526
+ const content = processAttachmentsInInput(
19527
+ event.content
19528
+ );
19529
+ if (turn.activeModel) {
19530
+ turn.activeModel.output.push(...content);
19531
+ } else {
19532
+ const output = { role: "assistant", content };
19533
+ turn.lastOutput = output;
19534
+ state.history.push(output);
19535
+ }
19536
+ }
19537
+ function startAnthropicSessionTool(state, event) {
19538
+ const turn = ensureAnthropicSessionTurn(state);
19539
+ if (turn.activeModel) {
19540
+ turn.activeModel.output.push({
19541
+ type: "tool_use",
19542
+ id: event.id,
19543
+ name: event.name,
19544
+ input: event.input
19545
+ });
19546
+ }
19547
+ const existing = turn.activeTools.get(event.id);
19548
+ if (existing) {
19549
+ existing.span.end();
19550
+ turn.activeTools.delete(event.id);
19551
+ }
19552
+ const span = withCurrent(
19553
+ turn.span,
19554
+ () => startSpan(
19555
+ withSpanInstrumentationName(
19556
+ {
19557
+ event: { input: event.input },
19558
+ name: event.name,
19559
+ spanAttributes: { type: "tool" /* TOOL */ }
19560
+ },
19561
+ INSTRUMENTATION_NAMES.ANTHROPIC
19562
+ )
19563
+ )
19564
+ );
19565
+ const toolState = {
19566
+ approval: event.evaluated_permission === "allow" ? "approved" : event.evaluated_permission === "deny" ? "denied" : void 0,
19567
+ span
19568
+ };
19569
+ turn.activeTools.set(event.id, toolState);
19570
+ if (toolState.approval === "denied") {
19571
+ finalizeAnthropicSessionToolState(toolState);
19572
+ turn.activeTools.delete(event.id);
19573
+ }
19574
+ }
19575
+ function recordAnthropicSessionToolConfirmation(state, event) {
19576
+ if (typeof event.tool_use_id !== "string" || event.result !== "allow" && event.result !== "deny") {
19577
+ return;
19578
+ }
19579
+ const turn = state.activeTurn;
19580
+ const tool = turn?.activeTools.get(event.tool_use_id);
19581
+ if (!turn || !tool) {
19582
+ return;
19583
+ }
19584
+ tool.approval = event.result === "allow" ? "approved" : "denied";
19585
+ if (tool.approval === "denied") {
19586
+ finalizeAnthropicSessionToolState(tool);
19587
+ turn.activeTools.delete(event.tool_use_id);
19588
+ }
19589
+ }
19590
+ function finalizeAnthropicSessionTool(state, event) {
19591
+ const toolUseId = event.tool_use_id ?? event.mcp_tool_use_id ?? event.custom_tool_use_id;
19592
+ if (!toolUseId) {
19593
+ return;
19594
+ }
19595
+ const content = processAttachmentsInInput(event.content);
19596
+ const turn = state.activeTurn;
19597
+ const tool = turn?.activeTools.get(toolUseId);
19598
+ if (turn && tool) {
19599
+ finalizeAnthropicSessionToolState(tool, event, content);
19600
+ turn.activeTools.delete(toolUseId);
19601
+ }
19602
+ const toolResult = {
19603
+ type: "tool_result",
19604
+ tool_use_id: toolUseId,
19605
+ ...content !== void 0 ? { content } : {},
19606
+ ...event.is_error === true ? { is_error: true } : {}
19607
+ };
19608
+ state.history.push({
19609
+ role: "user",
19610
+ content: [toolResult]
19611
+ });
19612
+ }
19613
+ function finalizeAnthropicSessionToolState(state, event, content = event?.content) {
19614
+ safeAnthropicSessionLog(state.span, {
19615
+ ...event?.is_error === true ? {
19616
+ error: content ?? "Anthropic session tool execution failed"
19617
+ } : {},
19618
+ ...state.approval ? { metadata: { tool_approval: state.approval } } : {},
19619
+ ...content !== void 0 ? { output: content } : {}
19620
+ });
19621
+ state.span.end();
19622
+ }
19623
+ function finalizeAnthropicSessionModel(state, event) {
19624
+ const turn = state.activeTurn;
19625
+ const model = turn?.activeModel;
19626
+ if (!turn || !model || model.startEventId !== event.model_request_start_id) {
19627
+ return;
19628
+ }
19629
+ finalizeAnthropicSessionModelState(state, turn, event);
19630
+ }
19631
+ function finalizeAnthropicSessionModelState(state, turn, event) {
19632
+ const model = turn.activeModel;
19633
+ if (!model) {
19634
+ return;
19635
+ }
19636
+ turn.activeModel = void 0;
19637
+ const metrics = event ? parseAnthropicSessionModelUsage(event.model_usage) : {};
19638
+ if (model.firstTokenTime !== void 0) {
19639
+ metrics.time_to_first_token = model.firstTokenTime - model.startTime;
19640
+ }
19641
+ const output = model.output.length > 0 ? { role: "assistant", content: model.output } : void 0;
19642
+ safeAnthropicSessionLog(model.span, {
19643
+ ...event?.is_error ? { error: "Anthropic session model request failed" } : {},
19644
+ ...Object.keys(metrics).length > 0 ? { metrics } : {},
19645
+ ...output ? { output } : {}
19646
+ });
19647
+ model.span.end();
19648
+ if (output) {
19649
+ turn.lastOutput = output;
19650
+ state.history.push(output);
19651
+ }
19652
+ for (const key of [
19653
+ "prompt_tokens",
19654
+ "completion_tokens",
19655
+ "tokens",
19656
+ "prompt_cached_tokens",
19657
+ "prompt_cache_creation_tokens"
19658
+ ]) {
19659
+ const value = metrics[key];
19660
+ if (typeof value === "number") {
19661
+ turn.metrics[key] = (turn.metrics[key] ?? 0) + value;
19662
+ }
19663
+ }
19664
+ }
19665
+ function parseAnthropicSessionModelUsage(usage) {
19666
+ const finalized = finalizeAnthropicTokens({
19667
+ completion_tokens: usage.output_tokens,
19668
+ prompt_cache_creation_tokens: usage.cache_creation_input_tokens,
19669
+ prompt_cached_tokens: usage.cache_read_input_tokens,
19670
+ prompt_tokens: usage.input_tokens
19671
+ });
19672
+ return Object.fromEntries(
19673
+ Object.entries(finalized).filter(
19674
+ (entry) => entry[1] !== void 0
19675
+ )
19676
+ );
19677
+ }
19678
+ function finalizeAnthropicSessionTurn(state, error) {
19679
+ const turn = state.activeTurn;
19680
+ if (!turn) {
19681
+ return;
19682
+ }
19683
+ state.activeTurn = void 0;
19684
+ finalizeAnthropicSessionModelState(state, turn);
19685
+ for (const tool of turn.activeTools.values()) {
19686
+ finalizeAnthropicSessionToolState(tool);
19687
+ }
19688
+ turn.activeTools.clear();
19689
+ safeAnthropicSessionLog(turn.span, {
19690
+ ...error !== void 0 ? { error: error instanceof Error ? error : toLoggedError(error) } : {},
19691
+ ...turn.input.length > 0 ? { input: turn.input } : {},
19692
+ ...turn.lastOutput ? { output: turn.lastOutput } : {},
19693
+ ...Object.keys(turn.metrics).length > 0 ? { metrics: turn.metrics } : {}
19694
+ });
19695
+ turn.span.end();
19696
+ }
19697
+ function safeAnthropicSessionLog(span, event) {
19698
+ try {
19699
+ span.log(event);
19700
+ } catch (error) {
19701
+ debugLogger.debug("Error logging Anthropic Sessions span:", error);
19702
+ }
19703
+ }
19158
19704
  function parseMetricsFromUsage2(usage) {
19159
19705
  if (!usage) {
19160
19706
  return {};
@@ -21213,6 +21759,283 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
21213
21759
  }
21214
21760
  };
21215
21761
 
21762
+ // src/instrumentation/plugins/cloudflare-think-plugin.ts
21763
+ var THINK_STATE_ID = /* @__PURE__ */ Symbol.for("braintrust.cloudflare-think.state-id");
21764
+ var CloudflareThinkPlugin = class extends BasePlugin {
21765
+ statesBySpanId = /* @__PURE__ */ new Map();
21766
+ onEnable() {
21767
+ this.subscribeToThinkRuns();
21768
+ this.subscribeToAISDKStreamTextSync();
21769
+ this.subscribeToAISDKStreamTextAsync();
21770
+ }
21771
+ onDisable() {
21772
+ for (const unsubscribe of this.unsubscribers) {
21773
+ unsubscribe();
21774
+ }
21775
+ this.unsubscribers = [];
21776
+ for (const state of this.statesBySpanId.values()) {
21777
+ this.finishState(state);
21778
+ }
21779
+ this.statesBySpanId.clear();
21780
+ }
21781
+ subscribeToThinkRuns() {
21782
+ const channel = cloudflareThinkChannels.runInferenceLoop.tracingChannel();
21783
+ const states = /* @__PURE__ */ new WeakMap();
21784
+ const state = _internalGetGlobalState();
21785
+ const contextManager = state?.contextManager;
21786
+ const currentSpanStore = contextManager ? contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
21787
+ const ensureState2 = (event) => {
21788
+ if (isAutoInstrumentationSuppressed()) {
21789
+ return void 0;
21790
+ }
21791
+ const existing = states.get(event);
21792
+ if (existing) {
21793
+ return existing;
21794
+ }
21795
+ const span = startSpan(
21796
+ withSpanInstrumentationName(
21797
+ {
21798
+ name: "Think.runTurn",
21799
+ spanAttributes: { type: "task" /* TASK */ }
21800
+ },
21801
+ INSTRUMENTATION_NAMES.CLOUDFLARE_THINK
21802
+ )
21803
+ );
21804
+ const runState = {
21805
+ aiResultPatched: false,
21806
+ fallbackInput: extractFallbackInput(event.self?.messages),
21807
+ finalized: false,
21808
+ inputLogged: false,
21809
+ span,
21810
+ startTime: getCurrentUnixTimestamp()
21811
+ };
21812
+ const metadata = {
21813
+ braintrust: {
21814
+ integration_name: "cloudflare-think",
21815
+ sdk_language: "typescript"
21816
+ }
21817
+ };
21818
+ if (typeof event.moduleVersion === "string") {
21819
+ metadata["cloudflare_think.version"] = event.moduleVersion;
21820
+ }
21821
+ span.log({ metadata });
21822
+ states.set(event, runState);
21823
+ if (span.spanId) {
21824
+ this.statesBySpanId.set(span.spanId, runState);
21825
+ }
21826
+ registerCloudflareThinkSpan(span);
21827
+ return runState;
21828
+ };
21829
+ if (contextManager && currentSpanStore && channel.start) {
21830
+ channel.start.bindStore(currentSpanStore, (event) => {
21831
+ const runState = ensureState2(event);
21832
+ return runState ? contextManager.wrapSpanForStore(runState.span) : currentSpanStore.getStore();
21833
+ });
21834
+ this.unsubscribers.push(
21835
+ () => channel.start?.unbindStore(currentSpanStore)
21836
+ );
21837
+ }
21838
+ const handlers = {
21839
+ start: (event) => {
21840
+ ensureState2(event);
21841
+ },
21842
+ asyncEnd: (event) => {
21843
+ const runState = states.get(event);
21844
+ states.delete(event);
21845
+ if (!runState || runState.finalized || runState.aiResultPatched) {
21846
+ return;
21847
+ }
21848
+ this.finishState(runState, void 0, event.result);
21849
+ },
21850
+ error: (event) => {
21851
+ const runState = states.get(event);
21852
+ states.delete(event);
21853
+ if (runState) {
21854
+ this.finishState(runState, event.error);
21855
+ }
21856
+ }
21857
+ };
21858
+ channel.subscribe(handlers);
21859
+ this.unsubscribers.push(() => channel.unsubscribe(handlers));
21860
+ }
21861
+ subscribeToAISDKStreamTextSync() {
21862
+ const channel = aiSDKChannels.streamTextSync.tracingChannel();
21863
+ const handlers = {
21864
+ start: (event) => {
21865
+ this.startAISDKStream(event);
21866
+ },
21867
+ end: (event) => {
21868
+ this.endAISDKStream(event);
21869
+ },
21870
+ error: (event) => {
21871
+ this.errorAISDKStream(event);
21872
+ }
21873
+ };
21874
+ channel.subscribe(handlers);
21875
+ this.unsubscribers.push(() => channel.unsubscribe(handlers));
21876
+ }
21877
+ subscribeToAISDKStreamTextAsync() {
21878
+ const channel = aiSDKChannels.streamText.tracingChannel();
21879
+ const handlers = {
21880
+ start: (event) => {
21881
+ this.startAISDKStream(event);
21882
+ },
21883
+ asyncEnd: (event) => {
21884
+ this.endAISDKStream(event);
21885
+ },
21886
+ error: (event) => {
21887
+ this.errorAISDKStream(event);
21888
+ }
21889
+ };
21890
+ channel.subscribe(handlers);
21891
+ this.unsubscribers.push(() => channel.unsubscribe(handlers));
21892
+ }
21893
+ startAISDKStream(event) {
21894
+ const runState = this.currentState();
21895
+ if (!runState || runState.finalized) {
21896
+ return;
21897
+ }
21898
+ try {
21899
+ const prepared = prepareAISDKAgentCallInput(
21900
+ event.arguments[0],
21901
+ event,
21902
+ runState.span
21903
+ );
21904
+ runState.span.log({
21905
+ input: extractThinkTaskInput(prepared.input),
21906
+ metadata: {
21907
+ ...prepared.metadata,
21908
+ braintrust: {
21909
+ integration_name: "cloudflare-think",
21910
+ sdk_language: "typescript"
21911
+ }
21912
+ }
21913
+ });
21914
+ runState.inputLogged = true;
21915
+ runState.aiEvent = event;
21916
+ Reflect.set(event, THINK_STATE_ID, runState.span.spanId);
21917
+ } catch (error) {
21918
+ debugLogger.error(
21919
+ "Error preparing @cloudflare/think AI SDK tracing:",
21920
+ error
21921
+ );
21922
+ }
21923
+ }
21924
+ endAISDKStream(event) {
21925
+ const runState = this.stateForAISDKEvent(event);
21926
+ if (!runState || runState.finalized) {
21927
+ return;
21928
+ }
21929
+ const patched = patchAISDKStreamingResult({
21930
+ defaultDenyOutputPaths: DEFAULT_DENY_OUTPUT_PATHS,
21931
+ endEvent: event,
21932
+ forceTopLevelMetrics: true,
21933
+ onComplete: () => this.releaseState(runState),
21934
+ result: event.result,
21935
+ span: runState.span,
21936
+ startTime: runState.startTime,
21937
+ transformOutput: extractThinkTaskOutput
21938
+ });
21939
+ runState.aiResultPatched = patched;
21940
+ if (!patched) {
21941
+ this.finishState(runState, void 0, event.result);
21942
+ }
21943
+ }
21944
+ errorAISDKStream(event) {
21945
+ const runState = this.stateForAISDKEvent(event);
21946
+ if (runState) {
21947
+ this.finishState(runState, event.error);
21948
+ }
21949
+ }
21950
+ currentState() {
21951
+ const parentIds = _internalGetGlobalState()?.contextManager.getParentSpanIds()?.spanParents ?? [];
21952
+ for (const parentId of parentIds) {
21953
+ const state = this.statesBySpanId.get(parentId);
21954
+ if (state) {
21955
+ return state;
21956
+ }
21957
+ }
21958
+ return void 0;
21959
+ }
21960
+ stateForAISDKEvent(event) {
21961
+ const spanId = Reflect.get(event, THINK_STATE_ID);
21962
+ return typeof spanId === "string" ? this.statesBySpanId.get(spanId) : void 0;
21963
+ }
21964
+ finishState(state, error, output) {
21965
+ if (state.finalized) {
21966
+ return;
21967
+ }
21968
+ state.finalized = true;
21969
+ try {
21970
+ if (!state.inputLogged && state.fallbackInput !== void 0) {
21971
+ state.span.log({ input: state.fallbackInput });
21972
+ }
21973
+ if (error !== void 0) {
21974
+ state.span.log({ error });
21975
+ } else if (output !== void 0) {
21976
+ state.span.log({ output: extractThinkTaskOutput(output) });
21977
+ }
21978
+ finalizeAISDKChildTracing(state.aiEvent);
21979
+ state.span.end();
21980
+ } finally {
21981
+ this.releaseState(state);
21982
+ }
21983
+ }
21984
+ releaseState(state) {
21985
+ state.finalized = true;
21986
+ unregisterCloudflareThinkSpan(state.span);
21987
+ if (state.span.spanId) {
21988
+ this.statesBySpanId.delete(state.span.spanId);
21989
+ }
21990
+ }
21991
+ };
21992
+ function extractFallbackInput(messages) {
21993
+ if (!Array.isArray(messages) || messages.length === 0) {
21994
+ return void 0;
21995
+ }
21996
+ let latestUserIndex = -1;
21997
+ for (let index = messages.length - 1; index >= 0; index--) {
21998
+ if (messages[index]?.role === "user") {
21999
+ latestUserIndex = index;
22000
+ break;
22001
+ }
22002
+ }
22003
+ const selected = latestUserIndex >= 0 ? messages.slice(latestUserIndex) : [];
22004
+ return selected.map((message) => ({
22005
+ role: message.role,
22006
+ content: message.content ?? message.parts
22007
+ }));
22008
+ }
22009
+ function extractThinkTaskOutput(output) {
22010
+ if (!isObject(output)) {
22011
+ return output;
22012
+ }
22013
+ if (typeof output.text === "string") {
22014
+ return { role: "assistant", content: output.text };
22015
+ }
22016
+ if (output.object !== void 0) {
22017
+ return output.object;
22018
+ }
22019
+ return output;
22020
+ }
22021
+ function extractThinkTaskInput(input) {
22022
+ if (!isObject(input)) {
22023
+ return input;
22024
+ }
22025
+ const system = input.instructions ?? input.system;
22026
+ if (Array.isArray(input.messages)) {
22027
+ return system === void 0 ? input.messages : [{ role: "system", content: system }, ...input.messages];
22028
+ }
22029
+ if (Array.isArray(input.prompt)) {
22030
+ return system === void 0 ? input.prompt : [{ role: "system", content: system }, ...input.prompt];
22031
+ }
22032
+ if (typeof input.prompt === "string") {
22033
+ const messages = [{ role: "user", content: input.prompt }];
22034
+ return system === void 0 ? messages : [{ role: "system", content: system }, ...messages];
22035
+ }
22036
+ return input;
22037
+ }
22038
+
21216
22039
  // src/instrumentation/plugins/cursor-sdk-plugin.ts
21217
22040
  var PATCHED_AGENT = /* @__PURE__ */ Symbol.for("braintrust.cursor-sdk.auto-patched-agent");
21218
22041
  var PATCHED_RUN = /* @__PURE__ */ Symbol.for("braintrust.cursor-sdk.patched-run");
@@ -26730,6 +27553,450 @@ function aggregateMistralStreamChunks(chunks) {
26730
27553
  };
26731
27554
  }
26732
27555
 
27556
+ // src/instrumentation/plugins/ollama-plugin.ts
27557
+ var OllamaPlugin = class extends BasePlugin {
27558
+ onEnable() {
27559
+ this.unsubscribers.push(
27560
+ traceStreamingChannel(ollamaChannels.chat, {
27561
+ name: "ollama.chat",
27562
+ type: "llm" /* LLM */,
27563
+ extractInput: extractOllamaChatInput,
27564
+ extractOutput: (result, event) => extractOllamaChatOutput(
27565
+ result,
27566
+ countOllamaToolCalls(event?.arguments?.[0]?.messages)
27567
+ ),
27568
+ extractMetadata: extractOllamaResponseMetadata,
27569
+ extractMetrics: extractOllamaMetrics,
27570
+ aggregateChunks: aggregateOllamaChatChunks
27571
+ }),
27572
+ traceStreamingChannel(ollamaChannels.generate, {
27573
+ name: "ollama.generate",
27574
+ type: "llm" /* LLM */,
27575
+ extractInput: extractOllamaGenerateInput,
27576
+ extractOutput: extractOllamaGenerateOutput,
27577
+ extractMetadata: extractOllamaResponseMetadata,
27578
+ extractMetrics: extractOllamaMetrics,
27579
+ aggregateChunks: aggregateOllamaGenerateChunks
27580
+ }),
27581
+ traceAsyncChannel(ollamaChannels.embed, {
27582
+ name: "ollama.embed",
27583
+ type: "llm" /* LLM */,
27584
+ extractInput: extractOllamaEmbedInput,
27585
+ extractOutput: extractOllamaEmbedOutput,
27586
+ extractMetadata: extractOllamaResponseMetadata,
27587
+ extractMetrics: extractOllamaMetrics
27588
+ })
27589
+ );
27590
+ }
27591
+ onDisable() {
27592
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
27593
+ }
27594
+ };
27595
+ function isNonNegativeNumber(value) {
27596
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
27597
+ }
27598
+ function normalizeFinishReason(response, hasToolCalls = false) {
27599
+ if (hasToolCalls) {
27600
+ return "tool_calls";
27601
+ }
27602
+ return response?.done_reason || "stop";
27603
+ }
27604
+ function stringifyArguments(value) {
27605
+ if (typeof value === "string") {
27606
+ return value;
27607
+ }
27608
+ try {
27609
+ return JSON.stringify(value ?? {});
27610
+ } catch {
27611
+ return String(value);
27612
+ }
27613
+ }
27614
+ function syntheticToolCallId(name, index) {
27615
+ const normalizedName = name.replace(/[^a-zA-Z0-9_-]/g, "_") || "tool";
27616
+ return `ollama_call_${normalizedName}_${index}`;
27617
+ }
27618
+ function normalizeToolCall(toolCall, index) {
27619
+ const name = toolCall.function?.name;
27620
+ if (typeof name !== "string" || name.length === 0) {
27621
+ return void 0;
27622
+ }
27623
+ return {
27624
+ id: typeof toolCall.id === "string" && toolCall.id.length > 0 ? toolCall.id : syntheticToolCallId(name, index),
27625
+ type: "function",
27626
+ function: {
27627
+ name,
27628
+ arguments: stringifyArguments(toolCall.function?.arguments)
27629
+ }
27630
+ };
27631
+ }
27632
+ function normalizeToolCalls(toolCalls, syntheticIdOffset = 0) {
27633
+ if (!Array.isArray(toolCalls)) {
27634
+ return [];
27635
+ }
27636
+ return toolCalls.flatMap((toolCall, index) => {
27637
+ const normalized = normalizeToolCall(toolCall, syntheticIdOffset + index);
27638
+ return normalized ? [normalized] : [];
27639
+ });
27640
+ }
27641
+ function countOllamaToolCalls(messages) {
27642
+ if (!Array.isArray(messages)) {
27643
+ return 0;
27644
+ }
27645
+ return messages.reduce(
27646
+ (count, message) => count + (isObject(message) && Array.isArray(message.tool_calls) ? message.tool_calls.length : 0),
27647
+ 0
27648
+ );
27649
+ }
27650
+ function imageBytes(value) {
27651
+ if (value instanceof Uint8Array) {
27652
+ return value;
27653
+ }
27654
+ if (value instanceof ArrayBuffer) {
27655
+ return new Uint8Array(value);
27656
+ }
27657
+ if (typeof value !== "string" || value.startsWith("data:")) {
27658
+ return void 0;
27659
+ }
27660
+ try {
27661
+ const decoded = atob(value.slice(0, 24));
27662
+ return Uint8Array.from(decoded, (character) => character.charCodeAt(0));
27663
+ } catch {
27664
+ return void 0;
27665
+ }
27666
+ }
27667
+ function inferImageMediaType(value) {
27668
+ if (typeof value === "string") {
27669
+ const dataUrlType = value.match(/^data:(image\/[^;]+);base64,/i)?.[1];
27670
+ if (dataUrlType) {
27671
+ return dataUrlType;
27672
+ }
27673
+ }
27674
+ const bytes = imageBytes(value);
27675
+ if (!bytes) {
27676
+ return void 0;
27677
+ }
27678
+ if (bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71) {
27679
+ return "image/png";
27680
+ }
27681
+ if (bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) {
27682
+ return "image/jpeg";
27683
+ }
27684
+ const signature = String.fromCharCode(...bytes.slice(0, 12));
27685
+ if (signature.startsWith("GIF87a") || signature.startsWith("GIF89a")) {
27686
+ return "image/gif";
27687
+ }
27688
+ if (signature.startsWith("RIFF") && signature.slice(8, 12) === "WEBP") {
27689
+ return "image/webp";
27690
+ }
27691
+ return void 0;
27692
+ }
27693
+ function normalizeTextAndImages(content, images) {
27694
+ if (!images || images.length === 0) {
27695
+ return { content };
27696
+ }
27697
+ const imageParts = [];
27698
+ const unrecognizedImages = [];
27699
+ for (const image of images) {
27700
+ let localImagePath;
27701
+ let localPathMediaType;
27702
+ if (typeof image === "string" && !/^data:/i.test(image) && !/^https?:\/\//i.test(image)) {
27703
+ const extension = image.match(/\.([a-z0-9]+)$/i)?.[1]?.toLowerCase();
27704
+ const extensionMediaType = {
27705
+ png: "image/png",
27706
+ jpg: "image/jpeg",
27707
+ jpeg: "image/jpeg",
27708
+ gif: "image/gif",
27709
+ webp: "image/webp"
27710
+ }[extension ?? ""];
27711
+ if (extensionMediaType && isomorph_default.statSync) {
27712
+ try {
27713
+ if (isomorph_default.statSync(image).isFile()) {
27714
+ localImagePath = image;
27715
+ localPathMediaType = extensionMediaType;
27716
+ }
27717
+ } catch {
27718
+ }
27719
+ }
27720
+ }
27721
+ const mediaType = localPathMediaType ?? inferImageMediaType(image);
27722
+ if (!mediaType) {
27723
+ unrecognizedImages.push(image);
27724
+ continue;
27725
+ }
27726
+ const processedImage = localImagePath ? new Attachment({
27727
+ data: localImagePath,
27728
+ filename: localImagePath.split(/[\\/]/).at(-1) ?? "image",
27729
+ contentType: mediaType
27730
+ }) : processInputAttachments({
27731
+ type: "image",
27732
+ image,
27733
+ mediaType
27734
+ }).image;
27735
+ imageParts.push({
27736
+ type: "image_url",
27737
+ image_url: { url: processedImage }
27738
+ });
27739
+ }
27740
+ return {
27741
+ content: imageParts.length > 0 ? [...content ? [{ type: "text", text: content }] : [], ...imageParts] : content,
27742
+ ...unrecognizedImages.length > 0 ? { unrecognizedImages } : {}
27743
+ };
27744
+ }
27745
+ function normalizeMessage(message, toolCallId, syntheticToolCallIdOffset = 0) {
27746
+ if (typeof message.role !== "string") {
27747
+ return void 0;
27748
+ }
27749
+ const toolCalls = normalizeToolCalls(
27750
+ message.tool_calls,
27751
+ syntheticToolCallIdOffset
27752
+ );
27753
+ if (message.role === "tool") {
27754
+ const toolName = typeof message.tool_name === "string" && message.tool_name.length > 0 ? message.tool_name : "tool";
27755
+ return {
27756
+ role: "tool",
27757
+ tool_call_id: toolCallId ?? syntheticToolCallId(toolName, 0),
27758
+ content: typeof message.content === "string" ? message.content : ""
27759
+ };
27760
+ }
27761
+ const normalizedContent = normalizeTextAndImages(
27762
+ typeof message.content === "string" ? message.content || (toolCalls.length > 0 ? null : "") : toolCalls.length > 0 ? null : "",
27763
+ message.images
27764
+ );
27765
+ return {
27766
+ role: message.role,
27767
+ content: normalizedContent.content,
27768
+ ...typeof message.thinking === "string" && message.thinking.length > 0 ? { reasoning: message.thinking } : {},
27769
+ ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {},
27770
+ ...normalizedContent.unrecognizedImages ? { images: normalizedContent.unrecognizedImages } : {}
27771
+ };
27772
+ }
27773
+ function normalizeMessages(messages) {
27774
+ if (!Array.isArray(messages)) {
27775
+ return [];
27776
+ }
27777
+ const pendingToolCallIds = /* @__PURE__ */ new Map();
27778
+ let syntheticToolCallIdOffset = 0;
27779
+ return messages.flatMap((message) => {
27780
+ if (!isObject(message)) {
27781
+ return [];
27782
+ }
27783
+ const ollamaMessage = message;
27784
+ let toolCallId;
27785
+ if (ollamaMessage.role === "tool") {
27786
+ const toolName = ollamaMessage.tool_name;
27787
+ if (typeof toolName === "string") {
27788
+ toolCallId = pendingToolCallIds.get(toolName)?.shift();
27789
+ }
27790
+ }
27791
+ const normalized = normalizeMessage(
27792
+ ollamaMessage,
27793
+ toolCallId,
27794
+ syntheticToolCallIdOffset
27795
+ );
27796
+ if (Array.isArray(ollamaMessage.tool_calls)) {
27797
+ syntheticToolCallIdOffset += ollamaMessage.tool_calls.length;
27798
+ }
27799
+ if (ollamaMessage.role === "assistant" && normalized) {
27800
+ const toolCalls = Array.isArray(normalized.tool_calls) ? normalized.tool_calls : [];
27801
+ for (const toolCall of toolCalls) {
27802
+ if (!isObject(toolCall) || !isObject(toolCall.function)) {
27803
+ continue;
27804
+ }
27805
+ const name = toolCall.function.name;
27806
+ const id = toolCall.id;
27807
+ if (typeof name === "string" && typeof id === "string") {
27808
+ const ids = pendingToolCallIds.get(name) ?? [];
27809
+ ids.push(id);
27810
+ pendingToolCallIds.set(name, ids);
27811
+ }
27812
+ }
27813
+ }
27814
+ return normalized ? [normalized] : [];
27815
+ });
27816
+ }
27817
+ function normalizeToolDefinition(tool) {
27818
+ const fn = tool.function;
27819
+ if (!fn || typeof fn.name !== "string" || fn.name.length === 0) {
27820
+ return void 0;
27821
+ }
27822
+ return {
27823
+ type: "function",
27824
+ function: {
27825
+ name: fn.name,
27826
+ ...typeof fn.description === "string" ? { description: fn.description } : {},
27827
+ ...isObject(fn.parameters) ? { parameters: fn.parameters } : {}
27828
+ }
27829
+ };
27830
+ }
27831
+ function extractToolsMetadata(tools) {
27832
+ if (!Array.isArray(tools)) {
27833
+ return {};
27834
+ }
27835
+ const normalized = tools.flatMap((tool) => {
27836
+ if (!isObject(tool)) {
27837
+ return [];
27838
+ }
27839
+ const definition = normalizeToolDefinition(tool);
27840
+ return definition ? [definition] : [];
27841
+ });
27842
+ return normalized.length > 0 ? { tools: normalized } : {};
27843
+ }
27844
+ function extractOptionsMetadata(options) {
27845
+ if (!isObject(options)) {
27846
+ return {};
27847
+ }
27848
+ const metadata = {};
27849
+ const mappings = [
27850
+ ["temperature", "temperature"],
27851
+ ["top_p", "top_p"],
27852
+ ["num_predict", "max_tokens"],
27853
+ ["frequency_penalty", "frequency_penalty"],
27854
+ ["presence_penalty", "presence_penalty"],
27855
+ ["stop", "stop"]
27856
+ ];
27857
+ for (const [source, target] of mappings) {
27858
+ if (options[source] !== void 0) {
27859
+ metadata[target] = options[source];
27860
+ }
27861
+ }
27862
+ return metadata;
27863
+ }
27864
+ function extractRequestMetadata(request) {
27865
+ return {
27866
+ provider: "ollama",
27867
+ ...typeof request?.model === "string" ? { model: request.model } : {},
27868
+ ...extractOptionsMetadata(request?.options),
27869
+ ...request?.format !== void 0 ? { response_format: request.format } : {}
27870
+ };
27871
+ }
27872
+ function extractOllamaChatInput([request]) {
27873
+ return {
27874
+ input: normalizeMessages(request.messages),
27875
+ metadata: {
27876
+ ...extractRequestMetadata(request),
27877
+ ...extractToolsMetadata(request.tools)
27878
+ }
27879
+ };
27880
+ }
27881
+ function extractOllamaGenerateInput([request]) {
27882
+ const normalizedPrompt = normalizeTextAndImages(
27883
+ typeof request.prompt === "string" ? request.prompt : "",
27884
+ Array.isArray(request.images) ? request.images : void 0
27885
+ );
27886
+ const input = [
27887
+ ...typeof request.system === "string" ? [{ role: "system", content: request.system }] : [],
27888
+ {
27889
+ role: "user",
27890
+ content: normalizedPrompt.content,
27891
+ ...typeof request.suffix === "string" ? { suffix: request.suffix } : {},
27892
+ ...normalizedPrompt.unrecognizedImages ? { images: normalizedPrompt.unrecognizedImages } : {}
27893
+ }
27894
+ ];
27895
+ return {
27896
+ input,
27897
+ metadata: extractRequestMetadata(request)
27898
+ };
27899
+ }
27900
+ function extractOllamaEmbedInput([request]) {
27901
+ return {
27902
+ input: request.input,
27903
+ metadata: extractRequestMetadata(request)
27904
+ };
27905
+ }
27906
+ function extractOllamaChatOutput(result, syntheticToolCallIdOffset = 0) {
27907
+ if (!isObject(result) || !isObject(result.message)) {
27908
+ return void 0;
27909
+ }
27910
+ const message = normalizeMessage(
27911
+ result.message,
27912
+ void 0,
27913
+ syntheticToolCallIdOffset
27914
+ );
27915
+ if (!message) {
27916
+ return void 0;
27917
+ }
27918
+ const toolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
27919
+ return [
27920
+ {
27921
+ index: 0,
27922
+ finish_reason: normalizeFinishReason(result, toolCalls.length > 0),
27923
+ message
27924
+ }
27925
+ ];
27926
+ }
27927
+ function extractOllamaGenerateOutput(result) {
27928
+ if (!isObject(result) || typeof result.response !== "string") {
27929
+ return void 0;
27930
+ }
27931
+ return [
27932
+ {
27933
+ index: 0,
27934
+ finish_reason: normalizeFinishReason(result),
27935
+ message: {
27936
+ role: "assistant",
27937
+ content: result.response,
27938
+ ...typeof result.thinking === "string" && result.thinking.length > 0 ? { reasoning: result.thinking } : {}
27939
+ }
27940
+ }
27941
+ ];
27942
+ }
27943
+ function extractOllamaEmbedOutput(result) {
27944
+ const embedding = Array.isArray(result?.embeddings) ? result.embeddings[0] : void 0;
27945
+ return Array.isArray(embedding) ? { embedding_length: embedding.length } : void 0;
27946
+ }
27947
+ function extractOllamaResponseMetadata(result) {
27948
+ return typeof result?.model === "string" ? { model: result.model } : void 0;
27949
+ }
27950
+ function extractOllamaMetrics(result) {
27951
+ const metrics = {};
27952
+ const promptTokens = result?.prompt_eval_count;
27953
+ const completionTokens = result?.eval_count;
27954
+ if (isNonNegativeNumber(promptTokens)) {
27955
+ metrics.prompt_tokens = promptTokens;
27956
+ }
27957
+ if (isNonNegativeNumber(completionTokens)) {
27958
+ metrics.completion_tokens = completionTokens;
27959
+ }
27960
+ if (isNonNegativeNumber(promptTokens) || isNonNegativeNumber(completionTokens)) {
27961
+ metrics.tokens = (isNonNegativeNumber(promptTokens) ? promptTokens : 0) + (isNonNegativeNumber(completionTokens) ? completionTokens : 0);
27962
+ }
27963
+ return metrics;
27964
+ }
27965
+ function aggregateOllamaChatChunks(chunks, _result, event, _startTime) {
27966
+ const last = chunks.at(-1);
27967
+ const message = {
27968
+ role: "assistant",
27969
+ content: chunks.map((chunk) => chunk.message?.content ?? "").join(""),
27970
+ thinking: chunks.map((chunk) => chunk.message?.thinking ?? "").join(""),
27971
+ tool_calls: chunks.flatMap((chunk) => chunk.message?.tool_calls ?? [])
27972
+ };
27973
+ const response = {
27974
+ ...last,
27975
+ message
27976
+ };
27977
+ return {
27978
+ output: extractOllamaChatOutput(
27979
+ response,
27980
+ countOllamaToolCalls(event?.arguments?.[0]?.messages)
27981
+ ),
27982
+ metrics: extractOllamaMetrics(last ?? {}),
27983
+ metadata: extractOllamaResponseMetadata(last ?? {})
27984
+ };
27985
+ }
27986
+ function aggregateOllamaGenerateChunks(chunks, _result, _event, _startTime) {
27987
+ const last = chunks.at(-1);
27988
+ const response = {
27989
+ ...last,
27990
+ response: chunks.map((chunk) => chunk.response ?? "").join(""),
27991
+ thinking: chunks.map((chunk) => chunk.thinking ?? "").join("")
27992
+ };
27993
+ return {
27994
+ output: extractOllamaGenerateOutput(response),
27995
+ metrics: extractOllamaMetrics(last ?? {}),
27996
+ metadata: extractOllamaResponseMetadata(last ?? {})
27997
+ };
27998
+ }
27999
+
26733
28000
  // src/instrumentation/plugins/google-adk-plugin.ts
26734
28001
  var GoogleADKPlugin = class extends BasePlugin {
26735
28002
  activeRunnerSpans = /* @__PURE__ */ new Map();
@@ -30406,12 +31673,13 @@ function sanitizeLoggedValue(value, seen = /* @__PURE__ */ new WeakSet(), depth
30406
31673
  }
30407
31674
 
30408
31675
  // src/instrumentation/plugins/pi-coding-agent-plugin.ts
30409
- var piStreamPatchStates = /* @__PURE__ */ new WeakMap();
31676
+ var piAgentPatchStates = /* @__PURE__ */ new WeakMap();
31677
+ var piAgentEventSubscriptions = /* @__PURE__ */ new WeakSet();
30410
31678
  var piPromptContextStore;
30411
31679
  var PiCodingAgentPlugin = class extends BasePlugin {
30412
31680
  activePromptStates = /* @__PURE__ */ new Set();
30413
31681
  onEnable() {
30414
- this.subscribeToPrompt();
31682
+ this.interceptPrompt();
30415
31683
  }
30416
31684
  onDisable() {
30417
31685
  for (const unsubscribe of this.unsubscribers) {
@@ -30419,65 +31687,74 @@ var PiCodingAgentPlugin = class extends BasePlugin {
30419
31687
  }
30420
31688
  this.unsubscribers = [];
30421
31689
  for (const state of [...this.activePromptStates]) {
30422
- void finalizePiPromptRun(state).catch((error) => {
30423
- logInstrumentationError4("Pi Coding Agent disable cleanup", error);
30424
- });
31690
+ finishPiPromptRun(state);
30425
31691
  }
30426
31692
  }
30427
- subscribeToPrompt() {
30428
- const channel = piCodingAgentChannels.prompt.tracingChannel();
30429
- const states = /* @__PURE__ */ new WeakMap();
30430
- const unbindAutoInstrumentationSuppression = bindAutoInstrumentationSuppressionToStart(channel);
30431
- const handlers = {
30432
- start: (event) => {
30433
- const state = startPiPromptRun(event, (state2) => {
30434
- this.activePromptStates.delete(state2);
30435
- });
30436
- if (state) {
30437
- this.activePromptStates.add(state);
30438
- states.set(event, state);
30439
- }
30440
- },
30441
- asyncEnd: async (event) => {
30442
- const state = states.get(event);
30443
- if (!state) {
30444
- return;
30445
- }
30446
- states.delete(event);
30447
- state.promptCallEnded = true;
30448
- if (!state.finalized && state.deferCompletionUntilTurnEnd && !state.turnEnded) {
30449
- if (!state.sawStreamFn) {
30450
- state.queued = true;
30451
- if (!state.streamPatchState.queuedPromptStates.includes(state)) {
30452
- state.streamPatchState.queuedPromptStates.push(state);
30453
- }
31693
+ interceptPrompt() {
31694
+ this.unsubscribers.push(
31695
+ piCodingAgentChannels.prompt.intercept(
31696
+ (target, thisArg, args, additional) => {
31697
+ const invokeTarget = () => Reflect.apply(target, thisArg, args);
31698
+ let state;
31699
+ try {
31700
+ state = startPiPromptRun(
31701
+ {
31702
+ ...additional,
31703
+ arguments: args,
31704
+ self: thisArg
31705
+ },
31706
+ (finalizedState) => {
31707
+ this.activePromptStates.delete(finalizedState);
31708
+ }
31709
+ );
31710
+ } catch (error) {
31711
+ logInstrumentationError4("Pi Coding Agent prompt start", error);
30454
31712
  }
30455
- return;
30456
- }
30457
- await finalizePiPromptRun(state);
30458
- },
30459
- error: async (event) => {
30460
- const state = states.get(event);
30461
- if (!state) {
30462
- return;
31713
+ if (!state) {
31714
+ return runWithAutoInstrumentationSuppressed(invokeTarget);
31715
+ }
31716
+ this.activePromptStates.add(state);
31717
+ return promptContextStore().run(
31718
+ state,
31719
+ () => runWithAutoInstrumentationSuppressed(() => {
31720
+ let result;
31721
+ try {
31722
+ result = invokeTarget();
31723
+ } catch (error) {
31724
+ finishPiPromptRun(state, error);
31725
+ throw error;
31726
+ }
31727
+ return Promise.resolve(result).then(
31728
+ (value) => {
31729
+ finishPiPromptCall(state);
31730
+ return value;
31731
+ },
31732
+ (error) => {
31733
+ finishPiPromptRun(state, error);
31734
+ throw error;
31735
+ }
31736
+ );
31737
+ })
31738
+ );
30463
31739
  }
30464
- states.delete(event);
30465
- await finalizePiPromptRun(state, event.error);
30466
- }
30467
- };
30468
- channel.subscribe(handlers);
30469
- this.unsubscribers.push(() => {
30470
- unbindAutoInstrumentationSuppression?.();
30471
- channel.unsubscribe(handlers);
30472
- });
31740
+ )
31741
+ );
30473
31742
  }
30474
31743
  };
31744
+ function finishPiPromptCall(state) {
31745
+ state.promptCallEnded = true;
31746
+ if (state.deferCompletionUntilTurnEnd && !state.turnEnded) {
31747
+ return;
31748
+ }
31749
+ finishPiPromptRun(state);
31750
+ }
30475
31751
  function startPiPromptRun(event, onFinalize) {
30476
31752
  const session = extractSession(event);
30477
31753
  const agent = session?.agent;
30478
31754
  if (!session || !isPiAgent(agent)) {
30479
31755
  return void 0;
30480
31756
  }
31757
+ installPiAgentInstrumentation(agent);
30481
31758
  const metadata = {
30482
31759
  ...extractSessionMetadata(session),
30483
31760
  ...extractPromptOptionsMetadata(event.arguments[1]),
@@ -30499,9 +31776,7 @@ function startPiPromptRun(event, onFinalize) {
30499
31776
  INSTRUMENTATION_NAMES.PI_CODING_AGENT
30500
31777
  )
30501
31778
  );
30502
- const streamPatchState = installPiStreamPatch(agent);
30503
31779
  const options = event.arguments[1];
30504
- const promptText = event.arguments[0];
30505
31780
  const state = {
30506
31781
  activeLlmSpans: /* @__PURE__ */ new Set(),
30507
31782
  activeToolSpans: /* @__PURE__ */ new Map(),
@@ -30513,29 +31788,10 @@ function startPiPromptRun(event, onFinalize) {
30513
31788
  metrics: {},
30514
31789
  onFinalize,
30515
31790
  promptCallEnded: false,
30516
- ...typeof promptText === "string" ? { promptText } : {},
30517
- queued: false,
30518
31791
  span,
30519
- sawStreamFn: false,
30520
31792
  startTime: getCurrentUnixTimestamp(),
30521
- streamPatchState,
30522
31793
  turnEnded: false
30523
31794
  };
30524
- state.restorePromptContext = enterPiPromptContext(state);
30525
- streamPatchState.activePromptStates.add(state);
30526
- try {
30527
- state.unsubscribeAgent = agent.subscribe(async (agentEvent) => {
30528
- try {
30529
- await runWithAutoInstrumentationSuppressed(
30530
- () => handlePiAgentEvent(state, agentEvent)
30531
- );
30532
- } catch (error) {
30533
- logInstrumentationError4("Pi Coding Agent event", error);
30534
- }
30535
- });
30536
- } catch (error) {
30537
- logInstrumentationError4("Pi Coding Agent event subscription", error);
30538
- }
30539
31795
  return state;
30540
31796
  }
30541
31797
  function extractSession(event) {
@@ -30549,111 +31805,55 @@ function promptContextStore() {
30549
31805
  piPromptContextStore ??= isomorph_default.newAsyncLocalStorage();
30550
31806
  return piPromptContextStore;
30551
31807
  }
30552
- function currentPromptContextFrames() {
30553
- return promptContextStore().getStore()?.frames ?? [];
30554
- }
30555
31808
  function currentPiPromptState() {
30556
- const frames = currentPromptContextFrames();
30557
- return frames[frames.length - 1]?.state;
30558
- }
30559
- function enterPiPromptContext(state) {
30560
- const frame = {
30561
- id: /* @__PURE__ */ Symbol("braintrust.pi-coding-agent.prompt"),
30562
- state
30563
- };
30564
- promptContextStore().enterWith({
30565
- frames: [...currentPromptContextFrames(), frame]
30566
- });
30567
- return () => {
30568
- const frames = currentPromptContextFrames().filter(
30569
- (candidate) => candidate.id !== frame.id
30570
- );
30571
- promptContextStore().enterWith(frames.length > 0 ? { frames } : void 0);
30572
- };
30573
- }
30574
- function installPiStreamPatch(agent) {
30575
- const existing = piStreamPatchStates.get(agent);
30576
- if (existing) {
30577
- if (agent.streamFn !== existing.wrappedStreamFn) {
30578
- debugLogger.debug(
30579
- "Pi Coding Agent streamFn changed while Braintrust instrumentation was active; preserving existing patch state."
30580
- );
30581
- }
30582
- return existing;
30583
- }
30584
- const patchState = {
30585
- activePromptStates: /* @__PURE__ */ new Set(),
30586
- agent,
30587
- originalStreamFn: agent.streamFn,
30588
- queuedPromptStates: [],
30589
- wrappedStreamFn: agent.streamFn
30590
- };
30591
- patchState.wrappedStreamFn = makeSharedInstrumentedStreamFn(patchState);
30592
- agent.streamFn = patchState.wrappedStreamFn;
30593
- piStreamPatchStates.set(agent, patchState);
30594
- return patchState;
30595
- }
30596
- function resolveStreamPromptState(patchState, context) {
30597
- let lastUserText;
30598
- if (Array.isArray(context.messages)) {
30599
- for (let i = context.messages.length - 1; i >= 0; i--) {
30600
- const message = context.messages[i];
30601
- if (isPiUserMessage(message)) {
30602
- if (typeof message.content === "string") {
30603
- lastUserText = message.content;
30604
- } else {
30605
- lastUserText = message.content.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
30606
- }
30607
- break;
30608
- }
30609
- }
30610
- }
30611
- if (lastUserText !== void 0) {
30612
- const queuedMatch = patchState.queuedPromptStates.find(
30613
- (state) => state.promptText === lastUserText
30614
- );
30615
- if (queuedMatch) {
30616
- return queuedMatch;
30617
- }
30618
- const matches = [...patchState.activePromptStates].filter(
30619
- (state) => state.promptText === lastUserText
31809
+ return promptContextStore().getStore();
31810
+ }
31811
+ function installPiAgentInstrumentation(agent) {
31812
+ const existing = piAgentPatchStates.get(agent);
31813
+ if (!existing || agent.streamFn !== existing.wrappedStreamFn) {
31814
+ const patchState = {
31815
+ originalStreamFn: agent.streamFn,
31816
+ wrappedStreamFn: agent.streamFn
31817
+ };
31818
+ patchState.wrappedStreamFn = makeInstrumentedStreamFn(
31819
+ agent,
31820
+ patchState.originalStreamFn
30620
31821
  );
30621
- if (matches.length === 1) {
30622
- return matches[0];
30623
- }
31822
+ agent.streamFn = patchState.wrappedStreamFn;
31823
+ piAgentPatchStates.set(agent, patchState);
30624
31824
  }
30625
- const contextState = currentPiPromptState();
30626
- if (contextState && patchState.activePromptStates.has(contextState) && (!contextState.queued || lastUserText !== void 0 && contextState.promptText === lastUserText)) {
30627
- return contextState;
31825
+ if (piAgentEventSubscriptions.has(agent)) {
31826
+ return;
30628
31827
  }
30629
- if (patchState.activePromptStates.size === 1) {
30630
- return [...patchState.activePromptStates][0];
31828
+ try {
31829
+ agent.subscribe(async (event) => {
31830
+ const state = currentPiPromptState();
31831
+ if (!state || state.agent !== agent || state.finalized) {
31832
+ return;
31833
+ }
31834
+ try {
31835
+ await runWithAutoInstrumentationSuppressed(
31836
+ () => handlePiAgentEvent(state, event)
31837
+ );
31838
+ } catch (error) {
31839
+ logInstrumentationError4("Pi Coding Agent event", error);
31840
+ }
31841
+ });
31842
+ piAgentEventSubscriptions.add(agent);
31843
+ } catch (error) {
31844
+ logInstrumentationError4("Pi Coding Agent event subscription", error);
30631
31845
  }
30632
- return void 0;
30633
31846
  }
30634
- function makeSharedInstrumentedStreamFn(patchState) {
31847
+ function makeInstrumentedStreamFn(agent, originalStreamFn) {
30635
31848
  return async function instrumentedPiStreamFn(model, context, options) {
30636
- const state = resolveStreamPromptState(patchState, context);
30637
- if (!state) {
30638
- const invokeOriginal = () => Reflect.apply(patchState.originalStreamFn, this, [
30639
- model,
30640
- context,
30641
- options
30642
- ]);
30643
- return patchState.activePromptStates.size > 0 ? runWithAutoInstrumentationSuppressed(invokeOriginal) : invokeOriginal();
31849
+ const invokeOriginal = () => Reflect.apply(originalStreamFn, this, [model, context, options]);
31850
+ const state = currentPiPromptState();
31851
+ if (!state || state.agent !== agent || state.finalized) {
31852
+ return invokeOriginal();
30644
31853
  }
30645
- state.sawStreamFn = true;
30646
- removeQueuedPromptState(state);
30647
- state.streamPatchState.eventPromptState = state;
30648
31854
  const llmState = await startPiLlmSpan(state, model, context, options);
30649
31855
  try {
30650
- const stream = await runWithAutoInstrumentationSuppressed(
30651
- () => Reflect.apply(patchState.originalStreamFn, this, [
30652
- model,
30653
- context,
30654
- options
30655
- ])
30656
- );
31856
+ const stream = await runWithAutoInstrumentationSuppressed(invokeOriginal);
30657
31857
  return patchAssistantMessageStream(stream, state, llmState);
30658
31858
  } catch (error) {
30659
31859
  finishPiLlmSpan(state, llmState, void 0, error);
@@ -30800,13 +32000,6 @@ async function handlePiAgentEvent(state, event) {
30800
32000
  if (state.finalized) {
30801
32001
  return;
30802
32002
  }
30803
- const eventPromptState = state.streamPatchState.eventPromptState;
30804
- if (eventPromptState && eventPromptState !== state) {
30805
- return;
30806
- }
30807
- if (!eventPromptState && (state.queued || state.streamPatchState.activePromptStates.size > 1 && currentPiPromptState() !== state)) {
30808
- return;
30809
- }
30810
32003
  switch (event.type) {
30811
32004
  case "message_end":
30812
32005
  if (isPiAssistantMessage(event.message)) {
@@ -30821,11 +32014,8 @@ async function handlePiAgentEvent(state, event) {
30821
32014
  addMetrics(state.metrics, extractUsageMetrics2(event.message.usage));
30822
32015
  }
30823
32016
  }
30824
- if (state.streamPatchState.eventPromptState === state) {
30825
- state.streamPatchState.eventPromptState = void 0;
30826
- }
30827
32017
  if (state.promptCallEnded && state.deferCompletionUntilTurnEnd) {
30828
- await finalizePiPromptRun(state);
32018
+ finishPiPromptRun(state);
30829
32019
  }
30830
32020
  return;
30831
32021
  case "tool_execution_start":
@@ -30898,19 +32088,13 @@ function finishPiToolSpan(state, event) {
30898
32088
  }
30899
32089
  }
30900
32090
  }
30901
- async function finalizePiPromptRun(state, error) {
32091
+ function finishPiPromptRun(state, error) {
30902
32092
  if (state.finalized) {
30903
32093
  return;
30904
32094
  }
30905
32095
  state.finalized = true;
30906
32096
  state.onFinalize?.(state);
30907
- restorePiStreamFn(state);
30908
- try {
30909
- state.unsubscribeAgent?.();
30910
- } catch (unsubscribeError) {
30911
- logInstrumentationError4("Pi Coding Agent unsubscribe", unsubscribeError);
30912
- }
30913
- await finishOpenLlmSpans(state, error);
32097
+ finishOpenLlmSpans(state, error);
30914
32098
  finishOpenToolSpans(state, error);
30915
32099
  const metadata = {
30916
32100
  ...state.metadata,
@@ -30927,34 +32111,14 @@ async function finalizePiPromptRun(state, error) {
30927
32111
  output: state.output
30928
32112
  });
30929
32113
  } finally {
30930
- state.span.end();
30931
- }
30932
- }
30933
- function restorePiStreamFn(state) {
30934
- const patchState = state.streamPatchState;
30935
- patchState.activePromptStates.delete(state);
30936
- removeQueuedPromptState(state);
30937
- if (patchState.eventPromptState === state) {
30938
- patchState.eventPromptState = void 0;
30939
- }
30940
- state.restorePromptContext?.();
30941
- if (patchState.activePromptStates.size > 0) {
30942
- return;
30943
- }
30944
- if (patchState.agent.streamFn === patchState.wrappedStreamFn) {
30945
- patchState.agent.streamFn = patchState.originalStreamFn;
30946
- }
30947
- piStreamPatchStates.delete(patchState.agent);
30948
- }
30949
- function removeQueuedPromptState(state) {
30950
- state.queued = false;
30951
- const queuedPromptStates = state.streamPatchState.queuedPromptStates;
30952
- const index = queuedPromptStates.indexOf(state);
30953
- if (index >= 0) {
30954
- queuedPromptStates.splice(index, 1);
32114
+ try {
32115
+ state.span.end();
32116
+ } catch (endError) {
32117
+ logInstrumentationError4("Pi Coding Agent prompt end", endError);
32118
+ }
30955
32119
  }
30956
32120
  }
30957
- async function finishOpenLlmSpans(state, error) {
32121
+ function finishOpenLlmSpans(state, error) {
30958
32122
  for (const llmState of [...state.activeLlmSpans]) {
30959
32123
  finishPiLlmSpan(state, llmState, void 0, error);
30960
32124
  }
@@ -31035,7 +32199,7 @@ function normalizeAssistantMessage(message) {
31035
32199
  (part) => part.type === "thinking" && !part.redacted ? [part.thinking] : []
31036
32200
  ).join("");
31037
32201
  const toolCalls = message.content.flatMap(
31038
- (part) => part.type === "toolCall" ? [normalizeToolCall(part)] : []
32202
+ (part) => part.type === "toolCall" ? [normalizeToolCall2(part)] : []
31039
32203
  );
31040
32204
  return {
31041
32205
  role: "assistant",
@@ -31070,13 +32234,13 @@ function normalizeUserContent(content) {
31070
32234
  return part;
31071
32235
  });
31072
32236
  }
31073
- function normalizeToolCall(toolCall) {
32237
+ function normalizeToolCall2(toolCall) {
31074
32238
  return {
31075
32239
  id: toolCall.id,
31076
32240
  type: "function",
31077
32241
  function: {
31078
32242
  name: toolCall.name,
31079
- arguments: stringifyArguments(toolCall.arguments)
32243
+ arguments: stringifyArguments2(toolCall.arguments)
31080
32244
  }
31081
32245
  };
31082
32246
  }
@@ -31259,7 +32423,7 @@ function cleanMetrics5(metrics) {
31259
32423
  }
31260
32424
  return cleaned;
31261
32425
  }
31262
- function stringifyArguments(value) {
32426
+ function stringifyArguments2(value) {
31263
32427
  if (typeof value === "string") {
31264
32428
  return value;
31265
32429
  }
@@ -32802,6 +33966,7 @@ var BraintrustPlugin = class extends BasePlugin {
32802
33966
  anthropicPlugin = null;
32803
33967
  aiSDKPlugin = null;
32804
33968
  claudeAgentSDKPlugin = null;
33969
+ cloudflareThinkPlugin = null;
32805
33970
  cursorSDKPlugin = null;
32806
33971
  openAIAgentsPlugin = null;
32807
33972
  googleGenAIPlugin = null;
@@ -32810,6 +33975,7 @@ var BraintrustPlugin = class extends BasePlugin {
32810
33975
  openRouterPlugin = null;
32811
33976
  openRouterAgentPlugin = null;
32812
33977
  mistralPlugin = null;
33978
+ ollamaPlugin = null;
32813
33979
  googleADKPlugin = null;
32814
33980
  coherePlugin = null;
32815
33981
  groqPlugin = null;
@@ -32849,6 +34015,10 @@ var BraintrustPlugin = class extends BasePlugin {
32849
34015
  this.claudeAgentSDKPlugin = new ClaudeAgentSDKPlugin();
32850
34016
  this.claudeAgentSDKPlugin.enable();
32851
34017
  }
34018
+ if (integrations.cloudflareThink !== false) {
34019
+ this.cloudflareThinkPlugin = new CloudflareThinkPlugin();
34020
+ this.cloudflareThinkPlugin.enable();
34021
+ }
32852
34022
  if (integrations.cursorSDK !== false && integrations.cursor !== false) {
32853
34023
  this.cursorSDKPlugin = new CursorSDKPlugin();
32854
34024
  this.cursorSDKPlugin.enable();
@@ -32879,6 +34049,10 @@ var BraintrustPlugin = class extends BasePlugin {
32879
34049
  this.mistralPlugin = new MistralPlugin();
32880
34050
  this.mistralPlugin.enable();
32881
34051
  }
34052
+ if (integrations.ollama !== false) {
34053
+ this.ollamaPlugin = new OllamaPlugin();
34054
+ this.ollamaPlugin.enable();
34055
+ }
32882
34056
  if (integrations.googleADK !== false) {
32883
34057
  this.googleADKPlugin = new GoogleADKPlugin();
32884
34058
  this.googleADKPlugin.enable();
@@ -32955,6 +34129,10 @@ var BraintrustPlugin = class extends BasePlugin {
32955
34129
  this.claudeAgentSDKPlugin.disable();
32956
34130
  this.claudeAgentSDKPlugin = null;
32957
34131
  }
34132
+ if (this.cloudflareThinkPlugin) {
34133
+ this.cloudflareThinkPlugin.disable();
34134
+ this.cloudflareThinkPlugin = null;
34135
+ }
32958
34136
  if (this.cursorSDKPlugin) {
32959
34137
  this.cursorSDKPlugin.disable();
32960
34138
  this.cursorSDKPlugin = null;
@@ -32987,6 +34165,10 @@ var BraintrustPlugin = class extends BasePlugin {
32987
34165
  this.mistralPlugin.disable();
32988
34166
  this.mistralPlugin = null;
32989
34167
  }
34168
+ if (this.ollamaPlugin) {
34169
+ this.ollamaPlugin.disable();
34170
+ this.ollamaPlugin = null;
34171
+ }
32990
34172
  if (this.googleADKPlugin) {
32991
34173
  this.googleADKPlugin.disable();
32992
34174
  this.googleADKPlugin = null;
@@ -33872,6 +35054,7 @@ export {
33872
35054
  parseCachedHeader,
33873
35055
  finalizeAnthropicTokens,
33874
35056
  extractAnthropicCacheTokens,
35057
+ collectAnthropicSession,
33875
35058
  zodToJsonSchema,
33876
35059
  currentWorkflowAgentWrapperSpan,
33877
35060
  braintrustAISDKTelemetry,