@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/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.58.5";
91
+ __version__ = "0.59.1";
92
92
  __packageName__ = "@bitfab/sdk";
93
93
  }
94
94
  });
@@ -2111,6 +2111,7 @@ __export(http_exports, {
2111
2111
  HttpClient: () => HttpClient,
2112
2112
  awaitOnExit: () => awaitOnExit,
2113
2113
  awaitPendingRequests: () => awaitPendingRequests,
2114
+ experimentIdWireFields: () => experimentIdWireFields,
2114
2115
  flushTraces: () => flushTraces,
2115
2116
  parseRetryAfterMs: () => parseRetryAfterMs,
2116
2117
  serializePayloadBody: () => serializePayloadBody
@@ -2279,6 +2280,9 @@ function warnDroppedExternalTrace(error) {
2279
2280
  `a trace was dropped because the send step failed: ${describeError(error)}`
2280
2281
  );
2281
2282
  }
2283
+ function experimentIdWireFields(experimentId) {
2284
+ return { experimentId, testRunId: experimentId };
2285
+ }
2282
2286
  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;
2283
2287
  var init_http = __esm({
2284
2288
  "src/http.ts"() {
@@ -2883,7 +2887,7 @@ var init_http = __esm({
2883
2887
  }
2884
2888
  /**
2885
2889
  * Start a replay session by fetching historical traces.
2886
- * Blocking call - creates a test run and returns lightweight item references.
2890
+ * Blocking call - creates an experiment and returns lightweight item references.
2887
2891
  */
2888
2892
  async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetIds, graderIds, dbBranchSettings, attempts, includeOriginalMetadata, onlyWithAssertions, judgeAssertions) {
2889
2893
  const payload = { traceFunctionKey };
@@ -2939,9 +2943,20 @@ var init_http = __esm({
2939
2943
  payload.git = git;
2940
2944
  }
2941
2945
  const timeout = includeDbBranchLease ? REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS : 3e4;
2942
- return this.request("/api/sdk/replay/start", payload, {
2943
- timeout
2944
- });
2946
+ const response = await this.request(
2947
+ "/api/sdk/replay/start",
2948
+ payload,
2949
+ { timeout }
2950
+ );
2951
+ const { testRunId, testRunUrl, ...rest } = response;
2952
+ const experimentId = response.experimentId ?? testRunId;
2953
+ const experimentUrl = response.experimentUrl ?? testRunUrl;
2954
+ if (experimentId === void 0 || experimentUrl === void 0) {
2955
+ throw new BitfabError(
2956
+ "Replay start response is missing the experiment ID or URL."
2957
+ );
2958
+ }
2959
+ return { ...rest, experimentId, experimentUrl };
2945
2960
  }
2946
2961
  /**
2947
2962
  * Fetch an external span by ID.
@@ -3038,21 +3053,21 @@ var init_http = __esm({
3038
3053
  * has a final status AND at least that many persisted spans, which is what
3039
3054
  * makes this a real barrier rather than a "the row exists" check.
3040
3055
  */
3041
- async getReplayStatus(testRunId, expectedSpanCounts) {
3056
+ async getReplayStatus(experimentId, expectedSpanCounts) {
3042
3057
  return this.request(
3043
3058
  "/api/sdk/replay/status",
3044
- { testRunId, expectedSpanCounts },
3059
+ { ...experimentIdWireFields(experimentId), expectedSpanCounts },
3045
3060
  { timeout: 3e4 }
3046
3061
  );
3047
3062
  }
3048
3063
  /**
3049
- * Mark a replay test run as completed.
3064
+ * Mark a replay experiment as completed.
3050
3065
  * Blocking call.
3051
3066
  */
3052
- async completeReplay(testRunId) {
3067
+ async completeReplay(experimentId) {
3053
3068
  return this.request(
3054
3069
  "/api/sdk/replay/complete",
3055
- { testRunId },
3070
+ experimentIdWireFields(experimentId),
3056
3071
  { timeout: REPLAY_COMPLETE_REQUEST_TIMEOUT_MS }
3057
3072
  );
3058
3073
  }
@@ -3074,11 +3089,11 @@ var init_http = __esm({
3074
3089
  * snapshot + preview branch and polls operations to readiness, which
3075
3090
  * can take seconds.
3076
3091
  */
3077
- async resolveDbBranchLease(testRunId, traceId, dbBranchSettings, attempt) {
3092
+ async resolveDbBranchLease(experimentId, traceId, dbBranchSettings, attempt) {
3078
3093
  return this.request(
3079
3094
  "/api/sdk/replay/resolveDbBranchLease",
3080
3095
  {
3081
- testRunId,
3096
+ ...experimentIdWireFields(experimentId),
3082
3097
  traceId,
3083
3098
  dbBranchSettings,
3084
3099
  ...attempt !== void 0 && attempt > 0 ? { attempt } : {}
@@ -3667,7 +3682,7 @@ function replayJsonReplacer(_key, value) {
3667
3682
  function serializeReplayResult(result) {
3668
3683
  return JSON.stringify(result, replayJsonReplacer, 2);
3669
3684
  }
3670
- async function preserveReplayFailure(operation, items, testRunId, testRunUrl) {
3685
+ async function preserveReplayFailure(operation, items, experimentId, experimentUrl) {
3671
3686
  try {
3672
3687
  return await operation();
3673
3688
  } catch (cause) {
@@ -3677,8 +3692,8 @@ async function preserveReplayFailure(operation, items, testRunId, testRunUrl) {
3677
3692
  throw new ReplayError(
3678
3693
  errorMessage(cause),
3679
3694
  items,
3680
- testRunId,
3681
- testRunUrl,
3695
+ experimentId,
3696
+ experimentUrl,
3682
3697
  cause
3683
3698
  );
3684
3699
  }
@@ -3732,7 +3747,7 @@ function buildMockTree(rootNode) {
3732
3747
  }
3733
3748
  return { spans };
3734
3749
  }
3735
- async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy, resolvedOverrides, replayedTraceId, attempt, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun) {
3750
+ async function processItem(httpClient, serverItem, fn, experimentId, mockStrategy, resolvedOverrides, replayedTraceId, attempt, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun) {
3736
3751
  let lease = includeDbBranchLease ? serverItem.dbBranchLease : void 0;
3737
3752
  let leaseError = includeDbBranchLease ? serverItem.dbBranchLeaseError : void 0;
3738
3753
  let dbSnapshotRef = serverItem.dbSnapshotRef;
@@ -3752,7 +3767,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
3752
3767
  let resolved2;
3753
3768
  try {
3754
3769
  resolved2 = await httpClient.resolveDbBranchLease(
3755
- testRunId,
3770
+ experimentId,
3756
3771
  originalTraceId,
3757
3772
  dbBranchSettings,
3758
3773
  attempt
@@ -3864,7 +3879,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
3864
3879
  replayStarted = performance.now();
3865
3880
  const maybePromise = runWithReplayContext(
3866
3881
  {
3867
- testRunId,
3882
+ experimentId,
3868
3883
  traceId: replayedTraceId,
3869
3884
  inputSourceSpanId: span.id,
3870
3885
  inputSourceTraceId: span.externalTraceId,
@@ -3947,14 +3962,14 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
3947
3962
  originalTraceOutline: null
3948
3963
  };
3949
3964
  }
3950
- async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds) {
3965
+ async function waitForReplayPersistence(httpClient, experimentId, replayedTraceIds) {
3951
3966
  const deferredSettled = await httpClient.settleDeferredWork(
3952
3967
  REPLAY_PERSISTENCE_TIMEOUT_MS
3953
3968
  );
3954
3969
  if (!deferredSettled) {
3955
3970
  httpClient.takeTraceDeliveries(replayedTraceIds);
3956
3971
  throw new BitfabError(
3957
- `Replay could not settle deferred span work before the deadline, so the expected span counts are incomplete (testRunId ${testRunId}).`
3972
+ `Replay could not settle deferred span work before the deadline, so the expected span counts are incomplete (experiment ${experimentId}).`
3958
3973
  );
3959
3974
  }
3960
3975
  if (!httpClient.hasClosedDeliveries(replayedTraceIds)) {
@@ -3983,7 +3998,7 @@ async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds)
3983
3998
  let missing = Object.keys(expectedSpanCounts).length;
3984
3999
  while (true) {
3985
4000
  const status = await httpClient.getReplayStatus(
3986
- testRunId,
4001
+ experimentId,
3987
4002
  expectedSpanCounts
3988
4003
  );
3989
4004
  const ready = status.traceIds ?? {};
@@ -4002,7 +4017,7 @@ async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds)
4002
4017
  }
4003
4018
  const cause = flushed ? "" : " Delivery was also not confirmed before the flush deadline, so the spans likely never reached the server.";
4004
4019
  throw new BitfabError(
4005
- `Replay traces were not fully persisted before the delivery deadline (testRunId ${testRunId}, missing ${missing} of ${Object.keys(expectedSpanCounts).length} trace(s)).${cause}`
4020
+ `Replay traces were not fully persisted before the delivery deadline (experiment ${experimentId}, missing ${missing} of ${Object.keys(expectedSpanCounts).length} trace(s)).${cause}`
4006
4021
  );
4007
4022
  }
4008
4023
  function sleepForReplayPersistence(ms) {
@@ -4069,8 +4084,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4069
4084
  }
4070
4085
  }
4071
4086
  const {
4072
- testRunId,
4073
- testRunUrl,
4087
+ experimentId,
4088
+ experimentUrl,
4074
4089
  items: serverItems
4075
4090
  } = await httpClient.startReplay(
4076
4091
  traceFunctionKey,
@@ -4103,7 +4118,14 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4103
4118
  }
4104
4119
  }
4105
4120
  const mockStrategy = options?.mock ?? "marked";
4106
- const fullTestRunUrl = `${serviceUrl}${testRunUrl}`;
4121
+ const fullExperimentUrl = `${serviceUrl}${experimentUrl}`;
4122
+ try {
4123
+ options?.onExperimentStart?.({
4124
+ experimentId,
4125
+ experimentUrl: fullExperimentUrl
4126
+ });
4127
+ } catch {
4128
+ }
4107
4129
  const resolvedOverrides = [
4108
4130
  ...normalizeMockOverrides(options?.mockOverride),
4109
4131
  ...registeredOverrides
@@ -4116,7 +4138,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4116
4138
  httpClient,
4117
4139
  serverItem,
4118
4140
  fn,
4119
- testRunId,
4141
+ experimentId,
4120
4142
  mockStrategy,
4121
4143
  resolvedOverrides,
4122
4144
  replayedTraceIds[index],
@@ -4171,7 +4193,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4171
4193
  }
4172
4194
  try {
4173
4195
  onItemFinish?.({
4174
- testRunId,
4196
+ experimentId,
4197
+ testRunId: experimentId,
4175
4198
  completed,
4176
4199
  total,
4177
4200
  succeeded,
@@ -4219,7 +4242,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4219
4242
  try {
4220
4243
  options.onItemStart?.({
4221
4244
  type: "started",
4222
- testRunId,
4245
+ experimentId,
4246
+ testRunId: experimentId,
4223
4247
  started,
4224
4248
  completed,
4225
4249
  total,
@@ -4238,21 +4262,23 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4238
4262
  } : void 0
4239
4263
  );
4240
4264
  if (options?.dryRun === true) {
4241
- await httpClient.completeReplay(testRunId).catch(() => void 0);
4265
+ await httpClient.completeReplay(experimentId).catch(() => void 0);
4242
4266
  const dryResult = {
4243
4267
  items: resultItems,
4244
- testRunId,
4245
- testRunUrl: fullTestRunUrl,
4268
+ experimentId,
4269
+ experimentUrl: fullExperimentUrl,
4270
+ testRunId: experimentId,
4271
+ testRunUrl: fullExperimentUrl,
4246
4272
  attempts
4247
4273
  };
4248
4274
  await writeReplayResultFile(dryResult);
4249
4275
  return dryResult;
4250
4276
  }
4251
4277
  const deliveredTraceIds = await preserveReplayFailure(
4252
- () => waitForReplayPersistence(httpClient, testRunId, replayedTraceIds),
4278
+ () => waitForReplayPersistence(httpClient, experimentId, replayedTraceIds),
4253
4279
  resultItems,
4254
- testRunId,
4255
- fullTestRunUrl
4280
+ experimentId,
4281
+ fullExperimentUrl
4256
4282
  );
4257
4283
  for (let index = 0; index < resultItems.length; index += 1) {
4258
4284
  const localId = replayedTraceIds[index];
@@ -4262,10 +4288,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4262
4288
  }
4263
4289
  }
4264
4290
  const completeResult = await preserveReplayFailure(
4265
- () => httpClient.completeReplay(testRunId),
4291
+ () => httpClient.completeReplay(experimentId),
4266
4292
  resultItems,
4267
- testRunId,
4268
- fullTestRunUrl
4293
+ experimentId,
4294
+ fullExperimentUrl
4269
4295
  );
4270
4296
  const serverTraceIds = completeResult.traceIds;
4271
4297
  const replayTokens = completeResult.tokens;
@@ -4292,20 +4318,20 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4292
4318
  if (completedCount > 0 && missing.length === completedCount) {
4293
4319
  const serverCount = completeResult.traceCount !== void 0 ? ` The server persisted ${completeResult.traceCount} trace(s) for this run.` : "";
4294
4320
  const cause = new BitfabError(
4295
- `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.`
4321
+ `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.`
4296
4322
  );
4297
4323
  throw new ReplayError(
4298
4324
  cause.message,
4299
4325
  resultItems,
4300
- testRunId,
4301
- fullTestRunUrl,
4326
+ experimentId,
4327
+ fullExperimentUrl,
4302
4328
  cause
4303
4329
  );
4304
4330
  }
4305
4331
  if (missing.length > 0) {
4306
4332
  try {
4307
4333
  console.error(
4308
- `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.`
4334
+ `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.`
4309
4335
  );
4310
4336
  } catch {
4311
4337
  }
@@ -4313,8 +4339,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4313
4339
  }
4314
4340
  const result = {
4315
4341
  items: resultItems,
4316
- testRunId,
4317
- testRunUrl: fullTestRunUrl,
4342
+ experimentId,
4343
+ experimentUrl: fullExperimentUrl,
4344
+ testRunId: experimentId,
4345
+ testRunUrl: fullExperimentUrl,
4318
4346
  attempts
4319
4347
  };
4320
4348
  await writeReplayResultFile(result);
@@ -4322,7 +4350,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4322
4350
  try {
4323
4351
  options?.onProgress?.({
4324
4352
  type: "complete",
4325
- testRunId,
4353
+ experimentId,
4354
+ testRunId: experimentId,
4326
4355
  completed: total,
4327
4356
  total,
4328
4357
  succeeded,
@@ -4372,13 +4401,15 @@ var init_replay = __esm({
4372
4401
  MAX_REPLAY_ATTEMPTS = 100;
4373
4402
  BITFAB_PROGRESS_PREFIX = "@@bitfab:progress ";
4374
4403
  ReplayError = class extends BitfabError {
4375
- constructor(message, items, testRunId, testRunUrl, cause) {
4376
- super(message, testRunUrl);
4404
+ constructor(message, items, experimentId, experimentUrl, cause) {
4405
+ super(message, experimentUrl);
4377
4406
  this.items = items;
4378
- this.testRunId = testRunId;
4379
- this.testRunUrl = testRunUrl;
4407
+ this.experimentId = experimentId;
4408
+ this.experimentUrl = experimentUrl;
4380
4409
  this.cause = cause;
4381
4410
  this.name = "ReplayError";
4411
+ this.testRunId = experimentId;
4412
+ this.testRunUrl = experimentUrl;
4382
4413
  }
4383
4414
  };
4384
4415
  DbBranchReplayError = class extends BitfabError {
@@ -4397,6 +4428,8 @@ var init_replay = __esm({
4397
4428
  var node_exports = {};
4398
4429
  __export(node_exports, {
4399
4430
  AssertionCategoriesClient: () => AssertionCategoriesClient,
4431
+ AssertionGenerationError: () => AssertionGenerationError,
4432
+ AssertionGenerationTimeoutError: () => AssertionGenerationTimeoutError,
4400
4433
  BITFAB_PROGRESS_PREFIX: () => BITFAB_PROGRESS_PREFIX,
4401
4434
  Bitfab: () => Bitfab,
4402
4435
  BitfabClaudeAgentHandler: () => BitfabClaudeAgentHandler,
@@ -4412,6 +4445,8 @@ __export(node_exports, {
4412
4445
  DEFAULT_SERVICE_URL: () => DEFAULT_SERVICE_URL,
4413
4446
  DatasetsClient: () => DatasetsClient,
4414
4447
  DbBranchReplayError: () => DbBranchReplayError,
4448
+ GraderRerunError: () => GraderRerunError,
4449
+ GraderRerunTimeoutError: () => GraderRerunTimeoutError,
4415
4450
  GradersClient: () => GradersClient,
4416
4451
  HttpClient: () => HttpClient,
4417
4452
  LabelsClient: () => LabelsClient,
@@ -5846,11 +5881,39 @@ init_constants();
5846
5881
 
5847
5882
  // src/datasets.ts
5848
5883
  var DEFAULT_RERUN_TIMEOUT_MS = 9e4;
5849
- var DEFAULT_RERUN_POLL_INTERVAL_MS = 1e3;
5884
+ var RERUN_POLL_INTERVAL_MS = 1e3;
5850
5885
  var TERMINAL_RERUN_STATUSES = /* @__PURE__ */ new Set([
5851
5886
  "completed",
5852
5887
  "errored"
5853
5888
  ]);
5889
+ var GraderRerunError = class extends Error {
5890
+ constructor(message, run) {
5891
+ super(message);
5892
+ this.run = run;
5893
+ this.name = "GraderRerunError";
5894
+ }
5895
+ };
5896
+ var GraderRerunTimeoutError = class extends GraderRerunError {
5897
+ constructor(datasetId, run, timeoutMs) {
5898
+ super(
5899
+ `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.`,
5900
+ run
5901
+ );
5902
+ this.name = "GraderRerunTimeoutError";
5903
+ }
5904
+ };
5905
+ function finishedRerun(datasetId, run, joinedExisting) {
5906
+ if (!TERMINAL_RERUN_STATUSES.has(run.status)) {
5907
+ return null;
5908
+ }
5909
+ if (run.status === "errored") {
5910
+ throw new GraderRerunError(
5911
+ `Grader re-run ${run.id} on dataset ${datasetId} errored: ${run.error ?? "no reason recorded"}`,
5912
+ run
5913
+ );
5914
+ }
5915
+ return { run, joinedExisting };
5916
+ }
5854
5917
  function sleep(ms) {
5855
5918
  return new Promise((resolve) => setTimeout(resolve, ms));
5856
5919
  }
@@ -5950,28 +6013,25 @@ var DatasetsClient = class {
5950
6013
  { graderIds }
5951
6014
  );
5952
6015
  }
5953
- /**
5954
- * Re-run graders over every trace in the dataset. Defaults to every assigned
5955
- * grader; an unassigned id is rejected. Waits for the run to finish (up to
5956
- * `timeoutMs`, default 90s) unless `wait` is `false`, and returns the last
5957
- * run state seen either way. A request matching an in-flight run joins it.
5958
- */
5959
6016
  async rerunGraders(datasetId, options = {}) {
5960
6017
  const started = await this.httpClient.request(
5961
6018
  datasetPath(datasetId, "/rerunGraders"),
5962
6019
  options.graderIds === void 0 ? {} : { graderIds: options.graderIds }
5963
6020
  );
5964
- if (options.wait === false) {
5965
- return started;
5966
- }
5967
- const deadline = Date.now() + (options.timeoutMs ?? DEFAULT_RERUN_TIMEOUT_MS);
5968
- const interval = options.pollIntervalMs ?? DEFAULT_RERUN_POLL_INTERVAL_MS;
6021
+ const timeoutMs = options.timeoutMs ?? DEFAULT_RERUN_TIMEOUT_MS;
6022
+ const deadline = Date.now() + timeoutMs;
5969
6023
  let run = started.run;
5970
- while (!TERMINAL_RERUN_STATUSES.has(run.status) && Date.now() < deadline) {
5971
- await sleep(interval);
6024
+ while (true) {
6025
+ const finished = finishedRerun(datasetId, run, started.joinedExisting);
6026
+ if (finished !== null) {
6027
+ return finished;
6028
+ }
6029
+ if (Date.now() >= deadline) {
6030
+ throw new GraderRerunTimeoutError(datasetId, run, timeoutMs);
6031
+ }
6032
+ await sleep(RERUN_POLL_INTERVAL_MS);
5972
6033
  run = await this.getGraderRerun(datasetId, run.id) ?? run;
5973
6034
  }
5974
- return { run, joinedExisting: started.joinedExisting };
5975
6035
  }
5976
6036
  /**
5977
6037
  * The dataset's active grader re-run, or the run named by `runId`. Returns
@@ -6042,6 +6102,7 @@ var GradersClient = class {
6042
6102
  init_http();
6043
6103
 
6044
6104
  // src/labels.ts
6105
+ init_http();
6045
6106
  var LABELS_PATH = "/api/sdk/traces/labels";
6046
6107
  var HUMAN_LABELS_PATH = "/api/sdk/traces/labels/human";
6047
6108
  function labelEvidencePath(traceId, assertionId) {
@@ -6051,27 +6112,27 @@ var LabelsClient = class {
6051
6112
  constructor(httpClient) {
6052
6113
  this.httpClient = httpClient;
6053
6114
  }
6054
- async save(update, testRunId) {
6055
- const [outcome] = await this.saveAll([update], testRunId);
6115
+ async save(update, experimentId) {
6116
+ const [outcome] = await this.saveAll([update], experimentId);
6056
6117
  return outcome;
6057
6118
  }
6058
- async saveAll(updates, testRunId) {
6119
+ async saveAll(updates, experimentId) {
6059
6120
  const response = await this.httpClient.request(
6060
6121
  LABELS_PATH,
6061
6122
  {
6062
6123
  labels: updates,
6063
- ...testRunId === void 0 ? {} : { testRunId }
6124
+ ...experimentId === void 0 ? {} : experimentIdWireFields(experimentId)
6064
6125
  }
6065
6126
  );
6066
6127
  return response.labels;
6067
6128
  }
6068
6129
  /** Withhold a verdict for a trace or replay attempt whose check did not run. */
6069
- async skip(target, testRunId) {
6070
- return this.save({ ...target, skip: true }, testRunId);
6130
+ async skip(target, experimentId) {
6131
+ return this.save({ ...target, skip: true }, experimentId);
6071
6132
  }
6072
6133
  /** Clear a previous verdict for a trace, replay attempt, or assertion. */
6073
- async archive(target, testRunId) {
6074
- return this.save({ ...target, archive: true }, testRunId);
6134
+ async archive(target, experimentId) {
6135
+ return this.save({ ...target, archive: true }, experimentId);
6075
6136
  }
6076
6137
  async saveHuman(update) {
6077
6138
  const [outcome] = await this.saveHumanAll([update]);
@@ -7827,6 +7888,46 @@ init_traceMetadata();
7827
7888
  // src/traces.ts
7828
7889
  var ASSERTIONS_PATH = "/api/sdk/traces/assertions";
7829
7890
  var SEARCH_PATH = "/api/sdk/traces/search";
7891
+ var DEFAULT_GENERATION_TIMEOUT_MS = 6e5;
7892
+ var GENERATION_POLL_INTERVAL_MS = 2e3;
7893
+ var AssertionGenerationError = class extends Error {
7894
+ constructor(message, traceId) {
7895
+ super(message);
7896
+ this.traceId = traceId;
7897
+ this.name = "AssertionGenerationError";
7898
+ }
7899
+ };
7900
+ var AssertionGenerationTimeoutError = class extends AssertionGenerationError {
7901
+ constructor(traceId, timeoutMs) {
7902
+ super(
7903
+ `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.`,
7904
+ traceId
7905
+ );
7906
+ this.name = "AssertionGenerationTimeoutError";
7907
+ }
7908
+ };
7909
+ function sleep2(ms) {
7910
+ return new Promise((resolve) => setTimeout(resolve, ms));
7911
+ }
7912
+ function isGenerationFinished(traceId, generation) {
7913
+ if (generation === null || generation.status === "running") {
7914
+ return false;
7915
+ }
7916
+ if (generation.status === "failed") {
7917
+ throw new AssertionGenerationError(
7918
+ `Assertion generation for trace ${traceId} failed: ${generation.error ?? "no reason recorded"}. Pass regenerate: true to try again.`,
7919
+ traceId
7920
+ );
7921
+ }
7922
+ return true;
7923
+ }
7924
+ function draftsInGenerationOrder(assertions, assertionIds) {
7925
+ const byId = new Map(assertions.map((assertion) => [assertion.id, assertion]));
7926
+ return assertionIds.flatMap((id) => {
7927
+ const assertion = byId.get(id);
7928
+ return assertion === void 0 ? [] : [assertion];
7929
+ });
7930
+ }
7830
7931
  function traceAssertionsPath(traceId, suffix = "") {
7831
7932
  return `/api/sdk/traces/${encodeURIComponent(traceId)}/assertions${suffix}`;
7832
7933
  }
@@ -7858,6 +7959,34 @@ var TracesClient = class {
7858
7959
  });
7859
7960
  return response.assertions;
7860
7961
  }
7962
+ async generateAssertions(traceId, options = {}) {
7963
+ const started = await this.httpClient.request(
7964
+ traceAssertionsPath(traceId, "/generate"),
7965
+ {
7966
+ ...options.guidance === void 0 ? {} : { guidance: options.guidance },
7967
+ ...options.categoryIds === void 0 ? {} : { categoryIds: options.categoryIds },
7968
+ ...options.regenerate === void 0 ? {} : { regenerate: options.regenerate }
7969
+ }
7970
+ );
7971
+ if (isGenerationFinished(traceId, started.generation)) {
7972
+ return started.assertions;
7973
+ }
7974
+ const timeoutMs = options.timeoutMs ?? DEFAULT_GENERATION_TIMEOUT_MS;
7975
+ const deadline = Date.now() + timeoutMs;
7976
+ while (true) {
7977
+ if (Date.now() >= deadline) {
7978
+ throw new AssertionGenerationTimeoutError(traceId, timeoutMs);
7979
+ }
7980
+ await sleep2(GENERATION_POLL_INTERVAL_MS);
7981
+ const read2 = await this.getAssertions(traceId);
7982
+ if (isGenerationFinished(traceId, read2.generation)) {
7983
+ return draftsInGenerationOrder(
7984
+ read2.assertions,
7985
+ read2.generation.assertionIds
7986
+ );
7987
+ }
7988
+ }
7989
+ }
7861
7990
  async archiveAssertions(params) {
7862
7991
  const response = await this.httpClient.request(
7863
7992
  traceAssertionsPath(params.traceId, "/archive"),
@@ -8361,6 +8490,15 @@ var BitfabVercelAiHandler = class {
8361
8490
 
8362
8491
  // src/client.ts
8363
8492
  init_warnOnce();
8493
+ function resolveSpanExperimentId(options) {
8494
+ const { experimentId, testRunId } = options;
8495
+ if (experimentId !== void 0 && testRunId !== void 0 && experimentId !== testRunId) {
8496
+ throw new BitfabError(
8497
+ `experimentId ("${experimentId}") and its deprecated alias testRunId ("${testRunId}") were both passed with different values. Pass only experimentId.`
8498
+ );
8499
+ }
8500
+ return experimentId ?? testRunId;
8501
+ }
8364
8502
  var TRUNCATION_LIMIT_ORDER = [
8365
8503
  "max_depth",
8366
8504
  "max_captured_subtree_spans"
@@ -8950,13 +9088,12 @@ var Bitfab = class {
8950
9088
  "bitfab.node({ capture: false }) cannot use mockOnReplay: true because an uncaptured node has no recorded output."
8951
9089
  );
8952
9090
  }
9091
+ const experimentId = resolveSpanExperimentId(options);
8953
9092
  return {
8954
9093
  capture,
8955
9094
  type: options.type ?? "custom",
8956
9095
  ...options.name !== void 0 && { name: options.name },
8957
- ...options.testRunId !== void 0 && {
8958
- testRunId: options.testRunId
8959
- },
9096
+ ...experimentId !== void 0 && { experimentId },
8960
9097
  ...options.mockOnReplay !== void 0 && {
8961
9098
  mockOnReplay: options.mockOnReplay
8962
9099
  },
@@ -9091,6 +9228,7 @@ var Bitfab = class {
9091
9228
  return invokeWithoutNode();
9092
9229
  }
9093
9230
  const mockOnReplay = nodeConfiguration?.mockOnReplay ?? options.mockOnReplayDefault;
9231
+ const nodeExperimentId = nodeConfiguration === void 0 ? void 0 : resolveSpanExperimentId(nodeConfiguration);
9094
9232
  const childOptions = {
9095
9233
  name: spanName,
9096
9234
  type: nodeConfiguration?.type ?? "function",
@@ -9101,8 +9239,8 @@ var Bitfab = class {
9101
9239
  captureContent: true,
9102
9240
  autoTraceDefinition: definition,
9103
9241
  ...declaredNode && { declaredNode },
9104
- ...nodeConfiguration?.testRunId !== void 0 && {
9105
- testRunId: nodeConfiguration.testRunId
9242
+ ...nodeExperimentId !== void 0 && {
9243
+ experimentId: nodeExperimentId
9106
9244
  },
9107
9245
  ...mockOnReplay !== void 0 && {
9108
9246
  mockOnReplay
@@ -9763,6 +9901,7 @@ var Bitfab = class {
9763
9901
  const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
9764
9902
  const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
9765
9903
  const self = this;
9904
+ const optionExperimentId = resolveSpanExperimentId(options);
9766
9905
  const fnIsAsyncFunction = fn.constructor.name === "AsyncFunction";
9767
9906
  const fnReturnsPromise = fnIsAsyncFunction || (() => {
9768
9907
  try {
@@ -9865,7 +10004,7 @@ var Bitfab = class {
9865
10004
  const inputs = args;
9866
10005
  const startedAt = nowIsoTimestamp();
9867
10006
  const replayCtxAtStart = getReplayContext();
9868
- const testRunId = replayCtxAtStart?.testRunId ?? options.testRunId;
10007
+ const experimentId = replayCtxAtStart?.experimentId ?? optionExperimentId;
9869
10008
  if (isRootSpan && !activeTraceStates.has(traceId)) {
9870
10009
  const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
9871
10010
  self.simulationPlan.refresh();
@@ -9874,7 +10013,7 @@ var Bitfab = class {
9874
10013
  traceFunctionKey,
9875
10014
  startedAt,
9876
10015
  contexts: [],
9877
- ...testRunId !== void 0 && { testRunId },
10016
+ ...experimentId !== void 0 && { experimentId },
9878
10017
  ...replayCtxAtStart?.inputSourceTraceId && {
9879
10018
  inputSourceTraceId: replayCtxAtStart.inputSourceTraceId
9880
10019
  },
@@ -9920,7 +10059,7 @@ var Bitfab = class {
9920
10059
  contexts: newContext.contexts,
9921
10060
  prompt: newContext.prompt,
9922
10061
  endedAt,
9923
- ...testRunId !== void 0 && { testRunId },
10062
+ ...experimentId !== void 0 && { experimentId },
9924
10063
  ...replayCtx?.inputSourceSpanId && {
9925
10064
  inputSourceSpanId: replayCtx.inputSourceSpanId
9926
10065
  }
@@ -9941,7 +10080,7 @@ var Bitfab = class {
9941
10080
  name: traceState?.name,
9942
10081
  metadata: callerTraceMetadata(traceId),
9943
10082
  contexts: traceState?.contexts ?? [],
9944
- testRunId: traceState?.testRunId,
10083
+ experimentId: traceState?.experimentId,
9945
10084
  inputSourceTraceId: traceState?.inputSourceTraceId,
9946
10085
  replayAttempt: traceState?.replayAttempt,
9947
10086
  dbSnapshotRef: traceState?.dbSnapshotRef,
@@ -10412,7 +10551,7 @@ var Bitfab = class {
10412
10551
  completed: true,
10413
10552
  ...params.dropped && { dropped: true },
10414
10553
  ...params.sessionId && { sessionId: params.sessionId },
10415
- ...params.testRunId && { testRunId: params.testRunId }
10554
+ ...params.experimentId !== void 0 && experimentIdWireFields(params.experimentId)
10416
10555
  });
10417
10556
  }
10418
10557
  sendsSpanOnCompletion(context) {
@@ -10524,7 +10663,7 @@ var Bitfab = class {
10524
10663
  },
10525
10664
  ...params.declaredNode === true && { [DECLARED_NODE_FIELD]: true },
10526
10665
  rawSpan: externalSpan,
10527
- ...params.testRunId && { testRunId: params.testRunId },
10666
+ ...params.experimentId !== void 0 && experimentIdWireFields(params.experimentId),
10528
10667
  ...params.mocked && { mocked: true },
10529
10668
  ...params.mockTarget && { mockTarget: params.mockTarget },
10530
10669
  ...params.mockSource && { mockSource: params.mockSource }
@@ -11080,6 +11219,8 @@ assertAsyncStorageRegistered();
11080
11219
  // Annotate the CommonJS export names for ESM import in node:
11081
11220
  0 && (module.exports = {
11082
11221
  AssertionCategoriesClient,
11222
+ AssertionGenerationError,
11223
+ AssertionGenerationTimeoutError,
11083
11224
  BITFAB_PROGRESS_PREFIX,
11084
11225
  Bitfab,
11085
11226
  BitfabClaudeAgentHandler,
@@ -11095,6 +11236,8 @@ assertAsyncStorageRegistered();
11095
11236
  DEFAULT_SERVICE_URL,
11096
11237
  DatasetsClient,
11097
11238
  DbBranchReplayError,
11239
+ GraderRerunError,
11240
+ GraderRerunTimeoutError,
11098
11241
  GradersClient,
11099
11242
  HttpClient,
11100
11243
  LabelsClient,