bitfab 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-TAEWAXPA.js → chunk-3D5EEY47.js} +54 -38
  6. package/dist/chunk-3D5EEY47.js.map +1 -0
  7. package/dist/{chunk-OTQA6MCF.js → chunk-4F46OBRG.js} +18 -10
  8. package/dist/chunk-4F46OBRG.js.map +1 -0
  9. package/dist/{chunk-ETZUSSF6.js → chunk-5DNB7EN7.js} +1 -1
  10. package/dist/chunk-5DNB7EN7.js.map +1 -0
  11. package/dist/{chunk-QIBFNIIF.js → chunk-5MN6WIMW.js} +29 -14
  12. package/dist/chunk-5MN6WIMW.js.map +1 -0
  13. package/dist/{chunk-7SPNHC33.js → chunk-GJCZABUC.js} +29 -14
  14. package/dist/chunk-GJCZABUC.js.map +1 -0
  15. package/dist/{chunk-UYAQRSCB.js → chunk-NVOQ7YMI.js} +149 -41
  16. package/dist/chunk-NVOQ7YMI.js.map +1 -0
  17. package/dist/{http-F6QV3YLY.js → http-2QEMP27E.js} +4 -2
  18. package/dist/{http-XDKEIZNV.js → http-OXKVRA3E.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-WLJFWS6T.js → replay-WYPFFXSU.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-7SPNHC33.js.map +0 -1
  35. package/dist/chunk-ETZUSSF6.js.map +0 -1
  36. package/dist/chunk-OTQA6MCF.js.map +0 -1
  37. package/dist/chunk-QIBFNIIF.js.map +0 -1
  38. package/dist/chunk-TAEWAXPA.js.map +0 -1
  39. package/dist/chunk-UYAQRSCB.js.map +0 -1
  40. /package/dist/{http-F6QV3YLY.js.map → http-2QEMP27E.js.map} +0 -0
  41. /package/dist/{http-XDKEIZNV.js.map → http-OXKVRA3E.js.map} +0 -0
  42. /package/dist/{replay-WLJFWS6T.js.map → replay-WYPFFXSU.js.map} +0 -0
package/dist/node.cjs CHANGED
@@ -97,7 +97,7 @@ var __version__, __packageName__;
97
97
  var init_version_generated = __esm({
98
98
  "src/version.generated.ts"() {
99
99
  "use strict";
100
- __version__ = "0.58.5";
100
+ __version__ = "0.59.1";
101
101
  __packageName__ = "bitfab";
102
102
  }
103
103
  });
@@ -2120,6 +2120,7 @@ __export(http_exports, {
2120
2120
  HttpClient: () => HttpClient,
2121
2121
  awaitOnExit: () => awaitOnExit,
2122
2122
  awaitPendingRequests: () => awaitPendingRequests,
2123
+ experimentIdWireFields: () => experimentIdWireFields,
2123
2124
  flushTraces: () => flushTraces,
2124
2125
  parseRetryAfterMs: () => parseRetryAfterMs,
2125
2126
  serializePayloadBody: () => serializePayloadBody
@@ -2288,6 +2289,9 @@ function warnDroppedExternalTrace(error) {
2288
2289
  `a trace was dropped because the send step failed: ${describeError(error)}`
2289
2290
  );
2290
2291
  }
2292
+ function experimentIdWireFields(experimentId) {
2293
+ return { experimentId, testRunId: experimentId };
2294
+ }
2291
2295
  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;
2292
2296
  var init_http = __esm({
2293
2297
  "src/http.ts"() {
@@ -2892,7 +2896,7 @@ var init_http = __esm({
2892
2896
  }
2893
2897
  /**
2894
2898
  * Start a replay session by fetching historical traces.
2895
- * Blocking call - creates a test run and returns lightweight item references.
2899
+ * Blocking call - creates an experiment and returns lightweight item references.
2896
2900
  */
2897
2901
  async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetIds, graderIds, dbBranchSettings, attempts, includeOriginalMetadata, onlyWithAssertions, judgeAssertions) {
2898
2902
  const payload = { traceFunctionKey };
@@ -2948,9 +2952,20 @@ var init_http = __esm({
2948
2952
  payload.git = git;
2949
2953
  }
2950
2954
  const timeout = includeDbBranchLease ? REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS : 3e4;
2951
- return this.request("/api/sdk/replay/start", payload, {
2952
- timeout
2953
- });
2955
+ const response = await this.request(
2956
+ "/api/sdk/replay/start",
2957
+ payload,
2958
+ { timeout }
2959
+ );
2960
+ const { testRunId, testRunUrl, ...rest } = response;
2961
+ const experimentId = response.experimentId ?? testRunId;
2962
+ const experimentUrl = response.experimentUrl ?? testRunUrl;
2963
+ if (experimentId === void 0 || experimentUrl === void 0) {
2964
+ throw new BitfabError(
2965
+ "Replay start response is missing the experiment ID or URL."
2966
+ );
2967
+ }
2968
+ return { ...rest, experimentId, experimentUrl };
2954
2969
  }
2955
2970
  /**
2956
2971
  * Fetch an external span by ID.
@@ -3047,21 +3062,21 @@ var init_http = __esm({
3047
3062
  * has a final status AND at least that many persisted spans, which is what
3048
3063
  * makes this a real barrier rather than a "the row exists" check.
3049
3064
  */
3050
- async getReplayStatus(testRunId, expectedSpanCounts) {
3065
+ async getReplayStatus(experimentId, expectedSpanCounts) {
3051
3066
  return this.request(
3052
3067
  "/api/sdk/replay/status",
3053
- { testRunId, expectedSpanCounts },
3068
+ { ...experimentIdWireFields(experimentId), expectedSpanCounts },
3054
3069
  { timeout: 3e4 }
3055
3070
  );
3056
3071
  }
3057
3072
  /**
3058
- * Mark a replay test run as completed.
3073
+ * Mark a replay experiment as completed.
3059
3074
  * Blocking call.
3060
3075
  */
3061
- async completeReplay(testRunId) {
3076
+ async completeReplay(experimentId) {
3062
3077
  return this.request(
3063
3078
  "/api/sdk/replay/complete",
3064
- { testRunId },
3079
+ experimentIdWireFields(experimentId),
3065
3080
  { timeout: REPLAY_COMPLETE_REQUEST_TIMEOUT_MS }
3066
3081
  );
3067
3082
  }
@@ -3083,11 +3098,11 @@ var init_http = __esm({
3083
3098
  * snapshot + preview branch and polls operations to readiness, which
3084
3099
  * can take seconds.
3085
3100
  */
3086
- async resolveDbBranchLease(testRunId, traceId, dbBranchSettings, attempt) {
3101
+ async resolveDbBranchLease(experimentId, traceId, dbBranchSettings, attempt) {
3087
3102
  return this.request(
3088
3103
  "/api/sdk/replay/resolveDbBranchLease",
3089
3104
  {
3090
- testRunId,
3105
+ ...experimentIdWireFields(experimentId),
3091
3106
  traceId,
3092
3107
  dbBranchSettings,
3093
3108
  ...attempt !== void 0 && attempt > 0 ? { attempt } : {}
@@ -3676,7 +3691,7 @@ function replayJsonReplacer(_key, value) {
3676
3691
  function serializeReplayResult(result) {
3677
3692
  return JSON.stringify(result, replayJsonReplacer, 2);
3678
3693
  }
3679
- async function preserveReplayFailure(operation, items, testRunId, testRunUrl) {
3694
+ async function preserveReplayFailure(operation, items, experimentId, experimentUrl) {
3680
3695
  try {
3681
3696
  return await operation();
3682
3697
  } catch (cause) {
@@ -3686,8 +3701,8 @@ async function preserveReplayFailure(operation, items, testRunId, testRunUrl) {
3686
3701
  throw new ReplayError(
3687
3702
  errorMessage(cause),
3688
3703
  items,
3689
- testRunId,
3690
- testRunUrl,
3704
+ experimentId,
3705
+ experimentUrl,
3691
3706
  cause
3692
3707
  );
3693
3708
  }
@@ -3741,7 +3756,7 @@ function buildMockTree(rootNode) {
3741
3756
  }
3742
3757
  return { spans };
3743
3758
  }
3744
- async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy, resolvedOverrides, replayedTraceId, attempt, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun) {
3759
+ async function processItem(httpClient, serverItem, fn, experimentId, mockStrategy, resolvedOverrides, replayedTraceId, attempt, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun) {
3745
3760
  let lease = includeDbBranchLease ? serverItem.dbBranchLease : void 0;
3746
3761
  let leaseError = includeDbBranchLease ? serverItem.dbBranchLeaseError : void 0;
3747
3762
  let dbSnapshotRef = serverItem.dbSnapshotRef;
@@ -3761,7 +3776,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
3761
3776
  let resolved2;
3762
3777
  try {
3763
3778
  resolved2 = await httpClient.resolveDbBranchLease(
3764
- testRunId,
3779
+ experimentId,
3765
3780
  originalTraceId,
3766
3781
  dbBranchSettings,
3767
3782
  attempt
@@ -3873,7 +3888,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
3873
3888
  replayStarted = performance.now();
3874
3889
  const maybePromise = runWithReplayContext(
3875
3890
  {
3876
- testRunId,
3891
+ experimentId,
3877
3892
  traceId: replayedTraceId,
3878
3893
  inputSourceSpanId: span.id,
3879
3894
  inputSourceTraceId: span.externalTraceId,
@@ -3956,14 +3971,14 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
3956
3971
  originalTraceOutline: null
3957
3972
  };
3958
3973
  }
3959
- async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds) {
3974
+ async function waitForReplayPersistence(httpClient, experimentId, replayedTraceIds) {
3960
3975
  const deferredSettled = await httpClient.settleDeferredWork(
3961
3976
  REPLAY_PERSISTENCE_TIMEOUT_MS
3962
3977
  );
3963
3978
  if (!deferredSettled) {
3964
3979
  httpClient.takeTraceDeliveries(replayedTraceIds);
3965
3980
  throw new BitfabError(
3966
- `Replay could not settle deferred span work before the deadline, so the expected span counts are incomplete (testRunId ${testRunId}).`
3981
+ `Replay could not settle deferred span work before the deadline, so the expected span counts are incomplete (experiment ${experimentId}).`
3967
3982
  );
3968
3983
  }
3969
3984
  if (!httpClient.hasClosedDeliveries(replayedTraceIds)) {
@@ -3992,7 +4007,7 @@ async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds)
3992
4007
  let missing = Object.keys(expectedSpanCounts).length;
3993
4008
  while (true) {
3994
4009
  const status = await httpClient.getReplayStatus(
3995
- testRunId,
4010
+ experimentId,
3996
4011
  expectedSpanCounts
3997
4012
  );
3998
4013
  const ready = status.traceIds ?? {};
@@ -4011,7 +4026,7 @@ async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds)
4011
4026
  }
4012
4027
  const cause = flushed ? "" : " Delivery was also not confirmed before the flush deadline, so the spans likely never reached the server.";
4013
4028
  throw new BitfabError(
4014
- `Replay traces were not fully persisted before the delivery deadline (testRunId ${testRunId}, missing ${missing} of ${Object.keys(expectedSpanCounts).length} trace(s)).${cause}`
4029
+ `Replay traces were not fully persisted before the delivery deadline (experiment ${experimentId}, missing ${missing} of ${Object.keys(expectedSpanCounts).length} trace(s)).${cause}`
4015
4030
  );
4016
4031
  }
4017
4032
  function sleepForReplayPersistence(ms) {
@@ -4078,8 +4093,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4078
4093
  }
4079
4094
  }
4080
4095
  const {
4081
- testRunId,
4082
- testRunUrl,
4096
+ experimentId,
4097
+ experimentUrl,
4083
4098
  items: serverItems
4084
4099
  } = await httpClient.startReplay(
4085
4100
  traceFunctionKey,
@@ -4112,7 +4127,14 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4112
4127
  }
4113
4128
  }
4114
4129
  const mockStrategy = options?.mock ?? "marked";
4115
- const fullTestRunUrl = `${serviceUrl}${testRunUrl}`;
4130
+ const fullExperimentUrl = `${serviceUrl}${experimentUrl}`;
4131
+ try {
4132
+ options?.onExperimentStart?.({
4133
+ experimentId,
4134
+ experimentUrl: fullExperimentUrl
4135
+ });
4136
+ } catch {
4137
+ }
4116
4138
  const resolvedOverrides = [
4117
4139
  ...normalizeMockOverrides(options?.mockOverride),
4118
4140
  ...registeredOverrides
@@ -4125,7 +4147,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4125
4147
  httpClient,
4126
4148
  serverItem,
4127
4149
  fn,
4128
- testRunId,
4150
+ experimentId,
4129
4151
  mockStrategy,
4130
4152
  resolvedOverrides,
4131
4153
  replayedTraceIds[index],
@@ -4180,7 +4202,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4180
4202
  }
4181
4203
  try {
4182
4204
  onItemFinish?.({
4183
- testRunId,
4205
+ experimentId,
4206
+ testRunId: experimentId,
4184
4207
  completed,
4185
4208
  total,
4186
4209
  succeeded,
@@ -4228,7 +4251,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4228
4251
  try {
4229
4252
  options.onItemStart?.({
4230
4253
  type: "started",
4231
- testRunId,
4254
+ experimentId,
4255
+ testRunId: experimentId,
4232
4256
  started,
4233
4257
  completed,
4234
4258
  total,
@@ -4247,21 +4271,23 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4247
4271
  } : void 0
4248
4272
  );
4249
4273
  if (options?.dryRun === true) {
4250
- await httpClient.completeReplay(testRunId).catch(() => void 0);
4274
+ await httpClient.completeReplay(experimentId).catch(() => void 0);
4251
4275
  const dryResult = {
4252
4276
  items: resultItems,
4253
- testRunId,
4254
- testRunUrl: fullTestRunUrl,
4277
+ experimentId,
4278
+ experimentUrl: fullExperimentUrl,
4279
+ testRunId: experimentId,
4280
+ testRunUrl: fullExperimentUrl,
4255
4281
  attempts
4256
4282
  };
4257
4283
  await writeReplayResultFile(dryResult);
4258
4284
  return dryResult;
4259
4285
  }
4260
4286
  const deliveredTraceIds = await preserveReplayFailure(
4261
- () => waitForReplayPersistence(httpClient, testRunId, replayedTraceIds),
4287
+ () => waitForReplayPersistence(httpClient, experimentId, replayedTraceIds),
4262
4288
  resultItems,
4263
- testRunId,
4264
- fullTestRunUrl
4289
+ experimentId,
4290
+ fullExperimentUrl
4265
4291
  );
4266
4292
  for (let index = 0; index < resultItems.length; index += 1) {
4267
4293
  const localId = replayedTraceIds[index];
@@ -4271,10 +4297,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4271
4297
  }
4272
4298
  }
4273
4299
  const completeResult = await preserveReplayFailure(
4274
- () => httpClient.completeReplay(testRunId),
4300
+ () => httpClient.completeReplay(experimentId),
4275
4301
  resultItems,
4276
- testRunId,
4277
- fullTestRunUrl
4302
+ experimentId,
4303
+ fullExperimentUrl
4278
4304
  );
4279
4305
  const serverTraceIds = completeResult.traceIds;
4280
4306
  const replayTokens = completeResult.tokens;
@@ -4301,20 +4327,20 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4301
4327
  if (completedCount > 0 && missing.length === completedCount) {
4302
4328
  const serverCount = completeResult.traceCount !== void 0 ? ` The server persisted ${completeResult.traceCount} trace(s) for this run.` : "";
4303
4329
  const cause = new BitfabError(
4304
- `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.`
4330
+ `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.`
4305
4331
  );
4306
4332
  throw new ReplayError(
4307
4333
  cause.message,
4308
4334
  resultItems,
4309
- testRunId,
4310
- fullTestRunUrl,
4335
+ experimentId,
4336
+ fullExperimentUrl,
4311
4337
  cause
4312
4338
  );
4313
4339
  }
4314
4340
  if (missing.length > 0) {
4315
4341
  try {
4316
4342
  console.error(
4317
- `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.`
4343
+ `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.`
4318
4344
  );
4319
4345
  } catch {
4320
4346
  }
@@ -4322,8 +4348,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4322
4348
  }
4323
4349
  const result = {
4324
4350
  items: resultItems,
4325
- testRunId,
4326
- testRunUrl: fullTestRunUrl,
4351
+ experimentId,
4352
+ experimentUrl: fullExperimentUrl,
4353
+ testRunId: experimentId,
4354
+ testRunUrl: fullExperimentUrl,
4327
4355
  attempts
4328
4356
  };
4329
4357
  await writeReplayResultFile(result);
@@ -4331,7 +4359,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4331
4359
  try {
4332
4360
  options?.onProgress?.({
4333
4361
  type: "complete",
4334
- testRunId,
4362
+ experimentId,
4363
+ testRunId: experimentId,
4335
4364
  completed: total,
4336
4365
  total,
4337
4366
  succeeded,
@@ -4381,13 +4410,15 @@ var init_replay = __esm({
4381
4410
  MAX_REPLAY_ATTEMPTS = 100;
4382
4411
  BITFAB_PROGRESS_PREFIX = "@@bitfab:progress ";
4383
4412
  ReplayError = class extends BitfabError {
4384
- constructor(message, items, testRunId, testRunUrl, cause) {
4385
- super(message, testRunUrl);
4413
+ constructor(message, items, experimentId, experimentUrl, cause) {
4414
+ super(message, experimentUrl);
4386
4415
  this.items = items;
4387
- this.testRunId = testRunId;
4388
- this.testRunUrl = testRunUrl;
4416
+ this.experimentId = experimentId;
4417
+ this.experimentUrl = experimentUrl;
4389
4418
  this.cause = cause;
4390
4419
  this.name = "ReplayError";
4420
+ this.testRunId = experimentId;
4421
+ this.testRunUrl = experimentUrl;
4391
4422
  }
4392
4423
  };
4393
4424
  DbBranchReplayError = class extends BitfabError {
@@ -4406,6 +4437,8 @@ var init_replay = __esm({
4406
4437
  var node_exports = {};
4407
4438
  __export(node_exports, {
4408
4439
  AssertionCategoriesClient: () => AssertionCategoriesClient,
4440
+ AssertionGenerationError: () => AssertionGenerationError,
4441
+ AssertionGenerationTimeoutError: () => AssertionGenerationTimeoutError,
4409
4442
  BITFAB_PROGRESS_PREFIX: () => BITFAB_PROGRESS_PREFIX,
4410
4443
  Bitfab: () => Bitfab,
4411
4444
  BitfabClaudeAgentHandler: () => BitfabClaudeAgentHandler,
@@ -4421,6 +4454,8 @@ __export(node_exports, {
4421
4454
  DEFAULT_SERVICE_URL: () => DEFAULT_SERVICE_URL,
4422
4455
  DatasetsClient: () => DatasetsClient,
4423
4456
  DbBranchReplayError: () => DbBranchReplayError,
4457
+ GraderRerunError: () => GraderRerunError,
4458
+ GraderRerunTimeoutError: () => GraderRerunTimeoutError,
4424
4459
  GradersClient: () => GradersClient,
4425
4460
  HttpClient: () => HttpClient,
4426
4461
  LabelsClient: () => LabelsClient,
@@ -5855,11 +5890,39 @@ init_constants();
5855
5890
 
5856
5891
  // src/datasets.ts
5857
5892
  var DEFAULT_RERUN_TIMEOUT_MS = 9e4;
5858
- var DEFAULT_RERUN_POLL_INTERVAL_MS = 1e3;
5893
+ var RERUN_POLL_INTERVAL_MS = 1e3;
5859
5894
  var TERMINAL_RERUN_STATUSES = /* @__PURE__ */ new Set([
5860
5895
  "completed",
5861
5896
  "errored"
5862
5897
  ]);
5898
+ var GraderRerunError = class extends Error {
5899
+ constructor(message, run) {
5900
+ super(message);
5901
+ this.run = run;
5902
+ this.name = "GraderRerunError";
5903
+ }
5904
+ };
5905
+ var GraderRerunTimeoutError = class extends GraderRerunError {
5906
+ constructor(datasetId, run, timeoutMs) {
5907
+ super(
5908
+ `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.`,
5909
+ run
5910
+ );
5911
+ this.name = "GraderRerunTimeoutError";
5912
+ }
5913
+ };
5914
+ function finishedRerun(datasetId, run, joinedExisting) {
5915
+ if (!TERMINAL_RERUN_STATUSES.has(run.status)) {
5916
+ return null;
5917
+ }
5918
+ if (run.status === "errored") {
5919
+ throw new GraderRerunError(
5920
+ `Grader re-run ${run.id} on dataset ${datasetId} errored: ${run.error ?? "no reason recorded"}`,
5921
+ run
5922
+ );
5923
+ }
5924
+ return { run, joinedExisting };
5925
+ }
5863
5926
  function sleep(ms) {
5864
5927
  return new Promise((resolve) => setTimeout(resolve, ms));
5865
5928
  }
@@ -5959,28 +6022,25 @@ var DatasetsClient = class {
5959
6022
  { graderIds }
5960
6023
  );
5961
6024
  }
5962
- /**
5963
- * Re-run graders over every trace in the dataset. Defaults to every assigned
5964
- * grader; an unassigned id is rejected. Waits for the run to finish (up to
5965
- * `timeoutMs`, default 90s) unless `wait` is `false`, and returns the last
5966
- * run state seen either way. A request matching an in-flight run joins it.
5967
- */
5968
6025
  async rerunGraders(datasetId, options = {}) {
5969
6026
  const started = await this.httpClient.request(
5970
6027
  datasetPath(datasetId, "/rerunGraders"),
5971
6028
  options.graderIds === void 0 ? {} : { graderIds: options.graderIds }
5972
6029
  );
5973
- if (options.wait === false) {
5974
- return started;
5975
- }
5976
- const deadline = Date.now() + (options.timeoutMs ?? DEFAULT_RERUN_TIMEOUT_MS);
5977
- const interval = options.pollIntervalMs ?? DEFAULT_RERUN_POLL_INTERVAL_MS;
6030
+ const timeoutMs = options.timeoutMs ?? DEFAULT_RERUN_TIMEOUT_MS;
6031
+ const deadline = Date.now() + timeoutMs;
5978
6032
  let run = started.run;
5979
- while (!TERMINAL_RERUN_STATUSES.has(run.status) && Date.now() < deadline) {
5980
- await sleep(interval);
6033
+ while (true) {
6034
+ const finished = finishedRerun(datasetId, run, started.joinedExisting);
6035
+ if (finished !== null) {
6036
+ return finished;
6037
+ }
6038
+ if (Date.now() >= deadline) {
6039
+ throw new GraderRerunTimeoutError(datasetId, run, timeoutMs);
6040
+ }
6041
+ await sleep(RERUN_POLL_INTERVAL_MS);
5981
6042
  run = await this.getGraderRerun(datasetId, run.id) ?? run;
5982
6043
  }
5983
- return { run, joinedExisting: started.joinedExisting };
5984
6044
  }
5985
6045
  /**
5986
6046
  * The dataset's active grader re-run, or the run named by `runId`. Returns
@@ -6051,6 +6111,7 @@ var GradersClient = class {
6051
6111
  init_http();
6052
6112
 
6053
6113
  // src/labels.ts
6114
+ init_http();
6054
6115
  var LABELS_PATH = "/api/sdk/traces/labels";
6055
6116
  var HUMAN_LABELS_PATH = "/api/sdk/traces/labels/human";
6056
6117
  function labelEvidencePath(traceId, assertionId) {
@@ -6060,27 +6121,27 @@ var LabelsClient = class {
6060
6121
  constructor(httpClient) {
6061
6122
  this.httpClient = httpClient;
6062
6123
  }
6063
- async save(update, testRunId) {
6064
- const [outcome] = await this.saveAll([update], testRunId);
6124
+ async save(update, experimentId) {
6125
+ const [outcome] = await this.saveAll([update], experimentId);
6065
6126
  return outcome;
6066
6127
  }
6067
- async saveAll(updates, testRunId) {
6128
+ async saveAll(updates, experimentId) {
6068
6129
  const response = await this.httpClient.request(
6069
6130
  LABELS_PATH,
6070
6131
  {
6071
6132
  labels: updates,
6072
- ...testRunId === void 0 ? {} : { testRunId }
6133
+ ...experimentId === void 0 ? {} : experimentIdWireFields(experimentId)
6073
6134
  }
6074
6135
  );
6075
6136
  return response.labels;
6076
6137
  }
6077
6138
  /** Withhold a verdict for a trace or replay attempt whose check did not run. */
6078
- async skip(target, testRunId) {
6079
- return this.save({ ...target, skip: true }, testRunId);
6139
+ async skip(target, experimentId) {
6140
+ return this.save({ ...target, skip: true }, experimentId);
6080
6141
  }
6081
6142
  /** Clear a previous verdict for a trace, replay attempt, or assertion. */
6082
- async archive(target, testRunId) {
6083
- return this.save({ ...target, archive: true }, testRunId);
6143
+ async archive(target, experimentId) {
6144
+ return this.save({ ...target, archive: true }, experimentId);
6084
6145
  }
6085
6146
  async saveHuman(update) {
6086
6147
  const [outcome] = await this.saveHumanAll([update]);
@@ -7836,6 +7897,46 @@ init_traceMetadata();
7836
7897
  // src/traces.ts
7837
7898
  var ASSERTIONS_PATH = "/api/sdk/traces/assertions";
7838
7899
  var SEARCH_PATH = "/api/sdk/traces/search";
7900
+ var DEFAULT_GENERATION_TIMEOUT_MS = 6e5;
7901
+ var GENERATION_POLL_INTERVAL_MS = 2e3;
7902
+ var AssertionGenerationError = class extends Error {
7903
+ constructor(message, traceId) {
7904
+ super(message);
7905
+ this.traceId = traceId;
7906
+ this.name = "AssertionGenerationError";
7907
+ }
7908
+ };
7909
+ var AssertionGenerationTimeoutError = class extends AssertionGenerationError {
7910
+ constructor(traceId, timeoutMs) {
7911
+ super(
7912
+ `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.`,
7913
+ traceId
7914
+ );
7915
+ this.name = "AssertionGenerationTimeoutError";
7916
+ }
7917
+ };
7918
+ function sleep2(ms) {
7919
+ return new Promise((resolve) => setTimeout(resolve, ms));
7920
+ }
7921
+ function isGenerationFinished(traceId, generation) {
7922
+ if (generation === null || generation.status === "running") {
7923
+ return false;
7924
+ }
7925
+ if (generation.status === "failed") {
7926
+ throw new AssertionGenerationError(
7927
+ `Assertion generation for trace ${traceId} failed: ${generation.error ?? "no reason recorded"}. Pass regenerate: true to try again.`,
7928
+ traceId
7929
+ );
7930
+ }
7931
+ return true;
7932
+ }
7933
+ function draftsInGenerationOrder(assertions, assertionIds) {
7934
+ const byId = new Map(assertions.map((assertion) => [assertion.id, assertion]));
7935
+ return assertionIds.flatMap((id) => {
7936
+ const assertion = byId.get(id);
7937
+ return assertion === void 0 ? [] : [assertion];
7938
+ });
7939
+ }
7839
7940
  function traceAssertionsPath(traceId, suffix = "") {
7840
7941
  return `/api/sdk/traces/${encodeURIComponent(traceId)}/assertions${suffix}`;
7841
7942
  }
@@ -7867,6 +7968,34 @@ var TracesClient = class {
7867
7968
  });
7868
7969
  return response.assertions;
7869
7970
  }
7971
+ async generateAssertions(traceId, options = {}) {
7972
+ const started = await this.httpClient.request(
7973
+ traceAssertionsPath(traceId, "/generate"),
7974
+ {
7975
+ ...options.guidance === void 0 ? {} : { guidance: options.guidance },
7976
+ ...options.categoryIds === void 0 ? {} : { categoryIds: options.categoryIds },
7977
+ ...options.regenerate === void 0 ? {} : { regenerate: options.regenerate }
7978
+ }
7979
+ );
7980
+ if (isGenerationFinished(traceId, started.generation)) {
7981
+ return started.assertions;
7982
+ }
7983
+ const timeoutMs = options.timeoutMs ?? DEFAULT_GENERATION_TIMEOUT_MS;
7984
+ const deadline = Date.now() + timeoutMs;
7985
+ while (true) {
7986
+ if (Date.now() >= deadline) {
7987
+ throw new AssertionGenerationTimeoutError(traceId, timeoutMs);
7988
+ }
7989
+ await sleep2(GENERATION_POLL_INTERVAL_MS);
7990
+ const read2 = await this.getAssertions(traceId);
7991
+ if (isGenerationFinished(traceId, read2.generation)) {
7992
+ return draftsInGenerationOrder(
7993
+ read2.assertions,
7994
+ read2.generation.assertionIds
7995
+ );
7996
+ }
7997
+ }
7998
+ }
7870
7999
  async archiveAssertions(params) {
7871
8000
  const response = await this.httpClient.request(
7872
8001
  traceAssertionsPath(params.traceId, "/archive"),
@@ -8370,6 +8499,15 @@ var BitfabVercelAiHandler = class {
8370
8499
 
8371
8500
  // src/client.ts
8372
8501
  init_warnOnce();
8502
+ function resolveSpanExperimentId(options) {
8503
+ const { experimentId, testRunId } = options;
8504
+ if (experimentId !== void 0 && testRunId !== void 0 && experimentId !== testRunId) {
8505
+ throw new BitfabError(
8506
+ `experimentId ("${experimentId}") and its deprecated alias testRunId ("${testRunId}") were both passed with different values. Pass only experimentId.`
8507
+ );
8508
+ }
8509
+ return experimentId ?? testRunId;
8510
+ }
8373
8511
  var TRUNCATION_LIMIT_ORDER = [
8374
8512
  "max_depth",
8375
8513
  "max_captured_subtree_spans"
@@ -8959,13 +9097,12 @@ var Bitfab = class {
8959
9097
  "bitfab.node({ capture: false }) cannot use mockOnReplay: true because an uncaptured node has no recorded output."
8960
9098
  );
8961
9099
  }
9100
+ const experimentId = resolveSpanExperimentId(options);
8962
9101
  return {
8963
9102
  capture,
8964
9103
  type: options.type ?? "custom",
8965
9104
  ...options.name !== void 0 && { name: options.name },
8966
- ...options.testRunId !== void 0 && {
8967
- testRunId: options.testRunId
8968
- },
9105
+ ...experimentId !== void 0 && { experimentId },
8969
9106
  ...options.mockOnReplay !== void 0 && {
8970
9107
  mockOnReplay: options.mockOnReplay
8971
9108
  },
@@ -9100,6 +9237,7 @@ var Bitfab = class {
9100
9237
  return invokeWithoutNode();
9101
9238
  }
9102
9239
  const mockOnReplay = nodeConfiguration?.mockOnReplay ?? options.mockOnReplayDefault;
9240
+ const nodeExperimentId = nodeConfiguration === void 0 ? void 0 : resolveSpanExperimentId(nodeConfiguration);
9103
9241
  const childOptions = {
9104
9242
  name: spanName,
9105
9243
  type: nodeConfiguration?.type ?? "function",
@@ -9110,8 +9248,8 @@ var Bitfab = class {
9110
9248
  captureContent: true,
9111
9249
  autoTraceDefinition: definition,
9112
9250
  ...declaredNode && { declaredNode },
9113
- ...nodeConfiguration?.testRunId !== void 0 && {
9114
- testRunId: nodeConfiguration.testRunId
9251
+ ...nodeExperimentId !== void 0 && {
9252
+ experimentId: nodeExperimentId
9115
9253
  },
9116
9254
  ...mockOnReplay !== void 0 && {
9117
9255
  mockOnReplay
@@ -9772,6 +9910,7 @@ var Bitfab = class {
9772
9910
  const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
9773
9911
  const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
9774
9912
  const self = this;
9913
+ const optionExperimentId = resolveSpanExperimentId(options);
9775
9914
  const fnIsAsyncFunction = fn.constructor.name === "AsyncFunction";
9776
9915
  const fnReturnsPromise = fnIsAsyncFunction || (() => {
9777
9916
  try {
@@ -9874,7 +10013,7 @@ var Bitfab = class {
9874
10013
  const inputs = args;
9875
10014
  const startedAt = nowIsoTimestamp();
9876
10015
  const replayCtxAtStart = getReplayContext();
9877
- const testRunId = replayCtxAtStart?.testRunId ?? options.testRunId;
10016
+ const experimentId = replayCtxAtStart?.experimentId ?? optionExperimentId;
9878
10017
  if (isRootSpan && !activeTraceStates.has(traceId)) {
9879
10018
  const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
9880
10019
  self.simulationPlan.refresh();
@@ -9883,7 +10022,7 @@ var Bitfab = class {
9883
10022
  traceFunctionKey,
9884
10023
  startedAt,
9885
10024
  contexts: [],
9886
- ...testRunId !== void 0 && { testRunId },
10025
+ ...experimentId !== void 0 && { experimentId },
9887
10026
  ...replayCtxAtStart?.inputSourceTraceId && {
9888
10027
  inputSourceTraceId: replayCtxAtStart.inputSourceTraceId
9889
10028
  },
@@ -9929,7 +10068,7 @@ var Bitfab = class {
9929
10068
  contexts: newContext.contexts,
9930
10069
  prompt: newContext.prompt,
9931
10070
  endedAt,
9932
- ...testRunId !== void 0 && { testRunId },
10071
+ ...experimentId !== void 0 && { experimentId },
9933
10072
  ...replayCtx?.inputSourceSpanId && {
9934
10073
  inputSourceSpanId: replayCtx.inputSourceSpanId
9935
10074
  }
@@ -9950,7 +10089,7 @@ var Bitfab = class {
9950
10089
  name: traceState?.name,
9951
10090
  metadata: callerTraceMetadata(traceId),
9952
10091
  contexts: traceState?.contexts ?? [],
9953
- testRunId: traceState?.testRunId,
10092
+ experimentId: traceState?.experimentId,
9954
10093
  inputSourceTraceId: traceState?.inputSourceTraceId,
9955
10094
  replayAttempt: traceState?.replayAttempt,
9956
10095
  dbSnapshotRef: traceState?.dbSnapshotRef,
@@ -10421,7 +10560,7 @@ var Bitfab = class {
10421
10560
  completed: true,
10422
10561
  ...params.dropped && { dropped: true },
10423
10562
  ...params.sessionId && { sessionId: params.sessionId },
10424
- ...params.testRunId && { testRunId: params.testRunId }
10563
+ ...params.experimentId !== void 0 && experimentIdWireFields(params.experimentId)
10425
10564
  });
10426
10565
  }
10427
10566
  sendsSpanOnCompletion(context) {
@@ -10533,7 +10672,7 @@ var Bitfab = class {
10533
10672
  },
10534
10673
  ...params.declaredNode === true && { [DECLARED_NODE_FIELD]: true },
10535
10674
  rawSpan: externalSpan,
10536
- ...params.testRunId && { testRunId: params.testRunId },
10675
+ ...params.experimentId !== void 0 && experimentIdWireFields(params.experimentId),
10537
10676
  ...params.mocked && { mocked: true },
10538
10677
  ...params.mockTarget && { mockTarget: params.mockTarget },
10539
10678
  ...params.mockSource && { mockSource: params.mockSource }
@@ -11089,6 +11228,8 @@ assertAsyncStorageRegistered();
11089
11228
  // Annotate the CommonJS export names for ESM import in node:
11090
11229
  0 && (module.exports = {
11091
11230
  AssertionCategoriesClient,
11231
+ AssertionGenerationError,
11232
+ AssertionGenerationTimeoutError,
11092
11233
  BITFAB_PROGRESS_PREFIX,
11093
11234
  Bitfab,
11094
11235
  BitfabClaudeAgentHandler,
@@ -11104,6 +11245,8 @@ assertAsyncStorageRegistered();
11104
11245
  DEFAULT_SERVICE_URL,
11105
11246
  DatasetsClient,
11106
11247
  DbBranchReplayError,
11248
+ GraderRerunError,
11249
+ GraderRerunTimeoutError,
11107
11250
  GradersClient,
11108
11251
  HttpClient,
11109
11252
  LabelsClient,