@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/node.cjs CHANGED
@@ -88,7 +88,7 @@ var __version__, __packageName__;
88
88
  var init_version_generated = __esm({
89
89
  "src/version.generated.ts"() {
90
90
  "use strict";
91
- __version__ = "0.54.13";
91
+ __version__ = "0.57.0";
92
92
  __packageName__ = "@bitfab/sdk";
93
93
  }
94
94
  });
@@ -970,19 +970,6 @@ var init_spanOrigin = __esm({
970
970
  }
971
971
  });
972
972
 
973
- // src/unrefTimer.ts
974
- function unrefTimer(timer) {
975
- const handle = timer;
976
- if (typeof handle.unref === "function") {
977
- handle.unref();
978
- }
979
- }
980
- var init_unrefTimer = __esm({
981
- "src/unrefTimer.ts"() {
982
- "use strict";
983
- }
984
- });
985
-
986
973
  // src/simulationPlan.ts
987
974
  function planWaitSliceMs(timeoutMs) {
988
975
  return Math.min(SIMULATION_PLAN_READ_TIMEOUT_MS, Math.max(timeoutMs, 0) / 2);
@@ -1055,6 +1042,10 @@ function simulationPlanEnvDisabled() {
1055
1042
  function missingSimulationPlanEndpoint(error) {
1056
1043
  return error instanceof BitfabError && error.status === 404;
1057
1044
  }
1045
+ function isTraceRootSpan(payload) {
1046
+ const parentId = rawSpanOf(payload)?.parent_id;
1047
+ return parentId === void 0 || parentId === null;
1048
+ }
1058
1049
  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;
1059
1050
  var init_simulationPlan = __esm({
1060
1051
  "src/simulationPlan.ts"() {
@@ -1063,7 +1054,6 @@ var init_simulationPlan = __esm({
1063
1054
  init_policyRefresh();
1064
1055
  init_readEnv();
1065
1056
  init_spanOrigin();
1066
- init_unrefTimer();
1067
1057
  init_warnOnce();
1068
1058
  SIMULATION_PLAN_REFRESH_MS = 6e4;
1069
1059
  SIMULATION_PLAN_RETRY_MS = 1e4;
@@ -1078,14 +1068,16 @@ var init_simulationPlan = __esm({
1078
1068
  this.source = source;
1079
1069
  this.enabled = enabled;
1080
1070
  this.policyRefresh = new PolicyRefresh();
1081
- this.retryPending = false;
1082
- this.waitingForFirstLoad = 0;
1071
+ this.firstReadOver = false;
1072
+ this.rootWaitOver = false;
1083
1073
  this.held = [];
1084
- this.droppedWhileHolding = 0;
1085
1074
  this.stopped = false;
1086
1075
  this.loaded = new Promise((resolve) => {
1087
1076
  this.resolveLoaded = resolve;
1088
1077
  });
1078
+ this.firstReadEnded = new Promise((resolve) => {
1079
+ this.resolveFirstReadEnded = resolve;
1080
+ });
1089
1081
  }
1090
1082
  disabled() {
1091
1083
  return !this.enabled || simulationPlanEnvDisabled();
@@ -1103,19 +1095,21 @@ var init_simulationPlan = __esm({
1103
1095
  "sim-plan-unreadable",
1104
1096
  "the sim plan response was not understood"
1105
1097
  );
1106
- return;
1098
+ } else {
1099
+ this.markLoaded(parsed);
1107
1100
  }
1108
- this.markLoaded(parsed);
1101
+ this.endFirstRead();
1109
1102
  },
1110
1103
  (error) => {
1111
1104
  if (missingSimulationPlanEndpoint(error)) {
1112
1105
  this.markLoaded(/* @__PURE__ */ new Map());
1113
- return;
1106
+ } else {
1107
+ this.scheduleRetry(
1108
+ "sim-plan-unavailable",
1109
+ `could not read the sim plan: ${error instanceof Error ? error.message : String(error)}`
1110
+ );
1114
1111
  }
1115
- this.scheduleRetry(
1116
- "sim-plan-unavailable",
1117
- `could not read the sim plan: ${error instanceof Error ? error.message : String(error)}`
1118
- );
1112
+ this.endFirstRead();
1119
1113
  }
1120
1114
  );
1121
1115
  }
@@ -1125,11 +1119,8 @@ var init_simulationPlan = __esm({
1125
1119
  }
1126
1120
  this.contentOffByKey = parsed;
1127
1121
  this.policyRefresh.hold(SIMULATION_PLAN_REFRESH_MS);
1128
- this.retryPending = false;
1129
- this.clearRetryTimer();
1130
1122
  this.resolveLoaded?.();
1131
1123
  this.resolveLoaded = void 0;
1132
- this.releaseHeld();
1133
1124
  }
1134
1125
  scheduleRetry(warnKey, reason) {
1135
1126
  if (this.stopped) {
@@ -1139,39 +1130,50 @@ var init_simulationPlan = __esm({
1139
1130
  if (this.contentOffByKey !== void 0) {
1140
1131
  return;
1141
1132
  }
1142
- this.retryPending = true;
1143
1133
  warnOnce(
1144
1134
  warnKey,
1145
- `${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.`
1135
+ `${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.`
1146
1136
  );
1147
- this.armRetry();
1148
1137
  }
1149
- holdsWork() {
1150
- return this.held.length > 0 || this.waitingForFirstLoad > 0;
1151
- }
1152
- armRetry() {
1153
- if (this.stopped || !this.retryPending || this.retryTimer !== void 0 || !this.holdsWork()) {
1154
- return;
1138
+ endFirstRead() {
1139
+ this.endRootWait();
1140
+ if (!this.firstReadOver) {
1141
+ this.firstReadOver = true;
1142
+ this.resolveFirstReadEnded?.();
1143
+ this.resolveFirstReadEnded = void 0;
1155
1144
  }
1156
- const timer = setTimeout(() => {
1157
- this.retryTimer = void 0;
1158
- this.policyRefresh.releaseHold();
1159
- this.refresh();
1160
- }, SIMULATION_PLAN_RETRY_MS);
1161
- unrefTimer(timer);
1162
- this.retryTimer = timer;
1145
+ this.releaseHeld();
1163
1146
  }
1164
- clearRetryTimer() {
1165
- if (this.retryTimer !== void 0) {
1166
- clearTimeout(this.retryTimer);
1167
- this.retryTimer = void 0;
1147
+ endRootWait() {
1148
+ this.rootWaitOver = true;
1149
+ if (this.rootWaitTimer !== void 0) {
1150
+ clearTimeout(this.rootWaitTimer);
1151
+ this.rootWaitTimer = void 0;
1168
1152
  }
1153
+ this.resolveRootWait?.();
1154
+ this.resolveRootWait = void 0;
1169
1155
  }
1170
- syncRetryTimer() {
1171
- if (this.holdsWork()) {
1172
- return;
1156
+ waitForFirstRead() {
1157
+ this.refresh();
1158
+ if (this.rootWaitOver || this.disabled() || !this.holdingForFirstRead()) {
1159
+ return void 0;
1173
1160
  }
1174
- this.clearRetryTimer();
1161
+ if (this.rootWait === void 0) {
1162
+ this.rootWait = new Promise((resolve) => {
1163
+ this.resolveRootWait = resolve;
1164
+ });
1165
+ this.rootWaitTimer = setTimeout(
1166
+ () => this.endRootWait(),
1167
+ SIMULATION_PLAN_READ_TIMEOUT_MS
1168
+ );
1169
+ }
1170
+ return this.rootWait;
1171
+ }
1172
+ holdingForFirstRead() {
1173
+ return this.contentOffByKey === void 0 && !this.firstReadOver && !this.stopped;
1174
+ }
1175
+ unavailableNow() {
1176
+ return !this.disabled() && this.contentOffByKey === void 0 && this.firstReadOver;
1175
1177
  }
1176
1178
  firstLoad() {
1177
1179
  if (this.contentOffByKey !== void 0 || this.stopped || this.disabled()) {
@@ -1181,19 +1183,20 @@ var init_simulationPlan = __esm({
1181
1183
  }
1182
1184
  send(payload, traceFunctionKey, submit) {
1183
1185
  this.refresh();
1184
- if (this.stopped || this.disabled() || traceFunctionKey === void 0 || spanNameOf(payload) === void 0 || recordedByFramework(payload)) {
1186
+ if (this.disabled() || traceFunctionKey === void 0 || spanNameOf(payload) === void 0 || recordedByFramework(payload)) {
1185
1187
  submit(payload);
1186
1188
  return;
1187
1189
  }
1188
- if (this.contentOffByKey === void 0) {
1189
- this.hold({ payload, traceFunctionKey, submit });
1190
+ const entry = { payload, traceFunctionKey, submit };
1191
+ if (this.holdingForFirstRead()) {
1192
+ this.hold(entry);
1190
1193
  return;
1191
1194
  }
1192
- this.submitEntry({ payload, traceFunctionKey, submit });
1195
+ this.submitEntry(entry);
1193
1196
  }
1194
1197
  sendTrace(payload, submit) {
1195
1198
  this.refresh();
1196
- if (this.stopped || this.disabled() || this.contentOffByKey !== void 0 || !this.holdsSpanOfTrace(traceIdOf(payload))) {
1199
+ if (this.disabled() || !this.holdingForFirstRead() || !this.holdsSpanOfTrace(traceIdOf(payload))) {
1197
1200
  submit(payload);
1198
1201
  return;
1199
1202
  }
@@ -1209,17 +1212,34 @@ var init_simulationPlan = __esm({
1209
1212
  }
1210
1213
  hold(entry) {
1211
1214
  this.held.push(entry);
1215
+ let overflowed = false;
1212
1216
  while (this.held.length > SIMULATION_PLAN_MAX_HELD) {
1213
- this.held.shift();
1214
- this.droppedWhileHolding += 1;
1217
+ const oldest = this.held.shift();
1218
+ this.submitEntry(oldest);
1219
+ overflowed = true;
1220
+ }
1221
+ if (overflowed) {
1222
+ warnOnce(
1223
+ "sim-plan-held-overflow",
1224
+ `more than ${SIMULATION_PLAN_MAX_HELD} records were waiting for the sim plan, so the oldest were sent without their inputs and outputs.`
1225
+ );
1226
+ }
1227
+ }
1228
+ payloadToSend(entry) {
1229
+ if (entry.traceFunctionKey === void 0) {
1230
+ return entry.payload;
1231
+ }
1232
+ if (this.contentOffByKey !== void 0) {
1233
+ return this.apply(entry.payload, entry.traceFunctionKey);
1215
1234
  }
1216
- this.armRetry();
1235
+ if (recordedByFramework(entry.payload) || isTraceRootSpan(entry.payload)) {
1236
+ return entry.payload;
1237
+ }
1238
+ return stripContent(entry.payload);
1217
1239
  }
1218
1240
  submitEntry(entry) {
1219
1241
  try {
1220
- entry.submit(
1221
- entry.traceFunctionKey === void 0 ? entry.payload : this.apply(entry.payload, entry.traceFunctionKey)
1222
- );
1242
+ entry.submit(this.payloadToSend(entry));
1223
1243
  } catch (error) {
1224
1244
  warnOnce(
1225
1245
  "sim-plan-held-span-dropped",
@@ -1230,62 +1250,38 @@ var init_simulationPlan = __esm({
1230
1250
  releaseHeld() {
1231
1251
  const held = this.held;
1232
1252
  this.held = [];
1233
- this.syncRetryTimer();
1234
- this.warnDropped();
1235
1253
  for (const entry of held) {
1236
1254
  this.submitEntry(entry);
1237
1255
  }
1238
1256
  }
1239
- warnDropped() {
1240
- if (this.droppedWhileHolding === 0) {
1241
- return;
1242
- }
1243
- const dropped = this.droppedWhileHolding;
1244
- this.droppedWhileHolding = 0;
1245
- warnOnce(
1246
- "sim-plan-held-overflow",
1247
- `${dropped} record(s) were dropped while the sim plan was still loading; at most ${SIMULATION_PLAN_MAX_HELD} are held per client.`
1248
- );
1249
- }
1250
- warnNeverLoaded() {
1257
+ releaseHeldWithoutPlan() {
1251
1258
  if (this.held.length === 0) {
1252
1259
  return;
1253
1260
  }
1254
- warnOnce(
1255
- "sim-plan-never-loaded",
1256
- `${this.held.length} record(s) were not sent because the sim plan never loaded`
1257
- );
1261
+ if (this.contentOffByKey === void 0) {
1262
+ warnOnce(
1263
+ "sim-plan-not-loaded-at-release",
1264
+ `${this.held.length} record(s) were sent without their inputs and outputs because the sim plan had not loaded`
1265
+ );
1266
+ }
1267
+ this.releaseHeld();
1258
1268
  }
1259
1269
  async release(timeoutMs) {
1260
1270
  this.refresh();
1261
- if (this.holdsWork()) {
1262
- await this.waitForFirstLoad(timeoutMs);
1271
+ if (this.held.length > 0) {
1272
+ await this.waitForFirstReadEnd(timeoutMs);
1263
1273
  }
1264
- this.warnNeverLoaded();
1265
- this.warnDropped();
1274
+ this.releaseHeldWithoutPlan();
1266
1275
  }
1267
- waitForFirstLoad(timeoutMs) {
1268
- const firstLoad = this.firstLoad();
1269
- if (firstLoad === void 0) {
1276
+ waitForFirstReadEnd(timeoutMs) {
1277
+ if (this.firstReadOver) {
1270
1278
  return Promise.resolve();
1271
1279
  }
1272
- this.waitingForFirstLoad += 1;
1273
- this.armRetry();
1274
1280
  return new Promise((resolve) => {
1275
- let settled2 = false;
1276
- const finish = () => {
1277
- if (settled2) {
1278
- return;
1279
- }
1280
- settled2 = true;
1281
- this.waitingForFirstLoad -= 1;
1282
- this.syncRetryTimer();
1283
- resolve();
1284
- };
1285
- const timer = setTimeout(finish, Math.max(timeoutMs, 0));
1286
- void firstLoad.then(() => {
1281
+ const timer = setTimeout(resolve, Math.max(timeoutMs, 0));
1282
+ void this.firstReadEnded.then(() => {
1287
1283
  clearTimeout(timer);
1288
- finish();
1284
+ resolve();
1289
1285
  });
1290
1286
  });
1291
1287
  }
@@ -1293,16 +1289,16 @@ var init_simulationPlan = __esm({
1293
1289
  if (this.stopped) {
1294
1290
  return;
1295
1291
  }
1292
+ this.releaseHeldWithoutPlan();
1296
1293
  this.stopped = true;
1297
- this.retryPending = false;
1298
- this.clearRetryTimer();
1299
- this.warnNeverLoaded();
1300
- this.held = [];
1301
- this.warnDropped();
1294
+ this.endFirstRead();
1302
1295
  }
1303
1296
  contentOff(traceFunctionKey, spanName) {
1304
1297
  return this.contentOffByKey?.get(traceFunctionKey)?.has(spanName) === true;
1305
1298
  }
1299
+ contentOffNow(traceFunctionKey, spanName) {
1300
+ return !this.stopped && !this.disabled() && this.contentOff(traceFunctionKey, spanName);
1301
+ }
1306
1302
  apply(payload, traceFunctionKey) {
1307
1303
  if (recordedByFramework(payload)) {
1308
1304
  return payload;
@@ -1433,6 +1429,19 @@ var init_transportTypes = __esm({
1433
1429
  }
1434
1430
  });
1435
1431
 
1432
+ // src/unrefTimer.ts
1433
+ function unrefTimer(timer) {
1434
+ const handle = timer;
1435
+ if (typeof handle.unref === "function") {
1436
+ handle.unref();
1437
+ }
1438
+ }
1439
+ var init_unrefTimer = __esm({
1440
+ "src/unrefTimer.ts"() {
1441
+ "use strict";
1442
+ }
1443
+ });
1444
+
1436
1445
  // src/otel.ts
1437
1446
  function readBoundedIntEnv(name, max, fallback, warnKey) {
1438
1447
  const raw = readEnv(name);
@@ -2085,9 +2094,14 @@ __export(http_exports, {
2085
2094
  });
2086
2095
  async function releaseHeldExternalSpans(timeoutMs) {
2087
2096
  const deadline = Date.now() + Math.max(timeoutMs, 0);
2097
+ let released = true;
2088
2098
  for (const client of [...liveHttpClients]) {
2089
- await client.releaseHeldExternalSpans?.(Math.max(0, deadline - Date.now()));
2099
+ const result = await client.releaseHeldExternalSpans?.(
2100
+ Math.max(0, deadline - Date.now())
2101
+ );
2102
+ released = result !== false && released;
2090
2103
  }
2104
+ return released;
2091
2105
  }
2092
2106
  function stopSimulationPlans() {
2093
2107
  for (const client of [...liveHttpClients]) {
@@ -2104,14 +2118,15 @@ function awaitOnExit(promise) {
2104
2118
  }
2105
2119
  async function flushTraces(timeoutMs = 5e3) {
2106
2120
  const deadline = Date.now() + Math.max(timeoutMs, 0);
2107
- await releaseHeldExternalSpans(planWaitSliceMs(timeoutMs));
2121
+ const released = await releaseHeldExternalSpans(planWaitSliceMs(timeoutMs));
2108
2122
  const requestsFlushed = await awaitPendingRequests(
2109
2123
  Math.max(0, deadline - Date.now())
2110
2124
  );
2125
+ const releasedAfterDeferred = await releaseHeldExternalSpans(0);
2111
2126
  const transportsFlushed = await flushTraceTransports(
2112
2127
  Math.max(0, deadline - Date.now())
2113
2128
  );
2114
- return requestsFlushed && transportsFlushed;
2129
+ return released && releasedAfterDeferred && requestsFlushed && transportsFlushed;
2115
2130
  }
2116
2131
  async function awaitPendingRequests(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS2) {
2117
2132
  await replayContextReady.catch(() => {
@@ -2521,13 +2536,17 @@ var init_http = __esm({
2521
2536
  */
2522
2537
  async settleDeferredWork(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS2) {
2523
2538
  const deadline = Date.now() + Math.max(timeoutMs, 0);
2524
- await this.releaseHeldExternalSpans?.(planWaitSliceMs(timeoutMs));
2539
+ const released = await this.releaseHeldExternalSpans?.(
2540
+ planWaitSliceMs(timeoutMs)
2541
+ );
2525
2542
  await replayContextReady.catch(() => {
2526
2543
  });
2527
- return waitForPromises(
2544
+ const settled2 = await waitForPromises(
2528
2545
  Array.from(this.deferredWork),
2529
2546
  Math.max(0, deadline - Date.now())
2530
2547
  );
2548
+ const releasedAfterDeferred = await this.releaseHeldExternalSpans?.(0);
2549
+ return released !== false && releasedAfterDeferred !== false && settled2;
2531
2550
  }
2532
2551
  /**
2533
2552
  * Wait for spans queued by this client to be delivered, within one deadline.
@@ -2552,7 +2571,9 @@ var init_http = __esm({
2552
2571
  }
2553
2572
  const deadline = Date.now() + Math.max(timeoutMs, 0);
2554
2573
  this.closing = (async () => {
2555
- await this.releaseHeldExternalSpans?.(planWaitSliceMs(timeoutMs));
2574
+ const released = await this.releaseHeldExternalSpans?.(
2575
+ planWaitSliceMs(timeoutMs)
2576
+ );
2556
2577
  this.stopSimulationPlan?.();
2557
2578
  this.externalSpanTransform = void 0;
2558
2579
  this.externalTraceTransform = void 0;
@@ -2566,7 +2587,7 @@ var init_http = __esm({
2566
2587
  const transport = this.traceTransport;
2567
2588
  this.traceTransport = void 0;
2568
2589
  const shutdownOk = await transport?.shutdown(Math.max(0, deadline - Date.now())) ?? true;
2569
- return settled2 && shutdownOk;
2590
+ return released !== false && settled2 && shutdownOk;
2570
2591
  })();
2571
2592
  return this.closing;
2572
2593
  }
@@ -4000,6 +4021,12 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4000
4021
  }
4001
4022
  }
4002
4023
  const attempts = resolveAttempts(options?.attempts);
4024
+ const maxConcurrency = options?.maxConcurrency ?? 10;
4025
+ if (!Number.isInteger(maxConcurrency) || maxConcurrency < 1) {
4026
+ throw new BitfabError(
4027
+ `maxConcurrency must be a positive integer (got ${maxConcurrency}).`
4028
+ );
4029
+ }
4003
4030
  await replayContextReady;
4004
4031
  let codeChangeDescription = options?.codeChangeDescription;
4005
4032
  let codeChangeFiles = options?.codeChangeFiles;
@@ -4046,7 +4073,6 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4046
4073
  }
4047
4074
  }
4048
4075
  const mockStrategy = options?.mock ?? "marked";
4049
- const maxConcurrency = options?.maxConcurrency ?? 10;
4050
4076
  const fullTestRunUrl = `${serviceUrl}${testRunUrl}`;
4051
4077
  const resolvedOverrides = [
4052
4078
  ...normalizeMockOverrides(options?.mockOverride),
@@ -4143,6 +4169,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4143
4169
  originalDurationMs: item.originalDurationMs,
4144
4170
  originalTokens: item.originalTokens,
4145
4171
  originalModel: item.originalModel,
4172
+ ingestionType: item.ingestionType,
4146
4173
  tokens: item.tokens,
4147
4174
  model: item.model,
4148
4175
  dbSnapshotRef: item.dbSnapshotRef,
@@ -5983,6 +6010,14 @@ var LabelsClient = class {
5983
6010
  );
5984
6011
  return response.labels;
5985
6012
  }
6013
+ /** Withhold a verdict for a trace or replay attempt whose check did not run. */
6014
+ async skip(target, testRunId) {
6015
+ return this.save({ ...target, skip: true }, testRunId);
6016
+ }
6017
+ /** Clear a previous verdict for a trace, replay attempt, or assertion. */
6018
+ async archive(target, testRunId) {
6019
+ return this.save({ ...target, archive: true }, testRunId);
6020
+ }
5986
6021
  async saveHuman(update) {
5987
6022
  const [outcome] = await this.saveHumanAll([update]);
5988
6023
  return outcome;
@@ -6901,6 +6936,15 @@ var BitfabLangGraphIntegration = class {
6901
6936
  // src/client.ts
6902
6937
  init_mockOverride();
6903
6938
 
6939
+ // src/streamFinalizationError.ts
6940
+ var StreamFinalizationError = class extends Error {
6941
+ constructor(message, output) {
6942
+ super(message);
6943
+ this.output = output;
6944
+ this.name = "StreamFinalizationError";
6945
+ }
6946
+ };
6947
+
6904
6948
  // src/openaiAgentSdk.ts
6905
6949
  var BitfabOpenAIAgentHandler = class {
6906
6950
  constructor(config) {
@@ -6940,7 +6984,11 @@ var BitfabOpenAIAgentHandler = class {
6940
6984
  if (isStreaming && res?.completed) {
6941
6985
  try {
6942
6986
  await res.completed;
6943
- } catch {
6987
+ } catch (error) {
6988
+ throw new StreamFinalizationError(
6989
+ error instanceof Error ? error.message : String(error),
6990
+ res.finalOutput
6991
+ );
6944
6992
  }
6945
6993
  }
6946
6994
  return res?.finalOutput;
@@ -7698,17 +7746,6 @@ function runWithSeedContext(ctx, fn) {
7698
7746
  init_serialize();
7699
7747
  init_simulationPlan();
7700
7748
  init_spanOrigin();
7701
-
7702
- // src/streamFinalizationError.ts
7703
- var StreamFinalizationError = class extends Error {
7704
- constructor(message, output) {
7705
- super(message);
7706
- this.output = output;
7707
- this.name = "StreamFinalizationError";
7708
- }
7709
- };
7710
-
7711
- // src/client.ts
7712
7749
  init_traceMetadata();
7713
7750
 
7714
7751
  // src/traces.ts
@@ -8248,6 +8285,25 @@ var BitfabVercelAiHandler = class {
8248
8285
  // src/client.ts
8249
8286
  init_warnOnce();
8250
8287
  var activeTraceStates = /* @__PURE__ */ new Map();
8288
+ function fitsTraceSpanBudget(payload) {
8289
+ if (!recordedByFramework(payload)) {
8290
+ return true;
8291
+ }
8292
+ const traceId = traceIdOf(payload);
8293
+ const spanBudget = traceId === void 0 ? void 0 : activeTraceStates.get(traceId)?.spanBudget;
8294
+ if (spanBudget === void 0) {
8295
+ return true;
8296
+ }
8297
+ if (spanBudget.spansUsed >= spanBudget.maxSpans) {
8298
+ spanBudget.truncate();
8299
+ return false;
8300
+ }
8301
+ spanBudget.spansUsed += 1;
8302
+ return true;
8303
+ }
8304
+ function rootTraceFunctionKeyOfTrace(traceId, fallback) {
8305
+ return activeTraceStates.get(traceId)?.traceFunctionKey ?? fallback;
8306
+ }
8251
8307
  function rootTraceFunctionKeyOf(payload) {
8252
8308
  const captured = payload[ROOT_TRACE_FUNCTION_KEY_FIELD];
8253
8309
  if (typeof captured === "string") {
@@ -8623,7 +8679,8 @@ function readBooleanEnv(name) {
8623
8679
  return void 0;
8624
8680
  }
8625
8681
  var DEFAULT_AUTO_TRACE_MAX_DEPTH = 30;
8626
- var DEFAULT_AUTO_TRACE_MAX_SPANS = 500;
8682
+ var DEFAULT_AUTO_TRACE_MAX_SPANS = 2048;
8683
+ var DEFAULT_AUTO_TRACE_MAX_CAPTURED_SUBTREE_SPANS = 512;
8627
8684
  var AUTO_TRACE_PROTOCOL = "ts-auto-v1";
8628
8685
  function spanOwnerName(receiver) {
8629
8686
  if (typeof receiver === "function") {
@@ -8694,6 +8751,9 @@ var Bitfab = class {
8694
8751
  this.httpClient.externalTraceTransform = (payload, submit) => this.simulationPlan.sendTrace(payload, submit);
8695
8752
  this.httpClient.releaseHeldExternalSpans = (timeoutMs) => this.simulationPlan.release(timeoutMs);
8696
8753
  this.httpClient.stopSimulationPlan = () => this.simulationPlan.stop();
8754
+ if (this.findApiKey(false) !== void 0) {
8755
+ this.simulationPlan.refresh();
8756
+ }
8697
8757
  this.datasets = new DatasetsClient(this.httpClient);
8698
8758
  this.assertionCategories = new AssertionCategoriesClient(this.httpClient);
8699
8759
  this.traces = new TracesClient(this.httpClient);
@@ -8850,10 +8910,23 @@ var Bitfab = class {
8850
8910
  options.maxDepth,
8851
8911
  DEFAULT_AUTO_TRACE_MAX_DEPTH
8852
8912
  );
8913
+ if (options.maxSpans !== void 0 && options.maxSpans < 1) {
8914
+ throw new BitfabError(
8915
+ `maxSpans must be at least 1 because the root span counts toward it (got ${options.maxSpans})`
8916
+ );
8917
+ }
8853
8918
  const maxSpans = autoTraceLimit(
8854
8919
  options.maxSpans,
8855
8920
  DEFAULT_AUTO_TRACE_MAX_SPANS
8856
8921
  );
8922
+ const maxCapturedSubtreeSpans = Math.min(
8923
+ autoTraceLimit(
8924
+ options.maxCapturedSubtreeSpans,
8925
+ DEFAULT_AUTO_TRACE_MAX_CAPTURED_SUBTREE_SPANS
8926
+ ),
8927
+ maxSpans
8928
+ );
8929
+ const rootIsAsync = fn.constructor.name === "AsyncFunction";
8857
8930
  const excluded = new Set(options.exclude ?? []);
8858
8931
  const includeWrappers = options.includeWrappers ?? false;
8859
8932
  const rootNameFor = (receiver) => options.name ?? (fn.name !== "" ? qualifiedSpanName(receiver, name) ?? name : name);
@@ -8869,7 +8942,7 @@ var Bitfab = class {
8869
8942
  spanOptions,
8870
8943
  function(...args) {
8871
8944
  self.simulationPlan.refresh();
8872
- let spansUsed = 0;
8945
+ let capturedSubtreeCount = 0;
8873
8946
  let truncated = false;
8874
8947
  const warnTruncated = () => {
8875
8948
  if (!truncated) {
@@ -8879,15 +8952,27 @@ var Bitfab = class {
8879
8952
  protocol: AUTO_TRACE_PROTOCOL,
8880
8953
  truncated: true,
8881
8954
  maxDepth,
8882
- maxSpans
8955
+ maxSpans,
8956
+ maxCapturedSubtreeSpans
8883
8957
  }
8884
8958
  });
8885
8959
  }
8886
8960
  warnOnce(
8887
8961
  `auto-trace-truncated:${traceFunctionKey}`,
8888
- `"${traceFunctionKey}" hit an automatic subtree capture limit (maxDepth=${maxDepth}, maxSpans=${maxSpans}); its trace is incomplete. Raise the limits or narrow the subtree with exclude.`
8962
+ `"${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.`
8889
8963
  );
8890
8964
  };
8965
+ const rootSpanStack = getSpanStack();
8966
+ const spanBudget = {
8967
+ spansUsed: 1,
8968
+ maxSpans,
8969
+ truncate: () => runWithSpanStack(rootSpanStack, warnTruncated)
8970
+ };
8971
+ const rootSpan = rootSpanStack[rootSpanStack.length - 1];
8972
+ const rootTraceState = rootSpan === void 0 ? void 0 : activeTraceStates.get(rootSpan.traceId);
8973
+ if (rootTraceState !== void 0 && rootTraceState.spanBudget === void 0) {
8974
+ rootTraceState.spanBudget = spanBudget;
8975
+ }
8891
8976
  const spanStackOf = (entry) => entry.spanStack;
8892
8977
  const openChild = (entry, childOptions, inputs, isAsync, invokeFn) => {
8893
8978
  const runInside = () => runWithAutoTraceEntry(
@@ -8916,12 +9001,25 @@ var Bitfab = class {
8916
9001
  return tracedChild(...inputs);
8917
9002
  });
8918
9003
  };
8919
- const overBudget = (entry) => {
8920
- if (entry.depth < maxDepth && spansUsed < maxSpans) {
8921
- return false;
9004
+ const rootTraceFunctionKeyOfEntry = (entry) => {
9005
+ const stack = spanStackOf(entry);
9006
+ const innermost = stack[stack.length - 1];
9007
+ return innermost === void 0 ? traceFunctionKey : rootTraceFunctionKeyOfTrace(innermost.traceId, traceFunctionKey);
9008
+ };
9009
+ const recordsSpan = (entry, spanName, declared) => {
9010
+ const countsTowardCapturedSubtree = !declared && !self.simulationPlan.contentOffNow(
9011
+ rootTraceFunctionKeyOfEntry(entry),
9012
+ spanName
9013
+ );
9014
+ if (entry.depth < maxDepth && spanBudget.spansUsed < maxSpans && (!countsTowardCapturedSubtree || capturedSubtreeCount < maxCapturedSubtreeSpans)) {
9015
+ spanBudget.spansUsed += 1;
9016
+ if (countsTowardCapturedSubtree) {
9017
+ capturedSubtreeCount += 1;
9018
+ }
9019
+ return true;
8922
9020
  }
8923
9021
  runWithSpanStack(spanStackOf(entry), warnTruncated);
8924
- return true;
9022
+ return false;
8925
9023
  };
8926
9024
  const autoTraceContext = {
8927
9025
  traceFunctionKey,
@@ -8935,13 +9033,13 @@ var Bitfab = class {
8935
9033
  if (nodeConfiguration?.capture === false) {
8936
9034
  return clearAutoTraceNodeConfiguration(invokeFn);
8937
9035
  }
8938
- if (overBudget(entry)) {
9036
+ const spanName = nodeConfiguration?.name ?? definition.name;
9037
+ if (!recordsSpan(entry, spanName, nodeConfiguration !== void 0)) {
8939
9038
  return invokeWithoutNode();
8940
9039
  }
8941
- spansUsed += 1;
8942
9040
  const mockOnReplay = nodeConfiguration?.mockOnReplay ?? options.mockOnReplayDefault;
8943
9041
  const childOptions = {
8944
- name: nodeConfiguration?.name ?? definition.name,
9042
+ name: spanName,
8945
9043
  type: nodeConfiguration?.type ?? "function",
8946
9044
  captureWhen: "nested",
8947
9045
  surface: "opt-out",
@@ -8968,10 +9066,9 @@ var Bitfab = class {
8968
9066
  );
8969
9067
  },
8970
9068
  invokeNestedRoot(nestedName, nestedFunctionName, inputs, invokeFn, entry, link) {
8971
- if (excluded.has(nestedName) || excluded.has(nestedFunctionName) || overBudget(entry)) {
9069
+ if (excluded.has(nestedName) || excluded.has(nestedFunctionName) || !recordsSpan(entry, nestedName, true)) {
8972
9070
  return invokeFn();
8973
9071
  }
8974
- spansUsed += 1;
8975
9072
  const childOptions = {
8976
9073
  name: nestedName,
8977
9074
  type: "function",
@@ -8998,6 +9095,12 @@ var Bitfab = class {
8998
9095
  }
8999
9096
  const enclosing = currentAutoTraceEntries();
9000
9097
  if (enclosing.length === 0) {
9098
+ const firstPlanRead = rootIsAsync ? self.simulationPlan.waitForFirstRead() : void 0;
9099
+ if (firstPlanRead !== void 0) {
9100
+ return firstPlanRead.then(
9101
+ () => tracedRoot.apply(this, args)
9102
+ );
9103
+ }
9001
9104
  return tracedRoot.apply(this, args);
9002
9105
  }
9003
9106
  const link = getReplayContext() || inSeedScope() ? void 0 : { traceFunctionKey };
@@ -9044,6 +9147,9 @@ var Bitfab = class {
9044
9147
  return autoTraceRoot;
9045
9148
  }
9046
9149
  applySimulationPlan(payload, submit) {
9150
+ if (!fitsTraceSpanBudget(payload)) {
9151
+ return;
9152
+ }
9047
9153
  this.simulationPlan.send(payload, rootTraceFunctionKeyOf(payload), submit);
9048
9154
  }
9049
9155
  /**
@@ -9078,15 +9184,24 @@ var Bitfab = class {
9078
9184
  * from the environment. Once a non-empty key is found it is cached, so an
9079
9185
  * early resolve that found nothing never poisons a later one.
9080
9186
  */
9081
- resolveApiKey() {
9187
+ findApiKey(callsKeyFunction) {
9082
9188
  if (this.resolvedApiKey !== void 0) {
9083
9189
  return this.resolvedApiKey;
9084
9190
  }
9191
+ if (typeof this.apiKeyConfig === "function" && !callsKeyFunction) {
9192
+ return void 0;
9193
+ }
9085
9194
  const fromConfig = typeof this.apiKeyConfig === "function" ? this.apiKeyConfig() : this.apiKeyConfig;
9086
9195
  const candidate = fromConfig && fromConfig.trim() !== "" ? fromConfig : readEnv2("BITFAB_API_KEY");
9087
9196
  const key = candidate && candidate.trim() !== "" ? candidate : void 0;
9088
9197
  if (key) {
9089
9198
  this.resolvedApiKey = key;
9199
+ }
9200
+ return key;
9201
+ }
9202
+ resolveApiKey() {
9203
+ const key = this.findApiKey(true);
9204
+ if (key) {
9090
9205
  return key;
9091
9206
  }
9092
9207
  if (this.strict) {
@@ -9706,7 +9821,10 @@ var Bitfab = class {
9706
9821
  self.httpClient.traceCompletion.start(traceId, spanId);
9707
9822
  startedSpan = { traceId, spanId };
9708
9823
  const functionName = fn.name !== "" ? fn.name : void 0;
9709
- const rootTraceFunctionKey = activeTraceStates.get(traceId)?.traceFunctionKey ?? traceFunctionKey;
9824
+ const rootTraceFunctionKey = rootTraceFunctionKeyOfTrace(
9825
+ traceId,
9826
+ traceFunctionKey
9827
+ );
9710
9828
  const baseSpanParams = {
9711
9829
  traceFunctionKey,
9712
9830
  rootTraceFunctionKey,
@@ -10228,14 +10346,25 @@ var Bitfab = class {
10228
10346
  ...params.testRunId && { testRunId: params.testRunId }
10229
10347
  });
10230
10348
  }
10349
+ contentOffBySimulationPlan(params) {
10350
+ if (params.rootTraceFunctionKey === void 0 || isFrameworkInstrumentation(params.instrumentation)) {
10351
+ return false;
10352
+ }
10353
+ return this.simulationPlan.contentOffNow(
10354
+ params.rootTraceFunctionKey,
10355
+ params.spanName
10356
+ ) || params.parentSpanId !== null && this.simulationPlan.unavailableNow();
10357
+ }
10231
10358
  /**
10232
10359
  * Send a wrapper span from function execution.
10233
10360
  * Internal method to record spans when using withSpan.
10234
10361
  * Queued on the client's span transport; delivery is the transport's job.
10235
10362
  */
10236
10363
  sendWrapperSpan(params) {
10237
- const serializedInputs = params.captureContent ? serializeValue(params.inputs) : void 0;
10238
- const serializedResult = params.captureContent ? serializeValue(params.result) : void 0;
10364
+ const contentOff = this.contentOffBySimulationPlan(params);
10365
+ const buildsContent = params.captureContent && !contentOff;
10366
+ const serializedInputs = buildsContent ? serializeValue(params.inputs) : void 0;
10367
+ const serializedResult = buildsContent ? serializeValue(params.result) : void 0;
10239
10368
  const externalSpan = {
10240
10369
  id: params.spanId,
10241
10370
  trace_id: params.traceId,
@@ -10286,7 +10415,8 @@ var Bitfab = class {
10286
10415
  ...params.captureContent && params.contexts && params.contexts.length > 0 && {
10287
10416
  contexts: params.contexts
10288
10417
  },
10289
- ...params.captureContent && params.prompt !== void 0 && { prompt: params.prompt }
10418
+ ...params.captureContent && params.prompt !== void 0 && { prompt: params.prompt },
10419
+ ...contentOff && { [CONTENT_OFF_KEY]: true }
10290
10420
  }
10291
10421
  };
10292
10422
  if (params.parentSpanId) {