@bitfab/sdk 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
@@ -42,7 +42,7 @@ var __version__, __packageName__;
42
42
  var init_version_generated = __esm({
43
43
  "src/version.generated.ts"() {
44
44
  "use strict";
45
- __version__ = "0.54.14";
45
+ __version__ = "0.57.0";
46
46
  __packageName__ = "@bitfab/sdk";
47
47
  }
48
48
  });
@@ -963,19 +963,6 @@ var init_spanOrigin = __esm({
963
963
  }
964
964
  });
965
965
 
966
- // src/unrefTimer.ts
967
- function unrefTimer(timer) {
968
- const handle = timer;
969
- if (typeof handle.unref === "function") {
970
- handle.unref();
971
- }
972
- }
973
- var init_unrefTimer = __esm({
974
- "src/unrefTimer.ts"() {
975
- "use strict";
976
- }
977
- });
978
-
979
966
  // src/simulationPlan.ts
980
967
  function planWaitSliceMs(timeoutMs) {
981
968
  return Math.min(SIMULATION_PLAN_READ_TIMEOUT_MS, Math.max(timeoutMs, 0) / 2);
@@ -1048,6 +1035,10 @@ function simulationPlanEnvDisabled() {
1048
1035
  function missingSimulationPlanEndpoint(error) {
1049
1036
  return error instanceof BitfabError && error.status === 404;
1050
1037
  }
1038
+ function isTraceRootSpan(payload) {
1039
+ const parentId = rawSpanOf(payload)?.parent_id;
1040
+ return parentId === void 0 || parentId === null;
1041
+ }
1051
1042
  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;
1052
1043
  var init_simulationPlan = __esm({
1053
1044
  "src/simulationPlan.ts"() {
@@ -1056,7 +1047,6 @@ var init_simulationPlan = __esm({
1056
1047
  init_policyRefresh();
1057
1048
  init_readEnv();
1058
1049
  init_spanOrigin();
1059
- init_unrefTimer();
1060
1050
  init_warnOnce();
1061
1051
  SIMULATION_PLAN_REFRESH_MS = 6e4;
1062
1052
  SIMULATION_PLAN_RETRY_MS = 1e4;
@@ -1071,14 +1061,16 @@ var init_simulationPlan = __esm({
1071
1061
  this.source = source;
1072
1062
  this.enabled = enabled;
1073
1063
  this.policyRefresh = new PolicyRefresh();
1074
- this.retryPending = false;
1075
- this.waitingForFirstLoad = 0;
1064
+ this.firstReadOver = false;
1065
+ this.rootWaitOver = false;
1076
1066
  this.held = [];
1077
- this.droppedWhileHolding = 0;
1078
1067
  this.stopped = false;
1079
1068
  this.loaded = new Promise((resolve) => {
1080
1069
  this.resolveLoaded = resolve;
1081
1070
  });
1071
+ this.firstReadEnded = new Promise((resolve) => {
1072
+ this.resolveFirstReadEnded = resolve;
1073
+ });
1082
1074
  }
1083
1075
  disabled() {
1084
1076
  return !this.enabled || simulationPlanEnvDisabled();
@@ -1096,19 +1088,21 @@ var init_simulationPlan = __esm({
1096
1088
  "sim-plan-unreadable",
1097
1089
  "the sim plan response was not understood"
1098
1090
  );
1099
- return;
1091
+ } else {
1092
+ this.markLoaded(parsed);
1100
1093
  }
1101
- this.markLoaded(parsed);
1094
+ this.endFirstRead();
1102
1095
  },
1103
1096
  (error) => {
1104
1097
  if (missingSimulationPlanEndpoint(error)) {
1105
1098
  this.markLoaded(/* @__PURE__ */ new Map());
1106
- return;
1099
+ } else {
1100
+ this.scheduleRetry(
1101
+ "sim-plan-unavailable",
1102
+ `could not read the sim plan: ${error instanceof Error ? error.message : String(error)}`
1103
+ );
1107
1104
  }
1108
- this.scheduleRetry(
1109
- "sim-plan-unavailable",
1110
- `could not read the sim plan: ${error instanceof Error ? error.message : String(error)}`
1111
- );
1105
+ this.endFirstRead();
1112
1106
  }
1113
1107
  );
1114
1108
  }
@@ -1118,11 +1112,8 @@ var init_simulationPlan = __esm({
1118
1112
  }
1119
1113
  this.contentOffByKey = parsed;
1120
1114
  this.policyRefresh.hold(SIMULATION_PLAN_REFRESH_MS);
1121
- this.retryPending = false;
1122
- this.clearRetryTimer();
1123
1115
  this.resolveLoaded?.();
1124
1116
  this.resolveLoaded = void 0;
1125
- this.releaseHeld();
1126
1117
  }
1127
1118
  scheduleRetry(warnKey, reason) {
1128
1119
  if (this.stopped) {
@@ -1132,39 +1123,50 @@ var init_simulationPlan = __esm({
1132
1123
  if (this.contentOffByKey !== void 0) {
1133
1124
  return;
1134
1125
  }
1135
- this.retryPending = true;
1136
1126
  warnOnce(
1137
1127
  warnKey,
1138
- `${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.`
1128
+ `${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.`
1139
1129
  );
1140
- this.armRetry();
1141
1130
  }
1142
- holdsWork() {
1143
- return this.held.length > 0 || this.waitingForFirstLoad > 0;
1144
- }
1145
- armRetry() {
1146
- if (this.stopped || !this.retryPending || this.retryTimer !== void 0 || !this.holdsWork()) {
1147
- return;
1131
+ endFirstRead() {
1132
+ this.endRootWait();
1133
+ if (!this.firstReadOver) {
1134
+ this.firstReadOver = true;
1135
+ this.resolveFirstReadEnded?.();
1136
+ this.resolveFirstReadEnded = void 0;
1148
1137
  }
1149
- const timer = setTimeout(() => {
1150
- this.retryTimer = void 0;
1151
- this.policyRefresh.releaseHold();
1152
- this.refresh();
1153
- }, SIMULATION_PLAN_RETRY_MS);
1154
- unrefTimer(timer);
1155
- this.retryTimer = timer;
1138
+ this.releaseHeld();
1156
1139
  }
1157
- clearRetryTimer() {
1158
- if (this.retryTimer !== void 0) {
1159
- clearTimeout(this.retryTimer);
1160
- this.retryTimer = void 0;
1140
+ endRootWait() {
1141
+ this.rootWaitOver = true;
1142
+ if (this.rootWaitTimer !== void 0) {
1143
+ clearTimeout(this.rootWaitTimer);
1144
+ this.rootWaitTimer = void 0;
1161
1145
  }
1146
+ this.resolveRootWait?.();
1147
+ this.resolveRootWait = void 0;
1162
1148
  }
1163
- syncRetryTimer() {
1164
- if (this.holdsWork()) {
1165
- return;
1149
+ waitForFirstRead() {
1150
+ this.refresh();
1151
+ if (this.rootWaitOver || this.disabled() || !this.holdingForFirstRead()) {
1152
+ return void 0;
1166
1153
  }
1167
- this.clearRetryTimer();
1154
+ if (this.rootWait === void 0) {
1155
+ this.rootWait = new Promise((resolve) => {
1156
+ this.resolveRootWait = resolve;
1157
+ });
1158
+ this.rootWaitTimer = setTimeout(
1159
+ () => this.endRootWait(),
1160
+ SIMULATION_PLAN_READ_TIMEOUT_MS
1161
+ );
1162
+ }
1163
+ return this.rootWait;
1164
+ }
1165
+ holdingForFirstRead() {
1166
+ return this.contentOffByKey === void 0 && !this.firstReadOver && !this.stopped;
1167
+ }
1168
+ unavailableNow() {
1169
+ return !this.disabled() && this.contentOffByKey === void 0 && this.firstReadOver;
1168
1170
  }
1169
1171
  firstLoad() {
1170
1172
  if (this.contentOffByKey !== void 0 || this.stopped || this.disabled()) {
@@ -1174,19 +1176,20 @@ var init_simulationPlan = __esm({
1174
1176
  }
1175
1177
  send(payload, traceFunctionKey, submit) {
1176
1178
  this.refresh();
1177
- if (this.stopped || this.disabled() || traceFunctionKey === void 0 || spanNameOf(payload) === void 0 || recordedByFramework(payload)) {
1179
+ if (this.disabled() || traceFunctionKey === void 0 || spanNameOf(payload) === void 0 || recordedByFramework(payload)) {
1178
1180
  submit(payload);
1179
1181
  return;
1180
1182
  }
1181
- if (this.contentOffByKey === void 0) {
1182
- this.hold({ payload, traceFunctionKey, submit });
1183
+ const entry = { payload, traceFunctionKey, submit };
1184
+ if (this.holdingForFirstRead()) {
1185
+ this.hold(entry);
1183
1186
  return;
1184
1187
  }
1185
- this.submitEntry({ payload, traceFunctionKey, submit });
1188
+ this.submitEntry(entry);
1186
1189
  }
1187
1190
  sendTrace(payload, submit) {
1188
1191
  this.refresh();
1189
- if (this.stopped || this.disabled() || this.contentOffByKey !== void 0 || !this.holdsSpanOfTrace(traceIdOf(payload))) {
1192
+ if (this.disabled() || !this.holdingForFirstRead() || !this.holdsSpanOfTrace(traceIdOf(payload))) {
1190
1193
  submit(payload);
1191
1194
  return;
1192
1195
  }
@@ -1202,17 +1205,34 @@ var init_simulationPlan = __esm({
1202
1205
  }
1203
1206
  hold(entry) {
1204
1207
  this.held.push(entry);
1208
+ let overflowed = false;
1205
1209
  while (this.held.length > SIMULATION_PLAN_MAX_HELD) {
1206
- this.held.shift();
1207
- this.droppedWhileHolding += 1;
1210
+ const oldest = this.held.shift();
1211
+ this.submitEntry(oldest);
1212
+ overflowed = true;
1213
+ }
1214
+ if (overflowed) {
1215
+ warnOnce(
1216
+ "sim-plan-held-overflow",
1217
+ `more than ${SIMULATION_PLAN_MAX_HELD} records were waiting for the sim plan, so the oldest were sent without their inputs and outputs.`
1218
+ );
1219
+ }
1220
+ }
1221
+ payloadToSend(entry) {
1222
+ if (entry.traceFunctionKey === void 0) {
1223
+ return entry.payload;
1224
+ }
1225
+ if (this.contentOffByKey !== void 0) {
1226
+ return this.apply(entry.payload, entry.traceFunctionKey);
1227
+ }
1228
+ if (recordedByFramework(entry.payload) || isTraceRootSpan(entry.payload)) {
1229
+ return entry.payload;
1208
1230
  }
1209
- this.armRetry();
1231
+ return stripContent(entry.payload);
1210
1232
  }
1211
1233
  submitEntry(entry) {
1212
1234
  try {
1213
- entry.submit(
1214
- entry.traceFunctionKey === void 0 ? entry.payload : this.apply(entry.payload, entry.traceFunctionKey)
1215
- );
1235
+ entry.submit(this.payloadToSend(entry));
1216
1236
  } catch (error) {
1217
1237
  warnOnce(
1218
1238
  "sim-plan-held-span-dropped",
@@ -1223,63 +1243,38 @@ var init_simulationPlan = __esm({
1223
1243
  releaseHeld() {
1224
1244
  const held = this.held;
1225
1245
  this.held = [];
1226
- this.syncRetryTimer();
1227
- this.warnDropped();
1228
1246
  for (const entry of held) {
1229
1247
  this.submitEntry(entry);
1230
1248
  }
1231
1249
  }
1232
- warnDropped() {
1233
- if (this.droppedWhileHolding === 0) {
1234
- return;
1235
- }
1236
- const dropped = this.droppedWhileHolding;
1237
- this.droppedWhileHolding = 0;
1238
- warnOnce(
1239
- "sim-plan-held-overflow",
1240
- `${dropped} record(s) were dropped while the sim plan was still loading; at most ${SIMULATION_PLAN_MAX_HELD} are held per client.`
1241
- );
1242
- }
1243
- warnNeverLoaded() {
1250
+ releaseHeldWithoutPlan() {
1244
1251
  if (this.held.length === 0) {
1245
1252
  return;
1246
1253
  }
1247
- warnOnce(
1248
- "sim-plan-never-loaded",
1249
- `${this.held.length} record(s) were not sent because the sim plan never loaded`
1250
- );
1254
+ if (this.contentOffByKey === void 0) {
1255
+ warnOnce(
1256
+ "sim-plan-not-loaded-at-release",
1257
+ `${this.held.length} record(s) were sent without their inputs and outputs because the sim plan had not loaded`
1258
+ );
1259
+ }
1260
+ this.releaseHeld();
1251
1261
  }
1252
1262
  async release(timeoutMs) {
1253
1263
  this.refresh();
1254
- if (this.holdsWork()) {
1255
- await this.waitForFirstLoad(timeoutMs);
1264
+ if (this.held.length > 0) {
1265
+ await this.waitForFirstReadEnd(timeoutMs);
1256
1266
  }
1257
- this.warnNeverLoaded();
1258
- this.warnDropped();
1259
- return this.held.length === 0;
1267
+ this.releaseHeldWithoutPlan();
1260
1268
  }
1261
- waitForFirstLoad(timeoutMs) {
1262
- const firstLoad = this.firstLoad();
1263
- if (firstLoad === void 0) {
1269
+ waitForFirstReadEnd(timeoutMs) {
1270
+ if (this.firstReadOver) {
1264
1271
  return Promise.resolve();
1265
1272
  }
1266
- this.waitingForFirstLoad += 1;
1267
- this.armRetry();
1268
1273
  return new Promise((resolve) => {
1269
- let settled2 = false;
1270
- const finish = () => {
1271
- if (settled2) {
1272
- return;
1273
- }
1274
- settled2 = true;
1275
- this.waitingForFirstLoad -= 1;
1276
- this.syncRetryTimer();
1277
- resolve();
1278
- };
1279
- const timer = setTimeout(finish, Math.max(timeoutMs, 0));
1280
- void firstLoad.then(() => {
1274
+ const timer = setTimeout(resolve, Math.max(timeoutMs, 0));
1275
+ void this.firstReadEnded.then(() => {
1281
1276
  clearTimeout(timer);
1282
- finish();
1277
+ resolve();
1283
1278
  });
1284
1279
  });
1285
1280
  }
@@ -1287,16 +1282,16 @@ var init_simulationPlan = __esm({
1287
1282
  if (this.stopped) {
1288
1283
  return;
1289
1284
  }
1285
+ this.releaseHeldWithoutPlan();
1290
1286
  this.stopped = true;
1291
- this.retryPending = false;
1292
- this.clearRetryTimer();
1293
- this.warnNeverLoaded();
1294
- this.held = [];
1295
- this.warnDropped();
1287
+ this.endFirstRead();
1296
1288
  }
1297
1289
  contentOff(traceFunctionKey, spanName) {
1298
1290
  return this.contentOffByKey?.get(traceFunctionKey)?.has(spanName) === true;
1299
1291
  }
1292
+ contentOffNow(traceFunctionKey, spanName) {
1293
+ return !this.stopped && !this.disabled() && this.contentOff(traceFunctionKey, spanName);
1294
+ }
1300
1295
  apply(payload, traceFunctionKey) {
1301
1296
  if (recordedByFramework(payload)) {
1302
1297
  return payload;
@@ -1427,6 +1422,19 @@ var init_transportTypes = __esm({
1427
1422
  }
1428
1423
  });
1429
1424
 
1425
+ // src/unrefTimer.ts
1426
+ function unrefTimer(timer) {
1427
+ const handle = timer;
1428
+ if (typeof handle.unref === "function") {
1429
+ handle.unref();
1430
+ }
1431
+ }
1432
+ var init_unrefTimer = __esm({
1433
+ "src/unrefTimer.ts"() {
1434
+ "use strict";
1435
+ }
1436
+ });
1437
+
1430
1438
  // src/otel.ts
1431
1439
  function readBoundedIntEnv(name, max, fallback, warnKey) {
1432
1440
  const raw = readEnv(name);
@@ -8263,6 +8271,25 @@ var BitfabVercelAiHandler = class {
8263
8271
  // src/client.ts
8264
8272
  init_warnOnce();
8265
8273
  var activeTraceStates = /* @__PURE__ */ new Map();
8274
+ function fitsTraceSpanBudget(payload) {
8275
+ if (!recordedByFramework(payload)) {
8276
+ return true;
8277
+ }
8278
+ const traceId = traceIdOf(payload);
8279
+ const spanBudget = traceId === void 0 ? void 0 : activeTraceStates.get(traceId)?.spanBudget;
8280
+ if (spanBudget === void 0) {
8281
+ return true;
8282
+ }
8283
+ if (spanBudget.spansUsed >= spanBudget.maxSpans) {
8284
+ spanBudget.truncate();
8285
+ return false;
8286
+ }
8287
+ spanBudget.spansUsed += 1;
8288
+ return true;
8289
+ }
8290
+ function rootTraceFunctionKeyOfTrace(traceId, fallback) {
8291
+ return activeTraceStates.get(traceId)?.traceFunctionKey ?? fallback;
8292
+ }
8266
8293
  function rootTraceFunctionKeyOf(payload) {
8267
8294
  const captured = payload[ROOT_TRACE_FUNCTION_KEY_FIELD];
8268
8295
  if (typeof captured === "string") {
@@ -8638,7 +8665,8 @@ function readBooleanEnv(name) {
8638
8665
  return void 0;
8639
8666
  }
8640
8667
  var DEFAULT_AUTO_TRACE_MAX_DEPTH = 30;
8641
- var DEFAULT_AUTO_TRACE_MAX_SPANS = 500;
8668
+ var DEFAULT_AUTO_TRACE_MAX_SPANS = 2048;
8669
+ var DEFAULT_AUTO_TRACE_MAX_CAPTURED_SUBTREE_SPANS = 512;
8642
8670
  var AUTO_TRACE_PROTOCOL = "ts-auto-v1";
8643
8671
  function spanOwnerName(receiver) {
8644
8672
  if (typeof receiver === "function") {
@@ -8709,6 +8737,9 @@ var Bitfab = class {
8709
8737
  this.httpClient.externalTraceTransform = (payload, submit) => this.simulationPlan.sendTrace(payload, submit);
8710
8738
  this.httpClient.releaseHeldExternalSpans = (timeoutMs) => this.simulationPlan.release(timeoutMs);
8711
8739
  this.httpClient.stopSimulationPlan = () => this.simulationPlan.stop();
8740
+ if (this.findApiKey(false) !== void 0) {
8741
+ this.simulationPlan.refresh();
8742
+ }
8712
8743
  this.datasets = new DatasetsClient(this.httpClient);
8713
8744
  this.assertionCategories = new AssertionCategoriesClient(this.httpClient);
8714
8745
  this.traces = new TracesClient(this.httpClient);
@@ -8865,10 +8896,23 @@ var Bitfab = class {
8865
8896
  options.maxDepth,
8866
8897
  DEFAULT_AUTO_TRACE_MAX_DEPTH
8867
8898
  );
8899
+ if (options.maxSpans !== void 0 && options.maxSpans < 1) {
8900
+ throw new BitfabError(
8901
+ `maxSpans must be at least 1 because the root span counts toward it (got ${options.maxSpans})`
8902
+ );
8903
+ }
8868
8904
  const maxSpans = autoTraceLimit(
8869
8905
  options.maxSpans,
8870
8906
  DEFAULT_AUTO_TRACE_MAX_SPANS
8871
8907
  );
8908
+ const maxCapturedSubtreeSpans = Math.min(
8909
+ autoTraceLimit(
8910
+ options.maxCapturedSubtreeSpans,
8911
+ DEFAULT_AUTO_TRACE_MAX_CAPTURED_SUBTREE_SPANS
8912
+ ),
8913
+ maxSpans
8914
+ );
8915
+ const rootIsAsync = fn.constructor.name === "AsyncFunction";
8872
8916
  const excluded = new Set(options.exclude ?? []);
8873
8917
  const includeWrappers = options.includeWrappers ?? false;
8874
8918
  const rootNameFor = (receiver) => options.name ?? (fn.name !== "" ? qualifiedSpanName(receiver, name) ?? name : name);
@@ -8884,7 +8928,7 @@ var Bitfab = class {
8884
8928
  spanOptions,
8885
8929
  function(...args) {
8886
8930
  self.simulationPlan.refresh();
8887
- let spansUsed = 0;
8931
+ let capturedSubtreeCount = 0;
8888
8932
  let truncated = false;
8889
8933
  const warnTruncated = () => {
8890
8934
  if (!truncated) {
@@ -8894,15 +8938,27 @@ var Bitfab = class {
8894
8938
  protocol: AUTO_TRACE_PROTOCOL,
8895
8939
  truncated: true,
8896
8940
  maxDepth,
8897
- maxSpans
8941
+ maxSpans,
8942
+ maxCapturedSubtreeSpans
8898
8943
  }
8899
8944
  });
8900
8945
  }
8901
8946
  warnOnce(
8902
8947
  `auto-trace-truncated:${traceFunctionKey}`,
8903
- `"${traceFunctionKey}" hit an automatic subtree capture limit (maxDepth=${maxDepth}, maxSpans=${maxSpans}); its trace is incomplete. Raise the limits or narrow the subtree with exclude.`
8948
+ `"${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.`
8904
8949
  );
8905
8950
  };
8951
+ const rootSpanStack = getSpanStack();
8952
+ const spanBudget = {
8953
+ spansUsed: 1,
8954
+ maxSpans,
8955
+ truncate: () => runWithSpanStack(rootSpanStack, warnTruncated)
8956
+ };
8957
+ const rootSpan = rootSpanStack[rootSpanStack.length - 1];
8958
+ const rootTraceState = rootSpan === void 0 ? void 0 : activeTraceStates.get(rootSpan.traceId);
8959
+ if (rootTraceState !== void 0 && rootTraceState.spanBudget === void 0) {
8960
+ rootTraceState.spanBudget = spanBudget;
8961
+ }
8906
8962
  const spanStackOf = (entry) => entry.spanStack;
8907
8963
  const openChild = (entry, childOptions, inputs, isAsync, invokeFn) => {
8908
8964
  const runInside = () => runWithAutoTraceEntry(
@@ -8931,12 +8987,25 @@ var Bitfab = class {
8931
8987
  return tracedChild(...inputs);
8932
8988
  });
8933
8989
  };
8934
- const overBudget = (entry) => {
8935
- if (entry.depth < maxDepth && spansUsed < maxSpans) {
8936
- return false;
8990
+ const rootTraceFunctionKeyOfEntry = (entry) => {
8991
+ const stack = spanStackOf(entry);
8992
+ const innermost = stack[stack.length - 1];
8993
+ return innermost === void 0 ? traceFunctionKey : rootTraceFunctionKeyOfTrace(innermost.traceId, traceFunctionKey);
8994
+ };
8995
+ const recordsSpan = (entry, spanName, declared) => {
8996
+ const countsTowardCapturedSubtree = !declared && !self.simulationPlan.contentOffNow(
8997
+ rootTraceFunctionKeyOfEntry(entry),
8998
+ spanName
8999
+ );
9000
+ if (entry.depth < maxDepth && spanBudget.spansUsed < maxSpans && (!countsTowardCapturedSubtree || capturedSubtreeCount < maxCapturedSubtreeSpans)) {
9001
+ spanBudget.spansUsed += 1;
9002
+ if (countsTowardCapturedSubtree) {
9003
+ capturedSubtreeCount += 1;
9004
+ }
9005
+ return true;
8937
9006
  }
8938
9007
  runWithSpanStack(spanStackOf(entry), warnTruncated);
8939
- return true;
9008
+ return false;
8940
9009
  };
8941
9010
  const autoTraceContext = {
8942
9011
  traceFunctionKey,
@@ -8950,13 +9019,13 @@ var Bitfab = class {
8950
9019
  if (nodeConfiguration?.capture === false) {
8951
9020
  return clearAutoTraceNodeConfiguration(invokeFn);
8952
9021
  }
8953
- if (overBudget(entry)) {
9022
+ const spanName = nodeConfiguration?.name ?? definition.name;
9023
+ if (!recordsSpan(entry, spanName, nodeConfiguration !== void 0)) {
8954
9024
  return invokeWithoutNode();
8955
9025
  }
8956
- spansUsed += 1;
8957
9026
  const mockOnReplay = nodeConfiguration?.mockOnReplay ?? options.mockOnReplayDefault;
8958
9027
  const childOptions = {
8959
- name: nodeConfiguration?.name ?? definition.name,
9028
+ name: spanName,
8960
9029
  type: nodeConfiguration?.type ?? "function",
8961
9030
  captureWhen: "nested",
8962
9031
  surface: "opt-out",
@@ -8983,10 +9052,9 @@ var Bitfab = class {
8983
9052
  );
8984
9053
  },
8985
9054
  invokeNestedRoot(nestedName, nestedFunctionName, inputs, invokeFn, entry, link) {
8986
- if (excluded.has(nestedName) || excluded.has(nestedFunctionName) || overBudget(entry)) {
9055
+ if (excluded.has(nestedName) || excluded.has(nestedFunctionName) || !recordsSpan(entry, nestedName, true)) {
8987
9056
  return invokeFn();
8988
9057
  }
8989
- spansUsed += 1;
8990
9058
  const childOptions = {
8991
9059
  name: nestedName,
8992
9060
  type: "function",
@@ -9013,6 +9081,12 @@ var Bitfab = class {
9013
9081
  }
9014
9082
  const enclosing = currentAutoTraceEntries();
9015
9083
  if (enclosing.length === 0) {
9084
+ const firstPlanRead = rootIsAsync ? self.simulationPlan.waitForFirstRead() : void 0;
9085
+ if (firstPlanRead !== void 0) {
9086
+ return firstPlanRead.then(
9087
+ () => tracedRoot.apply(this, args)
9088
+ );
9089
+ }
9016
9090
  return tracedRoot.apply(this, args);
9017
9091
  }
9018
9092
  const link = getReplayContext() || inSeedScope() ? void 0 : { traceFunctionKey };
@@ -9059,6 +9133,9 @@ var Bitfab = class {
9059
9133
  return autoTraceRoot;
9060
9134
  }
9061
9135
  applySimulationPlan(payload, submit) {
9136
+ if (!fitsTraceSpanBudget(payload)) {
9137
+ return;
9138
+ }
9062
9139
  this.simulationPlan.send(payload, rootTraceFunctionKeyOf(payload), submit);
9063
9140
  }
9064
9141
  /**
@@ -9093,15 +9170,24 @@ var Bitfab = class {
9093
9170
  * from the environment. Once a non-empty key is found it is cached, so an
9094
9171
  * early resolve that found nothing never poisons a later one.
9095
9172
  */
9096
- resolveApiKey() {
9173
+ findApiKey(callsKeyFunction) {
9097
9174
  if (this.resolvedApiKey !== void 0) {
9098
9175
  return this.resolvedApiKey;
9099
9176
  }
9177
+ if (typeof this.apiKeyConfig === "function" && !callsKeyFunction) {
9178
+ return void 0;
9179
+ }
9100
9180
  const fromConfig = typeof this.apiKeyConfig === "function" ? this.apiKeyConfig() : this.apiKeyConfig;
9101
9181
  const candidate = fromConfig && fromConfig.trim() !== "" ? fromConfig : readEnv2("BITFAB_API_KEY");
9102
9182
  const key = candidate && candidate.trim() !== "" ? candidate : void 0;
9103
9183
  if (key) {
9104
9184
  this.resolvedApiKey = key;
9185
+ }
9186
+ return key;
9187
+ }
9188
+ resolveApiKey() {
9189
+ const key = this.findApiKey(true);
9190
+ if (key) {
9105
9191
  return key;
9106
9192
  }
9107
9193
  if (this.strict) {
@@ -9721,7 +9807,10 @@ var Bitfab = class {
9721
9807
  self.httpClient.traceCompletion.start(traceId, spanId);
9722
9808
  startedSpan = { traceId, spanId };
9723
9809
  const functionName = fn.name !== "" ? fn.name : void 0;
9724
- const rootTraceFunctionKey = activeTraceStates.get(traceId)?.traceFunctionKey ?? traceFunctionKey;
9810
+ const rootTraceFunctionKey = rootTraceFunctionKeyOfTrace(
9811
+ traceId,
9812
+ traceFunctionKey
9813
+ );
9725
9814
  const baseSpanParams = {
9726
9815
  traceFunctionKey,
9727
9816
  rootTraceFunctionKey,
@@ -10243,14 +10332,25 @@ var Bitfab = class {
10243
10332
  ...params.testRunId && { testRunId: params.testRunId }
10244
10333
  });
10245
10334
  }
10335
+ contentOffBySimulationPlan(params) {
10336
+ if (params.rootTraceFunctionKey === void 0 || isFrameworkInstrumentation(params.instrumentation)) {
10337
+ return false;
10338
+ }
10339
+ return this.simulationPlan.contentOffNow(
10340
+ params.rootTraceFunctionKey,
10341
+ params.spanName
10342
+ ) || params.parentSpanId !== null && this.simulationPlan.unavailableNow();
10343
+ }
10246
10344
  /**
10247
10345
  * Send a wrapper span from function execution.
10248
10346
  * Internal method to record spans when using withSpan.
10249
10347
  * Queued on the client's span transport; delivery is the transport's job.
10250
10348
  */
10251
10349
  sendWrapperSpan(params) {
10252
- const serializedInputs = params.captureContent ? serializeValue(params.inputs) : void 0;
10253
- const serializedResult = params.captureContent ? serializeValue(params.result) : void 0;
10350
+ const contentOff = this.contentOffBySimulationPlan(params);
10351
+ const buildsContent = params.captureContent && !contentOff;
10352
+ const serializedInputs = buildsContent ? serializeValue(params.inputs) : void 0;
10353
+ const serializedResult = buildsContent ? serializeValue(params.result) : void 0;
10254
10354
  const externalSpan = {
10255
10355
  id: params.spanId,
10256
10356
  trace_id: params.traceId,
@@ -10301,7 +10401,8 @@ var Bitfab = class {
10301
10401
  ...params.captureContent && params.contexts && params.contexts.length > 0 && {
10302
10402
  contexts: params.contexts
10303
10403
  },
10304
- ...params.captureContent && params.prompt !== void 0 && { prompt: params.prompt }
10404
+ ...params.captureContent && params.prompt !== void 0 && { prompt: params.prompt },
10405
+ ...contentOff && { [CONTENT_OFF_KEY]: true }
10305
10406
  }
10306
10407
  };
10307
10408
  if (params.parentSpanId) {