@bitfab/sdk 0.54.13 → 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.
Files changed (34) hide show
  1. package/dist/{chunk-M7HWCJVO.js → chunk-J4DNPFBL.js} +9 -3
  2. package/dist/chunk-J4DNPFBL.js.map +1 -0
  3. package/dist/{chunk-WDSOB57F.js → chunk-JVOGFX5N.js} +136 -110
  4. package/dist/chunk-JVOGFX5N.js.map +1 -0
  5. package/dist/{chunk-IOLVW3PT.js → chunk-MAJCKG4R.js} +143 -34
  6. package/dist/chunk-MAJCKG4R.js.map +1 -0
  7. package/dist/{chunk-N5T6NDR3.js → chunk-PC3PWLYF.js} +29 -17
  8. package/dist/chunk-PC3PWLYF.js.map +1 -0
  9. package/dist/{chunk-BUGCOUK6.js → chunk-XQIZ335Q.js} +37 -7
  10. package/dist/chunk-XQIZ335Q.js.map +1 -0
  11. package/dist/{http-4W2BM7GE.js → http-R4LWGTGX.js} +2 -2
  12. package/dist/{http-Y6UGNXSO.js → http-RSLWOETW.js} +2 -2
  13. package/dist/index.cjs +276 -146
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +12 -5
  16. package/dist/index.d.ts +12 -5
  17. package/dist/index.js +3 -3
  18. package/dist/node.cjs +276 -146
  19. package/dist/node.cjs.map +1 -1
  20. package/dist/node.d.cts +1 -1
  21. package/dist/node.d.ts +1 -1
  22. package/dist/node.js +3 -3
  23. package/dist/{replay-YJBLRMQD.js → replay-7AUPVIXO.js} +3 -3
  24. package/dist/replayCli.js +2 -2
  25. package/dist/seedCli.js +2 -2
  26. package/package.json +1 -1
  27. package/dist/chunk-BUGCOUK6.js.map +0 -1
  28. package/dist/chunk-IOLVW3PT.js.map +0 -1
  29. package/dist/chunk-M7HWCJVO.js.map +0 -1
  30. package/dist/chunk-N5T6NDR3.js.map +0 -1
  31. package/dist/chunk-WDSOB57F.js.map +0 -1
  32. /package/dist/{http-4W2BM7GE.js.map → http-R4LWGTGX.js.map} +0 -0
  33. /package/dist/{http-Y6UGNXSO.js.map → http-RSLWOETW.js.map} +0 -0
  34. /package/dist/{replay-YJBLRMQD.js.map → replay-7AUPVIXO.js.map} +0 -0
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.13";
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);
1208
1227
  }
1209
- this.armRetry();
1228
+ if (recordedByFramework(entry.payload) || isTraceRootSpan(entry.payload)) {
1229
+ return entry.payload;
1230
+ }
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,62 +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();
1267
+ this.releaseHeldWithoutPlan();
1259
1268
  }
1260
- waitForFirstLoad(timeoutMs) {
1261
- const firstLoad = this.firstLoad();
1262
- if (firstLoad === void 0) {
1269
+ waitForFirstReadEnd(timeoutMs) {
1270
+ if (this.firstReadOver) {
1263
1271
  return Promise.resolve();
1264
1272
  }
1265
- this.waitingForFirstLoad += 1;
1266
- this.armRetry();
1267
1273
  return new Promise((resolve) => {
1268
- let settled2 = false;
1269
- const finish = () => {
1270
- if (settled2) {
1271
- return;
1272
- }
1273
- settled2 = true;
1274
- this.waitingForFirstLoad -= 1;
1275
- this.syncRetryTimer();
1276
- resolve();
1277
- };
1278
- const timer = setTimeout(finish, Math.max(timeoutMs, 0));
1279
- void firstLoad.then(() => {
1274
+ const timer = setTimeout(resolve, Math.max(timeoutMs, 0));
1275
+ void this.firstReadEnded.then(() => {
1280
1276
  clearTimeout(timer);
1281
- finish();
1277
+ resolve();
1282
1278
  });
1283
1279
  });
1284
1280
  }
@@ -1286,16 +1282,16 @@ var init_simulationPlan = __esm({
1286
1282
  if (this.stopped) {
1287
1283
  return;
1288
1284
  }
1285
+ this.releaseHeldWithoutPlan();
1289
1286
  this.stopped = true;
1290
- this.retryPending = false;
1291
- this.clearRetryTimer();
1292
- this.warnNeverLoaded();
1293
- this.held = [];
1294
- this.warnDropped();
1287
+ this.endFirstRead();
1295
1288
  }
1296
1289
  contentOff(traceFunctionKey, spanName) {
1297
1290
  return this.contentOffByKey?.get(traceFunctionKey)?.has(spanName) === true;
1298
1291
  }
1292
+ contentOffNow(traceFunctionKey, spanName) {
1293
+ return !this.stopped && !this.disabled() && this.contentOff(traceFunctionKey, spanName);
1294
+ }
1299
1295
  apply(payload, traceFunctionKey) {
1300
1296
  if (recordedByFramework(payload)) {
1301
1297
  return payload;
@@ -1426,6 +1422,19 @@ var init_transportTypes = __esm({
1426
1422
  }
1427
1423
  });
1428
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
+
1429
1438
  // src/otel.ts
1430
1439
  function readBoundedIntEnv(name, max, fallback, warnKey) {
1431
1440
  const raw = readEnv(name);
@@ -2078,9 +2087,14 @@ __export(http_exports, {
2078
2087
  });
2079
2088
  async function releaseHeldExternalSpans(timeoutMs) {
2080
2089
  const deadline = Date.now() + Math.max(timeoutMs, 0);
2090
+ let released = true;
2081
2091
  for (const client of [...liveHttpClients]) {
2082
- await client.releaseHeldExternalSpans?.(Math.max(0, deadline - Date.now()));
2092
+ const result = await client.releaseHeldExternalSpans?.(
2093
+ Math.max(0, deadline - Date.now())
2094
+ );
2095
+ released = result !== false && released;
2083
2096
  }
2097
+ return released;
2084
2098
  }
2085
2099
  function stopSimulationPlans() {
2086
2100
  for (const client of [...liveHttpClients]) {
@@ -2097,14 +2111,15 @@ function awaitOnExit(promise) {
2097
2111
  }
2098
2112
  async function flushTraces(timeoutMs = 5e3) {
2099
2113
  const deadline = Date.now() + Math.max(timeoutMs, 0);
2100
- await releaseHeldExternalSpans(planWaitSliceMs(timeoutMs));
2114
+ const released = await releaseHeldExternalSpans(planWaitSliceMs(timeoutMs));
2101
2115
  const requestsFlushed = await awaitPendingRequests(
2102
2116
  Math.max(0, deadline - Date.now())
2103
2117
  );
2118
+ const releasedAfterDeferred = await releaseHeldExternalSpans(0);
2104
2119
  const transportsFlushed = await flushTraceTransports(
2105
2120
  Math.max(0, deadline - Date.now())
2106
2121
  );
2107
- return requestsFlushed && transportsFlushed;
2122
+ return released && releasedAfterDeferred && requestsFlushed && transportsFlushed;
2108
2123
  }
2109
2124
  async function awaitPendingRequests(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS2) {
2110
2125
  await replayContextReady.catch(() => {
@@ -2514,13 +2529,17 @@ var init_http = __esm({
2514
2529
  */
2515
2530
  async settleDeferredWork(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS2) {
2516
2531
  const deadline = Date.now() + Math.max(timeoutMs, 0);
2517
- await this.releaseHeldExternalSpans?.(planWaitSliceMs(timeoutMs));
2532
+ const released = await this.releaseHeldExternalSpans?.(
2533
+ planWaitSliceMs(timeoutMs)
2534
+ );
2518
2535
  await replayContextReady.catch(() => {
2519
2536
  });
2520
- return waitForPromises(
2537
+ const settled2 = await waitForPromises(
2521
2538
  Array.from(this.deferredWork),
2522
2539
  Math.max(0, deadline - Date.now())
2523
2540
  );
2541
+ const releasedAfterDeferred = await this.releaseHeldExternalSpans?.(0);
2542
+ return released !== false && releasedAfterDeferred !== false && settled2;
2524
2543
  }
2525
2544
  /**
2526
2545
  * Wait for spans queued by this client to be delivered, within one deadline.
@@ -2545,7 +2564,9 @@ var init_http = __esm({
2545
2564
  }
2546
2565
  const deadline = Date.now() + Math.max(timeoutMs, 0);
2547
2566
  this.closing = (async () => {
2548
- await this.releaseHeldExternalSpans?.(planWaitSliceMs(timeoutMs));
2567
+ const released = await this.releaseHeldExternalSpans?.(
2568
+ planWaitSliceMs(timeoutMs)
2569
+ );
2549
2570
  this.stopSimulationPlan?.();
2550
2571
  this.externalSpanTransform = void 0;
2551
2572
  this.externalTraceTransform = void 0;
@@ -2559,7 +2580,7 @@ var init_http = __esm({
2559
2580
  const transport = this.traceTransport;
2560
2581
  this.traceTransport = void 0;
2561
2582
  const shutdownOk = await transport?.shutdown(Math.max(0, deadline - Date.now())) ?? true;
2562
- return settled2 && shutdownOk;
2583
+ return released !== false && settled2 && shutdownOk;
2563
2584
  })();
2564
2585
  return this.closing;
2565
2586
  }
@@ -3993,6 +4014,12 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
3993
4014
  }
3994
4015
  }
3995
4016
  const attempts = resolveAttempts(options?.attempts);
4017
+ const maxConcurrency = options?.maxConcurrency ?? 10;
4018
+ if (!Number.isInteger(maxConcurrency) || maxConcurrency < 1) {
4019
+ throw new BitfabError(
4020
+ `maxConcurrency must be a positive integer (got ${maxConcurrency}).`
4021
+ );
4022
+ }
3996
4023
  await replayContextReady;
3997
4024
  let codeChangeDescription = options?.codeChangeDescription;
3998
4025
  let codeChangeFiles = options?.codeChangeFiles;
@@ -4039,7 +4066,6 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4039
4066
  }
4040
4067
  }
4041
4068
  const mockStrategy = options?.mock ?? "marked";
4042
- const maxConcurrency = options?.maxConcurrency ?? 10;
4043
4069
  const fullTestRunUrl = `${serviceUrl}${testRunUrl}`;
4044
4070
  const resolvedOverrides = [
4045
4071
  ...normalizeMockOverrides(options?.mockOverride),
@@ -4136,6 +4162,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4136
4162
  originalDurationMs: item.originalDurationMs,
4137
4163
  originalTokens: item.originalTokens,
4138
4164
  originalModel: item.originalModel,
4165
+ ingestionType: item.ingestionType,
4139
4166
  tokens: item.tokens,
4140
4167
  model: item.model,
4141
4168
  dbSnapshotRef: item.dbSnapshotRef,
@@ -5969,6 +5996,14 @@ var LabelsClient = class {
5969
5996
  );
5970
5997
  return response.labels;
5971
5998
  }
5999
+ /** Withhold a verdict for a trace or replay attempt whose check did not run. */
6000
+ async skip(target, testRunId) {
6001
+ return this.save({ ...target, skip: true }, testRunId);
6002
+ }
6003
+ /** Clear a previous verdict for a trace, replay attempt, or assertion. */
6004
+ async archive(target, testRunId) {
6005
+ return this.save({ ...target, archive: true }, testRunId);
6006
+ }
5972
6007
  async saveHuman(update) {
5973
6008
  const [outcome] = await this.saveHumanAll([update]);
5974
6009
  return outcome;
@@ -6887,6 +6922,15 @@ var BitfabLangGraphIntegration = class {
6887
6922
  // src/client.ts
6888
6923
  init_mockOverride();
6889
6924
 
6925
+ // src/streamFinalizationError.ts
6926
+ var StreamFinalizationError = class extends Error {
6927
+ constructor(message, output) {
6928
+ super(message);
6929
+ this.output = output;
6930
+ this.name = "StreamFinalizationError";
6931
+ }
6932
+ };
6933
+
6890
6934
  // src/openaiAgentSdk.ts
6891
6935
  var BitfabOpenAIAgentHandler = class {
6892
6936
  constructor(config) {
@@ -6926,7 +6970,11 @@ var BitfabOpenAIAgentHandler = class {
6926
6970
  if (isStreaming && res?.completed) {
6927
6971
  try {
6928
6972
  await res.completed;
6929
- } catch {
6973
+ } catch (error) {
6974
+ throw new StreamFinalizationError(
6975
+ error instanceof Error ? error.message : String(error),
6976
+ res.finalOutput
6977
+ );
6930
6978
  }
6931
6979
  }
6932
6980
  return res?.finalOutput;
@@ -7684,17 +7732,6 @@ function runWithSeedContext(ctx, fn) {
7684
7732
  init_serialize();
7685
7733
  init_simulationPlan();
7686
7734
  init_spanOrigin();
7687
-
7688
- // src/streamFinalizationError.ts
7689
- var StreamFinalizationError = class extends Error {
7690
- constructor(message, output) {
7691
- super(message);
7692
- this.output = output;
7693
- this.name = "StreamFinalizationError";
7694
- }
7695
- };
7696
-
7697
- // src/client.ts
7698
7735
  init_traceMetadata();
7699
7736
 
7700
7737
  // src/traces.ts
@@ -8234,6 +8271,25 @@ var BitfabVercelAiHandler = class {
8234
8271
  // src/client.ts
8235
8272
  init_warnOnce();
8236
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
+ }
8237
8293
  function rootTraceFunctionKeyOf(payload) {
8238
8294
  const captured = payload[ROOT_TRACE_FUNCTION_KEY_FIELD];
8239
8295
  if (typeof captured === "string") {
@@ -8609,7 +8665,8 @@ function readBooleanEnv(name) {
8609
8665
  return void 0;
8610
8666
  }
8611
8667
  var DEFAULT_AUTO_TRACE_MAX_DEPTH = 30;
8612
- 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;
8613
8670
  var AUTO_TRACE_PROTOCOL = "ts-auto-v1";
8614
8671
  function spanOwnerName(receiver) {
8615
8672
  if (typeof receiver === "function") {
@@ -8680,6 +8737,9 @@ var Bitfab = class {
8680
8737
  this.httpClient.externalTraceTransform = (payload, submit) => this.simulationPlan.sendTrace(payload, submit);
8681
8738
  this.httpClient.releaseHeldExternalSpans = (timeoutMs) => this.simulationPlan.release(timeoutMs);
8682
8739
  this.httpClient.stopSimulationPlan = () => this.simulationPlan.stop();
8740
+ if (this.findApiKey(false) !== void 0) {
8741
+ this.simulationPlan.refresh();
8742
+ }
8683
8743
  this.datasets = new DatasetsClient(this.httpClient);
8684
8744
  this.assertionCategories = new AssertionCategoriesClient(this.httpClient);
8685
8745
  this.traces = new TracesClient(this.httpClient);
@@ -8836,10 +8896,23 @@ var Bitfab = class {
8836
8896
  options.maxDepth,
8837
8897
  DEFAULT_AUTO_TRACE_MAX_DEPTH
8838
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
+ }
8839
8904
  const maxSpans = autoTraceLimit(
8840
8905
  options.maxSpans,
8841
8906
  DEFAULT_AUTO_TRACE_MAX_SPANS
8842
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";
8843
8916
  const excluded = new Set(options.exclude ?? []);
8844
8917
  const includeWrappers = options.includeWrappers ?? false;
8845
8918
  const rootNameFor = (receiver) => options.name ?? (fn.name !== "" ? qualifiedSpanName(receiver, name) ?? name : name);
@@ -8855,7 +8928,7 @@ var Bitfab = class {
8855
8928
  spanOptions,
8856
8929
  function(...args) {
8857
8930
  self.simulationPlan.refresh();
8858
- let spansUsed = 0;
8931
+ let capturedSubtreeCount = 0;
8859
8932
  let truncated = false;
8860
8933
  const warnTruncated = () => {
8861
8934
  if (!truncated) {
@@ -8865,15 +8938,27 @@ var Bitfab = class {
8865
8938
  protocol: AUTO_TRACE_PROTOCOL,
8866
8939
  truncated: true,
8867
8940
  maxDepth,
8868
- maxSpans
8941
+ maxSpans,
8942
+ maxCapturedSubtreeSpans
8869
8943
  }
8870
8944
  });
8871
8945
  }
8872
8946
  warnOnce(
8873
8947
  `auto-trace-truncated:${traceFunctionKey}`,
8874
- `"${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.`
8875
8949
  );
8876
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
+ }
8877
8962
  const spanStackOf = (entry) => entry.spanStack;
8878
8963
  const openChild = (entry, childOptions, inputs, isAsync, invokeFn) => {
8879
8964
  const runInside = () => runWithAutoTraceEntry(
@@ -8902,12 +8987,25 @@ var Bitfab = class {
8902
8987
  return tracedChild(...inputs);
8903
8988
  });
8904
8989
  };
8905
- const overBudget = (entry) => {
8906
- if (entry.depth < maxDepth && spansUsed < maxSpans) {
8907
- 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;
8908
9006
  }
8909
9007
  runWithSpanStack(spanStackOf(entry), warnTruncated);
8910
- return true;
9008
+ return false;
8911
9009
  };
8912
9010
  const autoTraceContext = {
8913
9011
  traceFunctionKey,
@@ -8921,13 +9019,13 @@ var Bitfab = class {
8921
9019
  if (nodeConfiguration?.capture === false) {
8922
9020
  return clearAutoTraceNodeConfiguration(invokeFn);
8923
9021
  }
8924
- if (overBudget(entry)) {
9022
+ const spanName = nodeConfiguration?.name ?? definition.name;
9023
+ if (!recordsSpan(entry, spanName, nodeConfiguration !== void 0)) {
8925
9024
  return invokeWithoutNode();
8926
9025
  }
8927
- spansUsed += 1;
8928
9026
  const mockOnReplay = nodeConfiguration?.mockOnReplay ?? options.mockOnReplayDefault;
8929
9027
  const childOptions = {
8930
- name: nodeConfiguration?.name ?? definition.name,
9028
+ name: spanName,
8931
9029
  type: nodeConfiguration?.type ?? "function",
8932
9030
  captureWhen: "nested",
8933
9031
  surface: "opt-out",
@@ -8954,10 +9052,9 @@ var Bitfab = class {
8954
9052
  );
8955
9053
  },
8956
9054
  invokeNestedRoot(nestedName, nestedFunctionName, inputs, invokeFn, entry, link) {
8957
- if (excluded.has(nestedName) || excluded.has(nestedFunctionName) || overBudget(entry)) {
9055
+ if (excluded.has(nestedName) || excluded.has(nestedFunctionName) || !recordsSpan(entry, nestedName, true)) {
8958
9056
  return invokeFn();
8959
9057
  }
8960
- spansUsed += 1;
8961
9058
  const childOptions = {
8962
9059
  name: nestedName,
8963
9060
  type: "function",
@@ -8984,6 +9081,12 @@ var Bitfab = class {
8984
9081
  }
8985
9082
  const enclosing = currentAutoTraceEntries();
8986
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
+ }
8987
9090
  return tracedRoot.apply(this, args);
8988
9091
  }
8989
9092
  const link = getReplayContext() || inSeedScope() ? void 0 : { traceFunctionKey };
@@ -9030,6 +9133,9 @@ var Bitfab = class {
9030
9133
  return autoTraceRoot;
9031
9134
  }
9032
9135
  applySimulationPlan(payload, submit) {
9136
+ if (!fitsTraceSpanBudget(payload)) {
9137
+ return;
9138
+ }
9033
9139
  this.simulationPlan.send(payload, rootTraceFunctionKeyOf(payload), submit);
9034
9140
  }
9035
9141
  /**
@@ -9064,15 +9170,24 @@ var Bitfab = class {
9064
9170
  * from the environment. Once a non-empty key is found it is cached, so an
9065
9171
  * early resolve that found nothing never poisons a later one.
9066
9172
  */
9067
- resolveApiKey() {
9173
+ findApiKey(callsKeyFunction) {
9068
9174
  if (this.resolvedApiKey !== void 0) {
9069
9175
  return this.resolvedApiKey;
9070
9176
  }
9177
+ if (typeof this.apiKeyConfig === "function" && !callsKeyFunction) {
9178
+ return void 0;
9179
+ }
9071
9180
  const fromConfig = typeof this.apiKeyConfig === "function" ? this.apiKeyConfig() : this.apiKeyConfig;
9072
9181
  const candidate = fromConfig && fromConfig.trim() !== "" ? fromConfig : readEnv2("BITFAB_API_KEY");
9073
9182
  const key = candidate && candidate.trim() !== "" ? candidate : void 0;
9074
9183
  if (key) {
9075
9184
  this.resolvedApiKey = key;
9185
+ }
9186
+ return key;
9187
+ }
9188
+ resolveApiKey() {
9189
+ const key = this.findApiKey(true);
9190
+ if (key) {
9076
9191
  return key;
9077
9192
  }
9078
9193
  if (this.strict) {
@@ -9692,7 +9807,10 @@ var Bitfab = class {
9692
9807
  self.httpClient.traceCompletion.start(traceId, spanId);
9693
9808
  startedSpan = { traceId, spanId };
9694
9809
  const functionName = fn.name !== "" ? fn.name : void 0;
9695
- const rootTraceFunctionKey = activeTraceStates.get(traceId)?.traceFunctionKey ?? traceFunctionKey;
9810
+ const rootTraceFunctionKey = rootTraceFunctionKeyOfTrace(
9811
+ traceId,
9812
+ traceFunctionKey
9813
+ );
9696
9814
  const baseSpanParams = {
9697
9815
  traceFunctionKey,
9698
9816
  rootTraceFunctionKey,
@@ -10214,14 +10332,25 @@ var Bitfab = class {
10214
10332
  ...params.testRunId && { testRunId: params.testRunId }
10215
10333
  });
10216
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
+ }
10217
10344
  /**
10218
10345
  * Send a wrapper span from function execution.
10219
10346
  * Internal method to record spans when using withSpan.
10220
10347
  * Queued on the client's span transport; delivery is the transport's job.
10221
10348
  */
10222
10349
  sendWrapperSpan(params) {
10223
- const serializedInputs = params.captureContent ? serializeValue(params.inputs) : void 0;
10224
- 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;
10225
10354
  const externalSpan = {
10226
10355
  id: params.spanId,
10227
10356
  trace_id: params.traceId,
@@ -10272,7 +10401,8 @@ var Bitfab = class {
10272
10401
  ...params.captureContent && params.contexts && params.contexts.length > 0 && {
10273
10402
  contexts: params.contexts
10274
10403
  },
10275
- ...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 }
10276
10406
  }
10277
10407
  };
10278
10408
  if (params.parentSpanId) {