bitfab 0.54.14 → 0.57.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.
package/dist/index.cjs CHANGED
@@ -51,7 +51,7 @@ var __version__, __packageName__;
51
51
  var init_version_generated = __esm({
52
52
  "src/version.generated.ts"() {
53
53
  "use strict";
54
- __version__ = "0.54.14";
54
+ __version__ = "0.57.0";
55
55
  __packageName__ = "bitfab";
56
56
  }
57
57
  });
@@ -972,19 +972,6 @@ var init_spanOrigin = __esm({
972
972
  }
973
973
  });
974
974
 
975
- // src/unrefTimer.ts
976
- function unrefTimer(timer) {
977
- const handle = timer;
978
- if (typeof handle.unref === "function") {
979
- handle.unref();
980
- }
981
- }
982
- var init_unrefTimer = __esm({
983
- "src/unrefTimer.ts"() {
984
- "use strict";
985
- }
986
- });
987
-
988
975
  // src/simulationPlan.ts
989
976
  function planWaitSliceMs(timeoutMs) {
990
977
  return Math.min(SIMULATION_PLAN_READ_TIMEOUT_MS, Math.max(timeoutMs, 0) / 2);
@@ -1057,6 +1044,10 @@ function simulationPlanEnvDisabled() {
1057
1044
  function missingSimulationPlanEndpoint(error) {
1058
1045
  return error instanceof BitfabError && error.status === 404;
1059
1046
  }
1047
+ function isTraceRootSpan(payload) {
1048
+ const parentId = rawSpanOf(payload)?.parent_id;
1049
+ return parentId === void 0 || parentId === null;
1050
+ }
1060
1051
  var SIMULATION_PLAN_REFRESH_MS, SIMULATION_PLAN_RETRY_MS, SIMULATION_PLAN_READ_TIMEOUT_MS, SIMULATION_PLAN_MAX_HELD, DISABLE_SIMULATION_PLAN_ENV, ROOT_TRACE_FUNCTION_KEY_FIELD, CONTENT_OFF_KEY, CONTENT_KEYS, SimulationPlan;
1061
1052
  var init_simulationPlan = __esm({
1062
1053
  "src/simulationPlan.ts"() {
@@ -1065,7 +1056,6 @@ var init_simulationPlan = __esm({
1065
1056
  init_policyRefresh();
1066
1057
  init_readEnv();
1067
1058
  init_spanOrigin();
1068
- init_unrefTimer();
1069
1059
  init_warnOnce();
1070
1060
  SIMULATION_PLAN_REFRESH_MS = 6e4;
1071
1061
  SIMULATION_PLAN_RETRY_MS = 1e4;
@@ -1080,14 +1070,16 @@ var init_simulationPlan = __esm({
1080
1070
  this.source = source;
1081
1071
  this.enabled = enabled;
1082
1072
  this.policyRefresh = new PolicyRefresh();
1083
- this.retryPending = false;
1084
- this.waitingForFirstLoad = 0;
1073
+ this.firstReadOver = false;
1074
+ this.rootWaitOver = false;
1085
1075
  this.held = [];
1086
- this.droppedWhileHolding = 0;
1087
1076
  this.stopped = false;
1088
1077
  this.loaded = new Promise((resolve) => {
1089
1078
  this.resolveLoaded = resolve;
1090
1079
  });
1080
+ this.firstReadEnded = new Promise((resolve) => {
1081
+ this.resolveFirstReadEnded = resolve;
1082
+ });
1091
1083
  }
1092
1084
  disabled() {
1093
1085
  return !this.enabled || simulationPlanEnvDisabled();
@@ -1105,19 +1097,21 @@ var init_simulationPlan = __esm({
1105
1097
  "sim-plan-unreadable",
1106
1098
  "the sim plan response was not understood"
1107
1099
  );
1108
- return;
1100
+ } else {
1101
+ this.markLoaded(parsed);
1109
1102
  }
1110
- this.markLoaded(parsed);
1103
+ this.endFirstRead();
1111
1104
  },
1112
1105
  (error) => {
1113
1106
  if (missingSimulationPlanEndpoint(error)) {
1114
1107
  this.markLoaded(/* @__PURE__ */ new Map());
1115
- return;
1108
+ } else {
1109
+ this.scheduleRetry(
1110
+ "sim-plan-unavailable",
1111
+ `could not read the sim plan: ${error instanceof Error ? error.message : String(error)}`
1112
+ );
1116
1113
  }
1117
- this.scheduleRetry(
1118
- "sim-plan-unavailable",
1119
- `could not read the sim plan: ${error instanceof Error ? error.message : String(error)}`
1120
- );
1114
+ this.endFirstRead();
1121
1115
  }
1122
1116
  );
1123
1117
  }
@@ -1127,11 +1121,8 @@ var init_simulationPlan = __esm({
1127
1121
  }
1128
1122
  this.contentOffByKey = parsed;
1129
1123
  this.policyRefresh.hold(SIMULATION_PLAN_REFRESH_MS);
1130
- this.retryPending = false;
1131
- this.clearRetryTimer();
1132
1124
  this.resolveLoaded?.();
1133
1125
  this.resolveLoaded = void 0;
1134
- this.releaseHeld();
1135
1126
  }
1136
1127
  scheduleRetry(warnKey, reason) {
1137
1128
  if (this.stopped) {
@@ -1141,39 +1132,50 @@ var init_simulationPlan = __esm({
1141
1132
  if (this.contentOffByKey !== void 0) {
1142
1133
  return;
1143
1134
  }
1144
- this.retryPending = true;
1145
1135
  warnOnce(
1146
1136
  warnKey,
1147
- `${reason}. Spans are held until the first read succeeds; while a record is held it is retried every ${SIMULATION_PLAN_RETRY_MS / 1e3}s, otherwise on the next span.`
1137
+ `${reason}. Until a read succeeds, spans are sent without their inputs and outputs, except each trace's root span and spans recorded by a framework integration. The read is retried on the next span after ${SIMULATION_PLAN_RETRY_MS / 1e3}s.`
1148
1138
  );
1149
- this.armRetry();
1150
1139
  }
1151
- holdsWork() {
1152
- return this.held.length > 0 || this.waitingForFirstLoad > 0;
1153
- }
1154
- armRetry() {
1155
- if (this.stopped || !this.retryPending || this.retryTimer !== void 0 || !this.holdsWork()) {
1156
- return;
1140
+ endFirstRead() {
1141
+ this.endRootWait();
1142
+ if (!this.firstReadOver) {
1143
+ this.firstReadOver = true;
1144
+ this.resolveFirstReadEnded?.();
1145
+ this.resolveFirstReadEnded = void 0;
1157
1146
  }
1158
- const timer = setTimeout(() => {
1159
- this.retryTimer = void 0;
1160
- this.policyRefresh.releaseHold();
1161
- this.refresh();
1162
- }, SIMULATION_PLAN_RETRY_MS);
1163
- unrefTimer(timer);
1164
- this.retryTimer = timer;
1147
+ this.releaseHeld();
1165
1148
  }
1166
- clearRetryTimer() {
1167
- if (this.retryTimer !== void 0) {
1168
- clearTimeout(this.retryTimer);
1169
- this.retryTimer = void 0;
1149
+ endRootWait() {
1150
+ this.rootWaitOver = true;
1151
+ if (this.rootWaitTimer !== void 0) {
1152
+ clearTimeout(this.rootWaitTimer);
1153
+ this.rootWaitTimer = void 0;
1170
1154
  }
1155
+ this.resolveRootWait?.();
1156
+ this.resolveRootWait = void 0;
1171
1157
  }
1172
- syncRetryTimer() {
1173
- if (this.holdsWork()) {
1174
- return;
1158
+ waitForFirstRead() {
1159
+ this.refresh();
1160
+ if (this.rootWaitOver || this.disabled() || !this.holdingForFirstRead()) {
1161
+ return void 0;
1175
1162
  }
1176
- this.clearRetryTimer();
1163
+ if (this.rootWait === void 0) {
1164
+ this.rootWait = new Promise((resolve) => {
1165
+ this.resolveRootWait = resolve;
1166
+ });
1167
+ this.rootWaitTimer = setTimeout(
1168
+ () => this.endRootWait(),
1169
+ SIMULATION_PLAN_READ_TIMEOUT_MS
1170
+ );
1171
+ }
1172
+ return this.rootWait;
1173
+ }
1174
+ holdingForFirstRead() {
1175
+ return this.contentOffByKey === void 0 && !this.firstReadOver && !this.stopped;
1176
+ }
1177
+ unavailableNow() {
1178
+ return !this.disabled() && this.contentOffByKey === void 0 && this.firstReadOver;
1177
1179
  }
1178
1180
  firstLoad() {
1179
1181
  if (this.contentOffByKey !== void 0 || this.stopped || this.disabled()) {
@@ -1183,19 +1185,20 @@ var init_simulationPlan = __esm({
1183
1185
  }
1184
1186
  send(payload, traceFunctionKey, submit) {
1185
1187
  this.refresh();
1186
- if (this.stopped || this.disabled() || traceFunctionKey === void 0 || spanNameOf(payload) === void 0 || recordedByFramework(payload)) {
1188
+ if (this.disabled() || traceFunctionKey === void 0 || spanNameOf(payload) === void 0 || recordedByFramework(payload)) {
1187
1189
  submit(payload);
1188
1190
  return;
1189
1191
  }
1190
- if (this.contentOffByKey === void 0) {
1191
- this.hold({ payload, traceFunctionKey, submit });
1192
+ const entry = { payload, traceFunctionKey, submit };
1193
+ if (this.holdingForFirstRead()) {
1194
+ this.hold(entry);
1192
1195
  return;
1193
1196
  }
1194
- this.submitEntry({ payload, traceFunctionKey, submit });
1197
+ this.submitEntry(entry);
1195
1198
  }
1196
1199
  sendTrace(payload, submit) {
1197
1200
  this.refresh();
1198
- if (this.stopped || this.disabled() || this.contentOffByKey !== void 0 || !this.holdsSpanOfTrace(traceIdOf(payload))) {
1201
+ if (this.disabled() || !this.holdingForFirstRead() || !this.holdsSpanOfTrace(traceIdOf(payload))) {
1199
1202
  submit(payload);
1200
1203
  return;
1201
1204
  }
@@ -1211,17 +1214,34 @@ var init_simulationPlan = __esm({
1211
1214
  }
1212
1215
  hold(entry) {
1213
1216
  this.held.push(entry);
1217
+ let overflowed = false;
1214
1218
  while (this.held.length > SIMULATION_PLAN_MAX_HELD) {
1215
- this.held.shift();
1216
- this.droppedWhileHolding += 1;
1219
+ const oldest = this.held.shift();
1220
+ this.submitEntry(oldest);
1221
+ overflowed = true;
1222
+ }
1223
+ if (overflowed) {
1224
+ warnOnce(
1225
+ "sim-plan-held-overflow",
1226
+ `more than ${SIMULATION_PLAN_MAX_HELD} records were waiting for the sim plan, so the oldest were sent without their inputs and outputs.`
1227
+ );
1228
+ }
1229
+ }
1230
+ payloadToSend(entry) {
1231
+ if (entry.traceFunctionKey === void 0) {
1232
+ return entry.payload;
1233
+ }
1234
+ if (this.contentOffByKey !== void 0) {
1235
+ return this.apply(entry.payload, entry.traceFunctionKey);
1236
+ }
1237
+ if (recordedByFramework(entry.payload) || isTraceRootSpan(entry.payload)) {
1238
+ return entry.payload;
1217
1239
  }
1218
- this.armRetry();
1240
+ return stripContent(entry.payload);
1219
1241
  }
1220
1242
  submitEntry(entry) {
1221
1243
  try {
1222
- entry.submit(
1223
- entry.traceFunctionKey === void 0 ? entry.payload : this.apply(entry.payload, entry.traceFunctionKey)
1224
- );
1244
+ entry.submit(this.payloadToSend(entry));
1225
1245
  } catch (error) {
1226
1246
  warnOnce(
1227
1247
  "sim-plan-held-span-dropped",
@@ -1232,63 +1252,38 @@ var init_simulationPlan = __esm({
1232
1252
  releaseHeld() {
1233
1253
  const held = this.held;
1234
1254
  this.held = [];
1235
- this.syncRetryTimer();
1236
- this.warnDropped();
1237
1255
  for (const entry of held) {
1238
1256
  this.submitEntry(entry);
1239
1257
  }
1240
1258
  }
1241
- warnDropped() {
1242
- if (this.droppedWhileHolding === 0) {
1243
- return;
1244
- }
1245
- const dropped = this.droppedWhileHolding;
1246
- this.droppedWhileHolding = 0;
1247
- warnOnce(
1248
- "sim-plan-held-overflow",
1249
- `${dropped} record(s) were dropped while the sim plan was still loading; at most ${SIMULATION_PLAN_MAX_HELD} are held per client.`
1250
- );
1251
- }
1252
- warnNeverLoaded() {
1259
+ releaseHeldWithoutPlan() {
1253
1260
  if (this.held.length === 0) {
1254
1261
  return;
1255
1262
  }
1256
- warnOnce(
1257
- "sim-plan-never-loaded",
1258
- `${this.held.length} record(s) were not sent because the sim plan never loaded`
1259
- );
1263
+ if (this.contentOffByKey === void 0) {
1264
+ warnOnce(
1265
+ "sim-plan-not-loaded-at-release",
1266
+ `${this.held.length} record(s) were sent without their inputs and outputs because the sim plan had not loaded`
1267
+ );
1268
+ }
1269
+ this.releaseHeld();
1260
1270
  }
1261
1271
  async release(timeoutMs) {
1262
1272
  this.refresh();
1263
- if (this.holdsWork()) {
1264
- await this.waitForFirstLoad(timeoutMs);
1273
+ if (this.held.length > 0) {
1274
+ await this.waitForFirstReadEnd(timeoutMs);
1265
1275
  }
1266
- this.warnNeverLoaded();
1267
- this.warnDropped();
1268
- return this.held.length === 0;
1276
+ this.releaseHeldWithoutPlan();
1269
1277
  }
1270
- waitForFirstLoad(timeoutMs) {
1271
- const firstLoad = this.firstLoad();
1272
- if (firstLoad === void 0) {
1278
+ waitForFirstReadEnd(timeoutMs) {
1279
+ if (this.firstReadOver) {
1273
1280
  return Promise.resolve();
1274
1281
  }
1275
- this.waitingForFirstLoad += 1;
1276
- this.armRetry();
1277
1282
  return new Promise((resolve) => {
1278
- let settled2 = false;
1279
- const finish = () => {
1280
- if (settled2) {
1281
- return;
1282
- }
1283
- settled2 = true;
1284
- this.waitingForFirstLoad -= 1;
1285
- this.syncRetryTimer();
1286
- resolve();
1287
- };
1288
- const timer = setTimeout(finish, Math.max(timeoutMs, 0));
1289
- void firstLoad.then(() => {
1283
+ const timer = setTimeout(resolve, Math.max(timeoutMs, 0));
1284
+ void this.firstReadEnded.then(() => {
1290
1285
  clearTimeout(timer);
1291
- finish();
1286
+ resolve();
1292
1287
  });
1293
1288
  });
1294
1289
  }
@@ -1296,16 +1291,16 @@ var init_simulationPlan = __esm({
1296
1291
  if (this.stopped) {
1297
1292
  return;
1298
1293
  }
1294
+ this.releaseHeldWithoutPlan();
1299
1295
  this.stopped = true;
1300
- this.retryPending = false;
1301
- this.clearRetryTimer();
1302
- this.warnNeverLoaded();
1303
- this.held = [];
1304
- this.warnDropped();
1296
+ this.endFirstRead();
1305
1297
  }
1306
1298
  contentOff(traceFunctionKey, spanName) {
1307
1299
  return this.contentOffByKey?.get(traceFunctionKey)?.has(spanName) === true;
1308
1300
  }
1301
+ contentOffNow(traceFunctionKey, spanName) {
1302
+ return !this.stopped && !this.disabled() && this.contentOff(traceFunctionKey, spanName);
1303
+ }
1309
1304
  apply(payload, traceFunctionKey) {
1310
1305
  if (recordedByFramework(payload)) {
1311
1306
  return payload;
@@ -1436,6 +1431,19 @@ var init_transportTypes = __esm({
1436
1431
  }
1437
1432
  });
1438
1433
 
1434
+ // src/unrefTimer.ts
1435
+ function unrefTimer(timer) {
1436
+ const handle = timer;
1437
+ if (typeof handle.unref === "function") {
1438
+ handle.unref();
1439
+ }
1440
+ }
1441
+ var init_unrefTimer = __esm({
1442
+ "src/unrefTimer.ts"() {
1443
+ "use strict";
1444
+ }
1445
+ });
1446
+
1439
1447
  // src/otel.ts
1440
1448
  function readBoundedIntEnv(name, max, fallback, warnKey) {
1441
1449
  const raw = readEnv(name);
@@ -8272,6 +8280,25 @@ var BitfabVercelAiHandler = class {
8272
8280
  // src/client.ts
8273
8281
  init_warnOnce();
8274
8282
  var activeTraceStates = /* @__PURE__ */ new Map();
8283
+ function fitsTraceSpanBudget(payload) {
8284
+ if (!recordedByFramework(payload)) {
8285
+ return true;
8286
+ }
8287
+ const traceId = traceIdOf(payload);
8288
+ const spanBudget = traceId === void 0 ? void 0 : activeTraceStates.get(traceId)?.spanBudget;
8289
+ if (spanBudget === void 0) {
8290
+ return true;
8291
+ }
8292
+ if (spanBudget.spansUsed >= spanBudget.maxSpans) {
8293
+ spanBudget.truncate();
8294
+ return false;
8295
+ }
8296
+ spanBudget.spansUsed += 1;
8297
+ return true;
8298
+ }
8299
+ function rootTraceFunctionKeyOfTrace(traceId, fallback) {
8300
+ return activeTraceStates.get(traceId)?.traceFunctionKey ?? fallback;
8301
+ }
8275
8302
  function rootTraceFunctionKeyOf(payload) {
8276
8303
  const captured = payload[ROOT_TRACE_FUNCTION_KEY_FIELD];
8277
8304
  if (typeof captured === "string") {
@@ -8647,7 +8674,8 @@ function readBooleanEnv(name) {
8647
8674
  return void 0;
8648
8675
  }
8649
8676
  var DEFAULT_AUTO_TRACE_MAX_DEPTH = 30;
8650
- var DEFAULT_AUTO_TRACE_MAX_SPANS = 500;
8677
+ var DEFAULT_AUTO_TRACE_MAX_SPANS = 2048;
8678
+ var DEFAULT_AUTO_TRACE_MAX_CAPTURED_SUBTREE_SPANS = 512;
8651
8679
  var AUTO_TRACE_PROTOCOL = "ts-auto-v1";
8652
8680
  function spanOwnerName(receiver) {
8653
8681
  if (typeof receiver === "function") {
@@ -8718,6 +8746,9 @@ var Bitfab = class {
8718
8746
  this.httpClient.externalTraceTransform = (payload, submit) => this.simulationPlan.sendTrace(payload, submit);
8719
8747
  this.httpClient.releaseHeldExternalSpans = (timeoutMs) => this.simulationPlan.release(timeoutMs);
8720
8748
  this.httpClient.stopSimulationPlan = () => this.simulationPlan.stop();
8749
+ if (this.findApiKey(false) !== void 0) {
8750
+ this.simulationPlan.refresh();
8751
+ }
8721
8752
  this.datasets = new DatasetsClient(this.httpClient);
8722
8753
  this.assertionCategories = new AssertionCategoriesClient(this.httpClient);
8723
8754
  this.traces = new TracesClient(this.httpClient);
@@ -8874,10 +8905,23 @@ var Bitfab = class {
8874
8905
  options.maxDepth,
8875
8906
  DEFAULT_AUTO_TRACE_MAX_DEPTH
8876
8907
  );
8908
+ if (options.maxSpans !== void 0 && options.maxSpans < 1) {
8909
+ throw new BitfabError(
8910
+ `maxSpans must be at least 1 because the root span counts toward it (got ${options.maxSpans})`
8911
+ );
8912
+ }
8877
8913
  const maxSpans = autoTraceLimit(
8878
8914
  options.maxSpans,
8879
8915
  DEFAULT_AUTO_TRACE_MAX_SPANS
8880
8916
  );
8917
+ const maxCapturedSubtreeSpans = Math.min(
8918
+ autoTraceLimit(
8919
+ options.maxCapturedSubtreeSpans,
8920
+ DEFAULT_AUTO_TRACE_MAX_CAPTURED_SUBTREE_SPANS
8921
+ ),
8922
+ maxSpans
8923
+ );
8924
+ const rootIsAsync = fn.constructor.name === "AsyncFunction";
8881
8925
  const excluded = new Set(options.exclude ?? []);
8882
8926
  const includeWrappers = options.includeWrappers ?? false;
8883
8927
  const rootNameFor = (receiver) => options.name ?? (fn.name !== "" ? qualifiedSpanName(receiver, name) ?? name : name);
@@ -8893,7 +8937,7 @@ var Bitfab = class {
8893
8937
  spanOptions,
8894
8938
  function(...args) {
8895
8939
  self.simulationPlan.refresh();
8896
- let spansUsed = 0;
8940
+ let capturedSubtreeCount = 0;
8897
8941
  let truncated = false;
8898
8942
  const warnTruncated = () => {
8899
8943
  if (!truncated) {
@@ -8903,15 +8947,27 @@ var Bitfab = class {
8903
8947
  protocol: AUTO_TRACE_PROTOCOL,
8904
8948
  truncated: true,
8905
8949
  maxDepth,
8906
- maxSpans
8950
+ maxSpans,
8951
+ maxCapturedSubtreeSpans
8907
8952
  }
8908
8953
  });
8909
8954
  }
8910
8955
  warnOnce(
8911
8956
  `auto-trace-truncated:${traceFunctionKey}`,
8912
- `"${traceFunctionKey}" hit an automatic subtree capture limit (maxDepth=${maxDepth}, maxSpans=${maxSpans}); its trace is incomplete. Raise the limits or narrow the subtree with exclude.`
8957
+ `"${traceFunctionKey}" hit an automatic subtree capture limit (maxDepth=${maxDepth}, maxCapturedSubtreeSpans=${maxCapturedSubtreeSpans}, maxSpans=${maxSpans}); its trace is incomplete. Raise the limits, declare the calls you need with node(), or narrow the subtree with exclude.`
8913
8958
  );
8914
8959
  };
8960
+ const rootSpanStack = getSpanStack();
8961
+ const spanBudget = {
8962
+ spansUsed: 1,
8963
+ maxSpans,
8964
+ truncate: () => runWithSpanStack(rootSpanStack, warnTruncated)
8965
+ };
8966
+ const rootSpan = rootSpanStack[rootSpanStack.length - 1];
8967
+ const rootTraceState = rootSpan === void 0 ? void 0 : activeTraceStates.get(rootSpan.traceId);
8968
+ if (rootTraceState !== void 0 && rootTraceState.spanBudget === void 0) {
8969
+ rootTraceState.spanBudget = spanBudget;
8970
+ }
8915
8971
  const spanStackOf = (entry) => entry.spanStack;
8916
8972
  const openChild = (entry, childOptions, inputs, isAsync, invokeFn) => {
8917
8973
  const runInside = () => runWithAutoTraceEntry(
@@ -8940,12 +8996,25 @@ var Bitfab = class {
8940
8996
  return tracedChild(...inputs);
8941
8997
  });
8942
8998
  };
8943
- const overBudget = (entry) => {
8944
- if (entry.depth < maxDepth && spansUsed < maxSpans) {
8945
- return false;
8999
+ const rootTraceFunctionKeyOfEntry = (entry) => {
9000
+ const stack = spanStackOf(entry);
9001
+ const innermost = stack[stack.length - 1];
9002
+ return innermost === void 0 ? traceFunctionKey : rootTraceFunctionKeyOfTrace(innermost.traceId, traceFunctionKey);
9003
+ };
9004
+ const recordsSpan = (entry, spanName, declared) => {
9005
+ const countsTowardCapturedSubtree = !declared && !self.simulationPlan.contentOffNow(
9006
+ rootTraceFunctionKeyOfEntry(entry),
9007
+ spanName
9008
+ );
9009
+ if (entry.depth < maxDepth && spanBudget.spansUsed < maxSpans && (!countsTowardCapturedSubtree || capturedSubtreeCount < maxCapturedSubtreeSpans)) {
9010
+ spanBudget.spansUsed += 1;
9011
+ if (countsTowardCapturedSubtree) {
9012
+ capturedSubtreeCount += 1;
9013
+ }
9014
+ return true;
8946
9015
  }
8947
9016
  runWithSpanStack(spanStackOf(entry), warnTruncated);
8948
- return true;
9017
+ return false;
8949
9018
  };
8950
9019
  const autoTraceContext = {
8951
9020
  traceFunctionKey,
@@ -8959,13 +9028,13 @@ var Bitfab = class {
8959
9028
  if (nodeConfiguration?.capture === false) {
8960
9029
  return clearAutoTraceNodeConfiguration(invokeFn);
8961
9030
  }
8962
- if (overBudget(entry)) {
9031
+ const spanName = nodeConfiguration?.name ?? definition.name;
9032
+ if (!recordsSpan(entry, spanName, nodeConfiguration !== void 0)) {
8963
9033
  return invokeWithoutNode();
8964
9034
  }
8965
- spansUsed += 1;
8966
9035
  const mockOnReplay = nodeConfiguration?.mockOnReplay ?? options.mockOnReplayDefault;
8967
9036
  const childOptions = {
8968
- name: nodeConfiguration?.name ?? definition.name,
9037
+ name: spanName,
8969
9038
  type: nodeConfiguration?.type ?? "function",
8970
9039
  captureWhen: "nested",
8971
9040
  surface: "opt-out",
@@ -8992,10 +9061,9 @@ var Bitfab = class {
8992
9061
  );
8993
9062
  },
8994
9063
  invokeNestedRoot(nestedName, nestedFunctionName, inputs, invokeFn, entry, link) {
8995
- if (excluded.has(nestedName) || excluded.has(nestedFunctionName) || overBudget(entry)) {
9064
+ if (excluded.has(nestedName) || excluded.has(nestedFunctionName) || !recordsSpan(entry, nestedName, true)) {
8996
9065
  return invokeFn();
8997
9066
  }
8998
- spansUsed += 1;
8999
9067
  const childOptions = {
9000
9068
  name: nestedName,
9001
9069
  type: "function",
@@ -9022,6 +9090,12 @@ var Bitfab = class {
9022
9090
  }
9023
9091
  const enclosing = currentAutoTraceEntries();
9024
9092
  if (enclosing.length === 0) {
9093
+ const firstPlanRead = rootIsAsync ? self.simulationPlan.waitForFirstRead() : void 0;
9094
+ if (firstPlanRead !== void 0) {
9095
+ return firstPlanRead.then(
9096
+ () => tracedRoot.apply(this, args)
9097
+ );
9098
+ }
9025
9099
  return tracedRoot.apply(this, args);
9026
9100
  }
9027
9101
  const link = getReplayContext() || inSeedScope() ? void 0 : { traceFunctionKey };
@@ -9068,6 +9142,9 @@ var Bitfab = class {
9068
9142
  return autoTraceRoot;
9069
9143
  }
9070
9144
  applySimulationPlan(payload, submit) {
9145
+ if (!fitsTraceSpanBudget(payload)) {
9146
+ return;
9147
+ }
9071
9148
  this.simulationPlan.send(payload, rootTraceFunctionKeyOf(payload), submit);
9072
9149
  }
9073
9150
  /**
@@ -9102,15 +9179,24 @@ var Bitfab = class {
9102
9179
  * from the environment. Once a non-empty key is found it is cached, so an
9103
9180
  * early resolve that found nothing never poisons a later one.
9104
9181
  */
9105
- resolveApiKey() {
9182
+ findApiKey(callsKeyFunction) {
9106
9183
  if (this.resolvedApiKey !== void 0) {
9107
9184
  return this.resolvedApiKey;
9108
9185
  }
9186
+ if (typeof this.apiKeyConfig === "function" && !callsKeyFunction) {
9187
+ return void 0;
9188
+ }
9109
9189
  const fromConfig = typeof this.apiKeyConfig === "function" ? this.apiKeyConfig() : this.apiKeyConfig;
9110
9190
  const candidate = fromConfig && fromConfig.trim() !== "" ? fromConfig : readEnv2("BITFAB_API_KEY");
9111
9191
  const key = candidate && candidate.trim() !== "" ? candidate : void 0;
9112
9192
  if (key) {
9113
9193
  this.resolvedApiKey = key;
9194
+ }
9195
+ return key;
9196
+ }
9197
+ resolveApiKey() {
9198
+ const key = this.findApiKey(true);
9199
+ if (key) {
9114
9200
  return key;
9115
9201
  }
9116
9202
  if (this.strict) {
@@ -9730,7 +9816,10 @@ var Bitfab = class {
9730
9816
  self.httpClient.traceCompletion.start(traceId, spanId);
9731
9817
  startedSpan = { traceId, spanId };
9732
9818
  const functionName = fn.name !== "" ? fn.name : void 0;
9733
- const rootTraceFunctionKey = activeTraceStates.get(traceId)?.traceFunctionKey ?? traceFunctionKey;
9819
+ const rootTraceFunctionKey = rootTraceFunctionKeyOfTrace(
9820
+ traceId,
9821
+ traceFunctionKey
9822
+ );
9734
9823
  const baseSpanParams = {
9735
9824
  traceFunctionKey,
9736
9825
  rootTraceFunctionKey,
@@ -10252,14 +10341,25 @@ var Bitfab = class {
10252
10341
  ...params.testRunId && { testRunId: params.testRunId }
10253
10342
  });
10254
10343
  }
10344
+ contentOffBySimulationPlan(params) {
10345
+ if (params.rootTraceFunctionKey === void 0 || isFrameworkInstrumentation(params.instrumentation)) {
10346
+ return false;
10347
+ }
10348
+ return this.simulationPlan.contentOffNow(
10349
+ params.rootTraceFunctionKey,
10350
+ params.spanName
10351
+ ) || params.parentSpanId !== null && this.simulationPlan.unavailableNow();
10352
+ }
10255
10353
  /**
10256
10354
  * Send a wrapper span from function execution.
10257
10355
  * Internal method to record spans when using withSpan.
10258
10356
  * Queued on the client's span transport; delivery is the transport's job.
10259
10357
  */
10260
10358
  sendWrapperSpan(params) {
10261
- const serializedInputs = params.captureContent ? serializeValue(params.inputs) : void 0;
10262
- const serializedResult = params.captureContent ? serializeValue(params.result) : void 0;
10359
+ const contentOff = this.contentOffBySimulationPlan(params);
10360
+ const buildsContent = params.captureContent && !contentOff;
10361
+ const serializedInputs = buildsContent ? serializeValue(params.inputs) : void 0;
10362
+ const serializedResult = buildsContent ? serializeValue(params.result) : void 0;
10263
10363
  const externalSpan = {
10264
10364
  id: params.spanId,
10265
10365
  trace_id: params.traceId,
@@ -10310,7 +10410,8 @@ var Bitfab = class {
10310
10410
  ...params.captureContent && params.contexts && params.contexts.length > 0 && {
10311
10411
  contexts: params.contexts
10312
10412
  },
10313
- ...params.captureContent && params.prompt !== void 0 && { prompt: params.prompt }
10413
+ ...params.captureContent && params.prompt !== void 0 && { prompt: params.prompt },
10414
+ ...contentOff && { [CONTENT_OFF_KEY]: true }
10314
10415
  }
10315
10416
  };
10316
10417
  if (params.parentSpanId) {