@bitfab/sdk 0.58.5 → 0.59.1

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 (42) hide show
  1. package/dist/autoTrace.cjs.map +1 -1
  2. package/dist/autoTrace.d.cts +2 -0
  3. package/dist/autoTrace.d.ts +2 -0
  4. package/dist/autoTrace.js +1 -1
  5. package/dist/{chunk-ETZUSSF6.js → chunk-5DNB7EN7.js} +1 -1
  6. package/dist/chunk-5DNB7EN7.js.map +1 -0
  7. package/dist/{chunk-IP2AYKT7.js → chunk-5IQ3KQLI.js} +29 -14
  8. package/dist/chunk-5IQ3KQLI.js.map +1 -0
  9. package/dist/{chunk-Y47DQCCK.js → chunk-AYSXJGAZ.js} +149 -41
  10. package/dist/chunk-AYSXJGAZ.js.map +1 -0
  11. package/dist/{chunk-E25HU4YZ.js → chunk-ERS4TBD6.js} +29 -14
  12. package/dist/chunk-ERS4TBD6.js.map +1 -0
  13. package/dist/{chunk-YGZA3H67.js → chunk-MK756TJL.js} +54 -38
  14. package/dist/chunk-MK756TJL.js.map +1 -0
  15. package/dist/{chunk-NNSJVCR3.js → chunk-ZC6TZ2IM.js} +18 -10
  16. package/dist/chunk-ZC6TZ2IM.js.map +1 -0
  17. package/dist/{http-PVE62D6W.js → http-JBKOOV7C.js} +4 -2
  18. package/dist/{http-6K4GQC4S.js → http-SMGYDRGR.js} +4 -2
  19. package/dist/index.cjs +225 -82
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +98 -43
  22. package/dist/index.d.ts +98 -43
  23. package/dist/index.js +12 -4
  24. package/dist/node.cjs +225 -82
  25. package/dist/node.cjs.map +1 -1
  26. package/dist/node.d.cts +1 -1
  27. package/dist/node.d.ts +1 -1
  28. package/dist/node.js +12 -4
  29. package/dist/node.js.map +1 -1
  30. package/dist/{replay-6WA5GD4R.js → replay-OQN464ZF.js} +3 -3
  31. package/dist/replayCli.js +2 -2
  32. package/dist/seedCli.js +2 -2
  33. package/package.json +1 -1
  34. package/dist/chunk-E25HU4YZ.js.map +0 -1
  35. package/dist/chunk-ETZUSSF6.js.map +0 -1
  36. package/dist/chunk-IP2AYKT7.js.map +0 -1
  37. package/dist/chunk-NNSJVCR3.js.map +0 -1
  38. package/dist/chunk-Y47DQCCK.js.map +0 -1
  39. package/dist/chunk-YGZA3H67.js.map +0 -1
  40. /package/dist/{http-6K4GQC4S.js.map → http-JBKOOV7C.js.map} +0 -0
  41. /package/dist/{http-PVE62D6W.js.map → http-SMGYDRGR.js.map} +0 -0
  42. /package/dist/{replay-6WA5GD4R.js.map → replay-OQN464ZF.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.58.5";
45
+ __version__ = "0.59.1";
46
46
  __packageName__ = "@bitfab/sdk";
47
47
  }
48
48
  });
@@ -2104,6 +2104,7 @@ __export(http_exports, {
2104
2104
  HttpClient: () => HttpClient,
2105
2105
  awaitOnExit: () => awaitOnExit,
2106
2106
  awaitPendingRequests: () => awaitPendingRequests,
2107
+ experimentIdWireFields: () => experimentIdWireFields,
2107
2108
  flushTraces: () => flushTraces,
2108
2109
  parseRetryAfterMs: () => parseRetryAfterMs,
2109
2110
  serializePayloadBody: () => serializePayloadBody
@@ -2272,6 +2273,9 @@ function warnDroppedExternalTrace(error) {
2272
2273
  `a trace was dropped because the send step failed: ${describeError(error)}`
2273
2274
  );
2274
2275
  }
2276
+ function experimentIdWireFields(experimentId) {
2277
+ return { experimentId, testRunId: experimentId };
2278
+ }
2275
2279
  var REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS, REPLAY_COMPLETE_REQUEST_TIMEOUT_MS, OTLP_TRACES_ENDPOINT, RETRYABLE_STATUSES, EXIT_FLUSH_TIMEOUT_MS, DEFAULT_LIFECYCLE_TIMEOUT_MS2, pendingTracePromises, liveHttpClients, carrierSeq, HttpClient;
2276
2280
  var init_http = __esm({
2277
2281
  "src/http.ts"() {
@@ -2876,7 +2880,7 @@ var init_http = __esm({
2876
2880
  }
2877
2881
  /**
2878
2882
  * Start a replay session by fetching historical traces.
2879
- * Blocking call - creates a test run and returns lightweight item references.
2883
+ * Blocking call - creates an experiment and returns lightweight item references.
2880
2884
  */
2881
2885
  async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetIds, graderIds, dbBranchSettings, attempts, includeOriginalMetadata, onlyWithAssertions, judgeAssertions) {
2882
2886
  const payload = { traceFunctionKey };
@@ -2932,9 +2936,20 @@ var init_http = __esm({
2932
2936
  payload.git = git;
2933
2937
  }
2934
2938
  const timeout = includeDbBranchLease ? REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS : 3e4;
2935
- return this.request("/api/sdk/replay/start", payload, {
2936
- timeout
2937
- });
2939
+ const response = await this.request(
2940
+ "/api/sdk/replay/start",
2941
+ payload,
2942
+ { timeout }
2943
+ );
2944
+ const { testRunId, testRunUrl, ...rest } = response;
2945
+ const experimentId = response.experimentId ?? testRunId;
2946
+ const experimentUrl = response.experimentUrl ?? testRunUrl;
2947
+ if (experimentId === void 0 || experimentUrl === void 0) {
2948
+ throw new BitfabError(
2949
+ "Replay start response is missing the experiment ID or URL."
2950
+ );
2951
+ }
2952
+ return { ...rest, experimentId, experimentUrl };
2938
2953
  }
2939
2954
  /**
2940
2955
  * Fetch an external span by ID.
@@ -3031,21 +3046,21 @@ var init_http = __esm({
3031
3046
  * has a final status AND at least that many persisted spans, which is what
3032
3047
  * makes this a real barrier rather than a "the row exists" check.
3033
3048
  */
3034
- async getReplayStatus(testRunId, expectedSpanCounts) {
3049
+ async getReplayStatus(experimentId, expectedSpanCounts) {
3035
3050
  return this.request(
3036
3051
  "/api/sdk/replay/status",
3037
- { testRunId, expectedSpanCounts },
3052
+ { ...experimentIdWireFields(experimentId), expectedSpanCounts },
3038
3053
  { timeout: 3e4 }
3039
3054
  );
3040
3055
  }
3041
3056
  /**
3042
- * Mark a replay test run as completed.
3057
+ * Mark a replay experiment as completed.
3043
3058
  * Blocking call.
3044
3059
  */
3045
- async completeReplay(testRunId) {
3060
+ async completeReplay(experimentId) {
3046
3061
  return this.request(
3047
3062
  "/api/sdk/replay/complete",
3048
- { testRunId },
3063
+ experimentIdWireFields(experimentId),
3049
3064
  { timeout: REPLAY_COMPLETE_REQUEST_TIMEOUT_MS }
3050
3065
  );
3051
3066
  }
@@ -3067,11 +3082,11 @@ var init_http = __esm({
3067
3082
  * snapshot + preview branch and polls operations to readiness, which
3068
3083
  * can take seconds.
3069
3084
  */
3070
- async resolveDbBranchLease(testRunId, traceId, dbBranchSettings, attempt) {
3085
+ async resolveDbBranchLease(experimentId, traceId, dbBranchSettings, attempt) {
3071
3086
  return this.request(
3072
3087
  "/api/sdk/replay/resolveDbBranchLease",
3073
3088
  {
3074
- testRunId,
3089
+ ...experimentIdWireFields(experimentId),
3075
3090
  traceId,
3076
3091
  dbBranchSettings,
3077
3092
  ...attempt !== void 0 && attempt > 0 ? { attempt } : {}
@@ -3660,7 +3675,7 @@ function replayJsonReplacer(_key, value) {
3660
3675
  function serializeReplayResult(result) {
3661
3676
  return JSON.stringify(result, replayJsonReplacer, 2);
3662
3677
  }
3663
- async function preserveReplayFailure(operation, items, testRunId, testRunUrl) {
3678
+ async function preserveReplayFailure(operation, items, experimentId, experimentUrl) {
3664
3679
  try {
3665
3680
  return await operation();
3666
3681
  } catch (cause) {
@@ -3670,8 +3685,8 @@ async function preserveReplayFailure(operation, items, testRunId, testRunUrl) {
3670
3685
  throw new ReplayError(
3671
3686
  errorMessage(cause),
3672
3687
  items,
3673
- testRunId,
3674
- testRunUrl,
3688
+ experimentId,
3689
+ experimentUrl,
3675
3690
  cause
3676
3691
  );
3677
3692
  }
@@ -3725,7 +3740,7 @@ function buildMockTree(rootNode) {
3725
3740
  }
3726
3741
  return { spans };
3727
3742
  }
3728
- async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy, resolvedOverrides, replayedTraceId, attempt, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun) {
3743
+ async function processItem(httpClient, serverItem, fn, experimentId, mockStrategy, resolvedOverrides, replayedTraceId, attempt, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun) {
3729
3744
  let lease = includeDbBranchLease ? serverItem.dbBranchLease : void 0;
3730
3745
  let leaseError = includeDbBranchLease ? serverItem.dbBranchLeaseError : void 0;
3731
3746
  let dbSnapshotRef = serverItem.dbSnapshotRef;
@@ -3745,7 +3760,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
3745
3760
  let resolved2;
3746
3761
  try {
3747
3762
  resolved2 = await httpClient.resolveDbBranchLease(
3748
- testRunId,
3763
+ experimentId,
3749
3764
  originalTraceId,
3750
3765
  dbBranchSettings,
3751
3766
  attempt
@@ -3857,7 +3872,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
3857
3872
  replayStarted = performance.now();
3858
3873
  const maybePromise = runWithReplayContext(
3859
3874
  {
3860
- testRunId,
3875
+ experimentId,
3861
3876
  traceId: replayedTraceId,
3862
3877
  inputSourceSpanId: span.id,
3863
3878
  inputSourceTraceId: span.externalTraceId,
@@ -3940,14 +3955,14 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
3940
3955
  originalTraceOutline: null
3941
3956
  };
3942
3957
  }
3943
- async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds) {
3958
+ async function waitForReplayPersistence(httpClient, experimentId, replayedTraceIds) {
3944
3959
  const deferredSettled = await httpClient.settleDeferredWork(
3945
3960
  REPLAY_PERSISTENCE_TIMEOUT_MS
3946
3961
  );
3947
3962
  if (!deferredSettled) {
3948
3963
  httpClient.takeTraceDeliveries(replayedTraceIds);
3949
3964
  throw new BitfabError(
3950
- `Replay could not settle deferred span work before the deadline, so the expected span counts are incomplete (testRunId ${testRunId}).`
3965
+ `Replay could not settle deferred span work before the deadline, so the expected span counts are incomplete (experiment ${experimentId}).`
3951
3966
  );
3952
3967
  }
3953
3968
  if (!httpClient.hasClosedDeliveries(replayedTraceIds)) {
@@ -3976,7 +3991,7 @@ async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds)
3976
3991
  let missing = Object.keys(expectedSpanCounts).length;
3977
3992
  while (true) {
3978
3993
  const status = await httpClient.getReplayStatus(
3979
- testRunId,
3994
+ experimentId,
3980
3995
  expectedSpanCounts
3981
3996
  );
3982
3997
  const ready = status.traceIds ?? {};
@@ -3995,7 +4010,7 @@ async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds)
3995
4010
  }
3996
4011
  const cause = flushed ? "" : " Delivery was also not confirmed before the flush deadline, so the spans likely never reached the server.";
3997
4012
  throw new BitfabError(
3998
- `Replay traces were not fully persisted before the delivery deadline (testRunId ${testRunId}, missing ${missing} of ${Object.keys(expectedSpanCounts).length} trace(s)).${cause}`
4013
+ `Replay traces were not fully persisted before the delivery deadline (experiment ${experimentId}, missing ${missing} of ${Object.keys(expectedSpanCounts).length} trace(s)).${cause}`
3999
4014
  );
4000
4015
  }
4001
4016
  function sleepForReplayPersistence(ms) {
@@ -4062,8 +4077,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4062
4077
  }
4063
4078
  }
4064
4079
  const {
4065
- testRunId,
4066
- testRunUrl,
4080
+ experimentId,
4081
+ experimentUrl,
4067
4082
  items: serverItems
4068
4083
  } = await httpClient.startReplay(
4069
4084
  traceFunctionKey,
@@ -4096,7 +4111,14 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4096
4111
  }
4097
4112
  }
4098
4113
  const mockStrategy = options?.mock ?? "marked";
4099
- const fullTestRunUrl = `${serviceUrl}${testRunUrl}`;
4114
+ const fullExperimentUrl = `${serviceUrl}${experimentUrl}`;
4115
+ try {
4116
+ options?.onExperimentStart?.({
4117
+ experimentId,
4118
+ experimentUrl: fullExperimentUrl
4119
+ });
4120
+ } catch {
4121
+ }
4100
4122
  const resolvedOverrides = [
4101
4123
  ...normalizeMockOverrides(options?.mockOverride),
4102
4124
  ...registeredOverrides
@@ -4109,7 +4131,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4109
4131
  httpClient,
4110
4132
  serverItem,
4111
4133
  fn,
4112
- testRunId,
4134
+ experimentId,
4113
4135
  mockStrategy,
4114
4136
  resolvedOverrides,
4115
4137
  replayedTraceIds[index],
@@ -4164,7 +4186,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4164
4186
  }
4165
4187
  try {
4166
4188
  onItemFinish?.({
4167
- testRunId,
4189
+ experimentId,
4190
+ testRunId: experimentId,
4168
4191
  completed,
4169
4192
  total,
4170
4193
  succeeded,
@@ -4212,7 +4235,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4212
4235
  try {
4213
4236
  options.onItemStart?.({
4214
4237
  type: "started",
4215
- testRunId,
4238
+ experimentId,
4239
+ testRunId: experimentId,
4216
4240
  started,
4217
4241
  completed,
4218
4242
  total,
@@ -4231,21 +4255,23 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4231
4255
  } : void 0
4232
4256
  );
4233
4257
  if (options?.dryRun === true) {
4234
- await httpClient.completeReplay(testRunId).catch(() => void 0);
4258
+ await httpClient.completeReplay(experimentId).catch(() => void 0);
4235
4259
  const dryResult = {
4236
4260
  items: resultItems,
4237
- testRunId,
4238
- testRunUrl: fullTestRunUrl,
4261
+ experimentId,
4262
+ experimentUrl: fullExperimentUrl,
4263
+ testRunId: experimentId,
4264
+ testRunUrl: fullExperimentUrl,
4239
4265
  attempts
4240
4266
  };
4241
4267
  await writeReplayResultFile(dryResult);
4242
4268
  return dryResult;
4243
4269
  }
4244
4270
  const deliveredTraceIds = await preserveReplayFailure(
4245
- () => waitForReplayPersistence(httpClient, testRunId, replayedTraceIds),
4271
+ () => waitForReplayPersistence(httpClient, experimentId, replayedTraceIds),
4246
4272
  resultItems,
4247
- testRunId,
4248
- fullTestRunUrl
4273
+ experimentId,
4274
+ fullExperimentUrl
4249
4275
  );
4250
4276
  for (let index = 0; index < resultItems.length; index += 1) {
4251
4277
  const localId = replayedTraceIds[index];
@@ -4255,10 +4281,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4255
4281
  }
4256
4282
  }
4257
4283
  const completeResult = await preserveReplayFailure(
4258
- () => httpClient.completeReplay(testRunId),
4284
+ () => httpClient.completeReplay(experimentId),
4259
4285
  resultItems,
4260
- testRunId,
4261
- fullTestRunUrl
4286
+ experimentId,
4287
+ fullExperimentUrl
4262
4288
  );
4263
4289
  const serverTraceIds = completeResult.traceIds;
4264
4290
  const replayTokens = completeResult.tokens;
@@ -4285,20 +4311,20 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4285
4311
  if (completedCount > 0 && missing.length === completedCount) {
4286
4312
  const serverCount = completeResult.traceCount !== void 0 ? ` The server persisted ${completeResult.traceCount} trace(s) for this run.` : "";
4287
4313
  const cause = new BitfabError(
4288
- `Replay completed but the server has no persisted trace for any of the ${completedCount} completed item(s) (testRunId ${testRunId}).${serverCount} Trace uploads were awaited, so either the uploads failed (check for "Bitfab: Failed to create" errors above) or the replayed function is not wrapped with withSpan.`
4314
+ `Replay completed but the server has no persisted trace for any of the ${completedCount} completed item(s) (experiment ${experimentId}).${serverCount} Trace uploads were awaited, so either the uploads failed (check for "Bitfab: Failed to create" errors above) or the replayed function is not wrapped with withSpan.`
4289
4315
  );
4290
4316
  throw new ReplayError(
4291
4317
  cause.message,
4292
4318
  resultItems,
4293
- testRunId,
4294
- fullTestRunUrl,
4319
+ experimentId,
4320
+ fullExperimentUrl,
4295
4321
  cause
4296
4322
  );
4297
4323
  }
4298
4324
  if (missing.length > 0) {
4299
4325
  try {
4300
4326
  console.error(
4301
- `Bitfab: server has no persisted trace for ${missing.length} of ${completedCount} completed replay item(s) (testRunId ${testRunId}). Their replay token usage is unavailable and they cannot be labeled.`
4327
+ `Bitfab: server has no persisted trace for ${missing.length} of ${completedCount} completed replay item(s) (experiment ${experimentId}). Their replay token usage is unavailable and they cannot be labeled.`
4302
4328
  );
4303
4329
  } catch {
4304
4330
  }
@@ -4306,8 +4332,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4306
4332
  }
4307
4333
  const result = {
4308
4334
  items: resultItems,
4309
- testRunId,
4310
- testRunUrl: fullTestRunUrl,
4335
+ experimentId,
4336
+ experimentUrl: fullExperimentUrl,
4337
+ testRunId: experimentId,
4338
+ testRunUrl: fullExperimentUrl,
4311
4339
  attempts
4312
4340
  };
4313
4341
  await writeReplayResultFile(result);
@@ -4315,7 +4343,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4315
4343
  try {
4316
4344
  options?.onProgress?.({
4317
4345
  type: "complete",
4318
- testRunId,
4346
+ experimentId,
4347
+ testRunId: experimentId,
4319
4348
  completed: total,
4320
4349
  total,
4321
4350
  succeeded,
@@ -4365,13 +4394,15 @@ var init_replay = __esm({
4365
4394
  MAX_REPLAY_ATTEMPTS = 100;
4366
4395
  BITFAB_PROGRESS_PREFIX = "@@bitfab:progress ";
4367
4396
  ReplayError = class extends BitfabError {
4368
- constructor(message, items, testRunId, testRunUrl, cause) {
4369
- super(message, testRunUrl);
4397
+ constructor(message, items, experimentId, experimentUrl, cause) {
4398
+ super(message, experimentUrl);
4370
4399
  this.items = items;
4371
- this.testRunId = testRunId;
4372
- this.testRunUrl = testRunUrl;
4400
+ this.experimentId = experimentId;
4401
+ this.experimentUrl = experimentUrl;
4373
4402
  this.cause = cause;
4374
4403
  this.name = "ReplayError";
4404
+ this.testRunId = experimentId;
4405
+ this.testRunUrl = experimentUrl;
4375
4406
  }
4376
4407
  };
4377
4408
  DbBranchReplayError = class extends BitfabError {
@@ -4390,6 +4421,8 @@ var init_replay = __esm({
4390
4421
  var index_exports = {};
4391
4422
  __export(index_exports, {
4392
4423
  AssertionCategoriesClient: () => AssertionCategoriesClient,
4424
+ AssertionGenerationError: () => AssertionGenerationError,
4425
+ AssertionGenerationTimeoutError: () => AssertionGenerationTimeoutError,
4393
4426
  BITFAB_PROGRESS_PREFIX: () => BITFAB_PROGRESS_PREFIX,
4394
4427
  Bitfab: () => Bitfab,
4395
4428
  BitfabClaudeAgentHandler: () => BitfabClaudeAgentHandler,
@@ -4405,6 +4438,8 @@ __export(index_exports, {
4405
4438
  DEFAULT_SERVICE_URL: () => DEFAULT_SERVICE_URL,
4406
4439
  DatasetsClient: () => DatasetsClient,
4407
4440
  DbBranchReplayError: () => DbBranchReplayError,
4441
+ GraderRerunError: () => GraderRerunError,
4442
+ GraderRerunTimeoutError: () => GraderRerunTimeoutError,
4408
4443
  GradersClient: () => GradersClient,
4409
4444
  HttpClient: () => HttpClient,
4410
4445
  LabelsClient: () => LabelsClient,
@@ -5832,11 +5867,39 @@ init_constants();
5832
5867
 
5833
5868
  // src/datasets.ts
5834
5869
  var DEFAULT_RERUN_TIMEOUT_MS = 9e4;
5835
- var DEFAULT_RERUN_POLL_INTERVAL_MS = 1e3;
5870
+ var RERUN_POLL_INTERVAL_MS = 1e3;
5836
5871
  var TERMINAL_RERUN_STATUSES = /* @__PURE__ */ new Set([
5837
5872
  "completed",
5838
5873
  "errored"
5839
5874
  ]);
5875
+ var GraderRerunError = class extends Error {
5876
+ constructor(message, run) {
5877
+ super(message);
5878
+ this.run = run;
5879
+ this.name = "GraderRerunError";
5880
+ }
5881
+ };
5882
+ var GraderRerunTimeoutError = class extends GraderRerunError {
5883
+ constructor(datasetId, run, timeoutMs) {
5884
+ super(
5885
+ `Grader re-run ${run.id} on dataset ${datasetId} was still ${run.status} after ${timeoutMs}ms. It keeps running on Bitfab; read it later with getGraderRerun.`,
5886
+ run
5887
+ );
5888
+ this.name = "GraderRerunTimeoutError";
5889
+ }
5890
+ };
5891
+ function finishedRerun(datasetId, run, joinedExisting) {
5892
+ if (!TERMINAL_RERUN_STATUSES.has(run.status)) {
5893
+ return null;
5894
+ }
5895
+ if (run.status === "errored") {
5896
+ throw new GraderRerunError(
5897
+ `Grader re-run ${run.id} on dataset ${datasetId} errored: ${run.error ?? "no reason recorded"}`,
5898
+ run
5899
+ );
5900
+ }
5901
+ return { run, joinedExisting };
5902
+ }
5840
5903
  function sleep(ms) {
5841
5904
  return new Promise((resolve) => setTimeout(resolve, ms));
5842
5905
  }
@@ -5936,28 +5999,25 @@ var DatasetsClient = class {
5936
5999
  { graderIds }
5937
6000
  );
5938
6001
  }
5939
- /**
5940
- * Re-run graders over every trace in the dataset. Defaults to every assigned
5941
- * grader; an unassigned id is rejected. Waits for the run to finish (up to
5942
- * `timeoutMs`, default 90s) unless `wait` is `false`, and returns the last
5943
- * run state seen either way. A request matching an in-flight run joins it.
5944
- */
5945
6002
  async rerunGraders(datasetId, options = {}) {
5946
6003
  const started = await this.httpClient.request(
5947
6004
  datasetPath(datasetId, "/rerunGraders"),
5948
6005
  options.graderIds === void 0 ? {} : { graderIds: options.graderIds }
5949
6006
  );
5950
- if (options.wait === false) {
5951
- return started;
5952
- }
5953
- const deadline = Date.now() + (options.timeoutMs ?? DEFAULT_RERUN_TIMEOUT_MS);
5954
- const interval = options.pollIntervalMs ?? DEFAULT_RERUN_POLL_INTERVAL_MS;
6007
+ const timeoutMs = options.timeoutMs ?? DEFAULT_RERUN_TIMEOUT_MS;
6008
+ const deadline = Date.now() + timeoutMs;
5955
6009
  let run = started.run;
5956
- while (!TERMINAL_RERUN_STATUSES.has(run.status) && Date.now() < deadline) {
5957
- await sleep(interval);
6010
+ while (true) {
6011
+ const finished = finishedRerun(datasetId, run, started.joinedExisting);
6012
+ if (finished !== null) {
6013
+ return finished;
6014
+ }
6015
+ if (Date.now() >= deadline) {
6016
+ throw new GraderRerunTimeoutError(datasetId, run, timeoutMs);
6017
+ }
6018
+ await sleep(RERUN_POLL_INTERVAL_MS);
5958
6019
  run = await this.getGraderRerun(datasetId, run.id) ?? run;
5959
6020
  }
5960
- return { run, joinedExisting: started.joinedExisting };
5961
6021
  }
5962
6022
  /**
5963
6023
  * The dataset's active grader re-run, or the run named by `runId`. Returns
@@ -6028,6 +6088,7 @@ var GradersClient = class {
6028
6088
  init_http();
6029
6089
 
6030
6090
  // src/labels.ts
6091
+ init_http();
6031
6092
  var LABELS_PATH = "/api/sdk/traces/labels";
6032
6093
  var HUMAN_LABELS_PATH = "/api/sdk/traces/labels/human";
6033
6094
  function labelEvidencePath(traceId, assertionId) {
@@ -6037,27 +6098,27 @@ var LabelsClient = class {
6037
6098
  constructor(httpClient) {
6038
6099
  this.httpClient = httpClient;
6039
6100
  }
6040
- async save(update, testRunId) {
6041
- const [outcome] = await this.saveAll([update], testRunId);
6101
+ async save(update, experimentId) {
6102
+ const [outcome] = await this.saveAll([update], experimentId);
6042
6103
  return outcome;
6043
6104
  }
6044
- async saveAll(updates, testRunId) {
6105
+ async saveAll(updates, experimentId) {
6045
6106
  const response = await this.httpClient.request(
6046
6107
  LABELS_PATH,
6047
6108
  {
6048
6109
  labels: updates,
6049
- ...testRunId === void 0 ? {} : { testRunId }
6110
+ ...experimentId === void 0 ? {} : experimentIdWireFields(experimentId)
6050
6111
  }
6051
6112
  );
6052
6113
  return response.labels;
6053
6114
  }
6054
6115
  /** Withhold a verdict for a trace or replay attempt whose check did not run. */
6055
- async skip(target, testRunId) {
6056
- return this.save({ ...target, skip: true }, testRunId);
6116
+ async skip(target, experimentId) {
6117
+ return this.save({ ...target, skip: true }, experimentId);
6057
6118
  }
6058
6119
  /** Clear a previous verdict for a trace, replay attempt, or assertion. */
6059
- async archive(target, testRunId) {
6060
- return this.save({ ...target, archive: true }, testRunId);
6120
+ async archive(target, experimentId) {
6121
+ return this.save({ ...target, archive: true }, experimentId);
6061
6122
  }
6062
6123
  async saveHuman(update) {
6063
6124
  const [outcome] = await this.saveHumanAll([update]);
@@ -7813,6 +7874,46 @@ init_traceMetadata();
7813
7874
  // src/traces.ts
7814
7875
  var ASSERTIONS_PATH = "/api/sdk/traces/assertions";
7815
7876
  var SEARCH_PATH = "/api/sdk/traces/search";
7877
+ var DEFAULT_GENERATION_TIMEOUT_MS = 6e5;
7878
+ var GENERATION_POLL_INTERVAL_MS = 2e3;
7879
+ var AssertionGenerationError = class extends Error {
7880
+ constructor(message, traceId) {
7881
+ super(message);
7882
+ this.traceId = traceId;
7883
+ this.name = "AssertionGenerationError";
7884
+ }
7885
+ };
7886
+ var AssertionGenerationTimeoutError = class extends AssertionGenerationError {
7887
+ constructor(traceId, timeoutMs) {
7888
+ super(
7889
+ `Assertion generation for trace ${traceId} did not finish within ${timeoutMs}ms. Generation keeps running on Bitfab. Call getAssertions and read its generation field to check its status.`,
7890
+ traceId
7891
+ );
7892
+ this.name = "AssertionGenerationTimeoutError";
7893
+ }
7894
+ };
7895
+ function sleep2(ms) {
7896
+ return new Promise((resolve) => setTimeout(resolve, ms));
7897
+ }
7898
+ function isGenerationFinished(traceId, generation) {
7899
+ if (generation === null || generation.status === "running") {
7900
+ return false;
7901
+ }
7902
+ if (generation.status === "failed") {
7903
+ throw new AssertionGenerationError(
7904
+ `Assertion generation for trace ${traceId} failed: ${generation.error ?? "no reason recorded"}. Pass regenerate: true to try again.`,
7905
+ traceId
7906
+ );
7907
+ }
7908
+ return true;
7909
+ }
7910
+ function draftsInGenerationOrder(assertions, assertionIds) {
7911
+ const byId = new Map(assertions.map((assertion) => [assertion.id, assertion]));
7912
+ return assertionIds.flatMap((id) => {
7913
+ const assertion = byId.get(id);
7914
+ return assertion === void 0 ? [] : [assertion];
7915
+ });
7916
+ }
7816
7917
  function traceAssertionsPath(traceId, suffix = "") {
7817
7918
  return `/api/sdk/traces/${encodeURIComponent(traceId)}/assertions${suffix}`;
7818
7919
  }
@@ -7844,6 +7945,34 @@ var TracesClient = class {
7844
7945
  });
7845
7946
  return response.assertions;
7846
7947
  }
7948
+ async generateAssertions(traceId, options = {}) {
7949
+ const started = await this.httpClient.request(
7950
+ traceAssertionsPath(traceId, "/generate"),
7951
+ {
7952
+ ...options.guidance === void 0 ? {} : { guidance: options.guidance },
7953
+ ...options.categoryIds === void 0 ? {} : { categoryIds: options.categoryIds },
7954
+ ...options.regenerate === void 0 ? {} : { regenerate: options.regenerate }
7955
+ }
7956
+ );
7957
+ if (isGenerationFinished(traceId, started.generation)) {
7958
+ return started.assertions;
7959
+ }
7960
+ const timeoutMs = options.timeoutMs ?? DEFAULT_GENERATION_TIMEOUT_MS;
7961
+ const deadline = Date.now() + timeoutMs;
7962
+ while (true) {
7963
+ if (Date.now() >= deadline) {
7964
+ throw new AssertionGenerationTimeoutError(traceId, timeoutMs);
7965
+ }
7966
+ await sleep2(GENERATION_POLL_INTERVAL_MS);
7967
+ const read2 = await this.getAssertions(traceId);
7968
+ if (isGenerationFinished(traceId, read2.generation)) {
7969
+ return draftsInGenerationOrder(
7970
+ read2.assertions,
7971
+ read2.generation.assertionIds
7972
+ );
7973
+ }
7974
+ }
7975
+ }
7847
7976
  async archiveAssertions(params) {
7848
7977
  const response = await this.httpClient.request(
7849
7978
  traceAssertionsPath(params.traceId, "/archive"),
@@ -8347,6 +8476,15 @@ var BitfabVercelAiHandler = class {
8347
8476
 
8348
8477
  // src/client.ts
8349
8478
  init_warnOnce();
8479
+ function resolveSpanExperimentId(options) {
8480
+ const { experimentId, testRunId } = options;
8481
+ if (experimentId !== void 0 && testRunId !== void 0 && experimentId !== testRunId) {
8482
+ throw new BitfabError(
8483
+ `experimentId ("${experimentId}") and its deprecated alias testRunId ("${testRunId}") were both passed with different values. Pass only experimentId.`
8484
+ );
8485
+ }
8486
+ return experimentId ?? testRunId;
8487
+ }
8350
8488
  var TRUNCATION_LIMIT_ORDER = [
8351
8489
  "max_depth",
8352
8490
  "max_captured_subtree_spans"
@@ -8936,13 +9074,12 @@ var Bitfab = class {
8936
9074
  "bitfab.node({ capture: false }) cannot use mockOnReplay: true because an uncaptured node has no recorded output."
8937
9075
  );
8938
9076
  }
9077
+ const experimentId = resolveSpanExperimentId(options);
8939
9078
  return {
8940
9079
  capture,
8941
9080
  type: options.type ?? "custom",
8942
9081
  ...options.name !== void 0 && { name: options.name },
8943
- ...options.testRunId !== void 0 && {
8944
- testRunId: options.testRunId
8945
- },
9082
+ ...experimentId !== void 0 && { experimentId },
8946
9083
  ...options.mockOnReplay !== void 0 && {
8947
9084
  mockOnReplay: options.mockOnReplay
8948
9085
  },
@@ -9077,6 +9214,7 @@ var Bitfab = class {
9077
9214
  return invokeWithoutNode();
9078
9215
  }
9079
9216
  const mockOnReplay = nodeConfiguration?.mockOnReplay ?? options.mockOnReplayDefault;
9217
+ const nodeExperimentId = nodeConfiguration === void 0 ? void 0 : resolveSpanExperimentId(nodeConfiguration);
9080
9218
  const childOptions = {
9081
9219
  name: spanName,
9082
9220
  type: nodeConfiguration?.type ?? "function",
@@ -9087,8 +9225,8 @@ var Bitfab = class {
9087
9225
  captureContent: true,
9088
9226
  autoTraceDefinition: definition,
9089
9227
  ...declaredNode && { declaredNode },
9090
- ...nodeConfiguration?.testRunId !== void 0 && {
9091
- testRunId: nodeConfiguration.testRunId
9228
+ ...nodeExperimentId !== void 0 && {
9229
+ experimentId: nodeExperimentId
9092
9230
  },
9093
9231
  ...mockOnReplay !== void 0 && {
9094
9232
  mockOnReplay
@@ -9749,6 +9887,7 @@ var Bitfab = class {
9749
9887
  const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
9750
9888
  const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
9751
9889
  const self = this;
9890
+ const optionExperimentId = resolveSpanExperimentId(options);
9752
9891
  const fnIsAsyncFunction = fn.constructor.name === "AsyncFunction";
9753
9892
  const fnReturnsPromise = fnIsAsyncFunction || (() => {
9754
9893
  try {
@@ -9851,7 +9990,7 @@ var Bitfab = class {
9851
9990
  const inputs = args;
9852
9991
  const startedAt = nowIsoTimestamp();
9853
9992
  const replayCtxAtStart = getReplayContext();
9854
- const testRunId = replayCtxAtStart?.testRunId ?? options.testRunId;
9993
+ const experimentId = replayCtxAtStart?.experimentId ?? optionExperimentId;
9855
9994
  if (isRootSpan && !activeTraceStates.has(traceId)) {
9856
9995
  const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
9857
9996
  self.simulationPlan.refresh();
@@ -9860,7 +9999,7 @@ var Bitfab = class {
9860
9999
  traceFunctionKey,
9861
10000
  startedAt,
9862
10001
  contexts: [],
9863
- ...testRunId !== void 0 && { testRunId },
10002
+ ...experimentId !== void 0 && { experimentId },
9864
10003
  ...replayCtxAtStart?.inputSourceTraceId && {
9865
10004
  inputSourceTraceId: replayCtxAtStart.inputSourceTraceId
9866
10005
  },
@@ -9906,7 +10045,7 @@ var Bitfab = class {
9906
10045
  contexts: newContext.contexts,
9907
10046
  prompt: newContext.prompt,
9908
10047
  endedAt,
9909
- ...testRunId !== void 0 && { testRunId },
10048
+ ...experimentId !== void 0 && { experimentId },
9910
10049
  ...replayCtx?.inputSourceSpanId && {
9911
10050
  inputSourceSpanId: replayCtx.inputSourceSpanId
9912
10051
  }
@@ -9927,7 +10066,7 @@ var Bitfab = class {
9927
10066
  name: traceState?.name,
9928
10067
  metadata: callerTraceMetadata(traceId),
9929
10068
  contexts: traceState?.contexts ?? [],
9930
- testRunId: traceState?.testRunId,
10069
+ experimentId: traceState?.experimentId,
9931
10070
  inputSourceTraceId: traceState?.inputSourceTraceId,
9932
10071
  replayAttempt: traceState?.replayAttempt,
9933
10072
  dbSnapshotRef: traceState?.dbSnapshotRef,
@@ -10398,7 +10537,7 @@ var Bitfab = class {
10398
10537
  completed: true,
10399
10538
  ...params.dropped && { dropped: true },
10400
10539
  ...params.sessionId && { sessionId: params.sessionId },
10401
- ...params.testRunId && { testRunId: params.testRunId }
10540
+ ...params.experimentId !== void 0 && experimentIdWireFields(params.experimentId)
10402
10541
  });
10403
10542
  }
10404
10543
  sendsSpanOnCompletion(context) {
@@ -10510,7 +10649,7 @@ var Bitfab = class {
10510
10649
  },
10511
10650
  ...params.declaredNode === true && { [DECLARED_NODE_FIELD]: true },
10512
10651
  rawSpan: externalSpan,
10513
- ...params.testRunId && { testRunId: params.testRunId },
10652
+ ...params.experimentId !== void 0 && experimentIdWireFields(params.experimentId),
10514
10653
  ...params.mocked && { mocked: true },
10515
10654
  ...params.mockTarget && { mockTarget: params.mockTarget },
10516
10655
  ...params.mockSource && { mockSource: params.mockSource }
@@ -11062,6 +11201,8 @@ function resolveTraceFunctionKey(registration) {
11062
11201
  // Annotate the CommonJS export names for ESM import in node:
11063
11202
  0 && (module.exports = {
11064
11203
  AssertionCategoriesClient,
11204
+ AssertionGenerationError,
11205
+ AssertionGenerationTimeoutError,
11065
11206
  BITFAB_PROGRESS_PREFIX,
11066
11207
  Bitfab,
11067
11208
  BitfabClaudeAgentHandler,
@@ -11077,6 +11218,8 @@ function resolveTraceFunctionKey(registration) {
11077
11218
  DEFAULT_SERVICE_URL,
11078
11219
  DatasetsClient,
11079
11220
  DbBranchReplayError,
11221
+ GraderRerunError,
11222
+ GraderRerunTimeoutError,
11080
11223
  GradersClient,
11081
11224
  HttpClient,
11082
11225
  LabelsClient,