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/index.cjs CHANGED
@@ -51,7 +51,7 @@ var __version__, __packageName__;
51
51
  var init_version_generated = __esm({
52
52
  "src/version.generated.ts"() {
53
53
  "use strict";
54
- __version__ = "0.58.5";
54
+ __version__ = "0.59.1";
55
55
  __packageName__ = "bitfab";
56
56
  }
57
57
  });
@@ -2113,6 +2113,7 @@ __export(http_exports, {
2113
2113
  HttpClient: () => HttpClient,
2114
2114
  awaitOnExit: () => awaitOnExit,
2115
2115
  awaitPendingRequests: () => awaitPendingRequests,
2116
+ experimentIdWireFields: () => experimentIdWireFields,
2116
2117
  flushTraces: () => flushTraces,
2117
2118
  parseRetryAfterMs: () => parseRetryAfterMs,
2118
2119
  serializePayloadBody: () => serializePayloadBody
@@ -2281,6 +2282,9 @@ function warnDroppedExternalTrace(error) {
2281
2282
  `a trace was dropped because the send step failed: ${describeError(error)}`
2282
2283
  );
2283
2284
  }
2285
+ function experimentIdWireFields(experimentId) {
2286
+ return { experimentId, testRunId: experimentId };
2287
+ }
2284
2288
  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;
2285
2289
  var init_http = __esm({
2286
2290
  "src/http.ts"() {
@@ -2885,7 +2889,7 @@ var init_http = __esm({
2885
2889
  }
2886
2890
  /**
2887
2891
  * Start a replay session by fetching historical traces.
2888
- * Blocking call - creates a test run and returns lightweight item references.
2892
+ * Blocking call - creates an experiment and returns lightweight item references.
2889
2893
  */
2890
2894
  async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetIds, graderIds, dbBranchSettings, attempts, includeOriginalMetadata, onlyWithAssertions, judgeAssertions) {
2891
2895
  const payload = { traceFunctionKey };
@@ -2941,9 +2945,20 @@ var init_http = __esm({
2941
2945
  payload.git = git;
2942
2946
  }
2943
2947
  const timeout = includeDbBranchLease ? REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS : 3e4;
2944
- return this.request("/api/sdk/replay/start", payload, {
2945
- timeout
2946
- });
2948
+ const response = await this.request(
2949
+ "/api/sdk/replay/start",
2950
+ payload,
2951
+ { timeout }
2952
+ );
2953
+ const { testRunId, testRunUrl, ...rest } = response;
2954
+ const experimentId = response.experimentId ?? testRunId;
2955
+ const experimentUrl = response.experimentUrl ?? testRunUrl;
2956
+ if (experimentId === void 0 || experimentUrl === void 0) {
2957
+ throw new BitfabError(
2958
+ "Replay start response is missing the experiment ID or URL."
2959
+ );
2960
+ }
2961
+ return { ...rest, experimentId, experimentUrl };
2947
2962
  }
2948
2963
  /**
2949
2964
  * Fetch an external span by ID.
@@ -3040,21 +3055,21 @@ var init_http = __esm({
3040
3055
  * has a final status AND at least that many persisted spans, which is what
3041
3056
  * makes this a real barrier rather than a "the row exists" check.
3042
3057
  */
3043
- async getReplayStatus(testRunId, expectedSpanCounts) {
3058
+ async getReplayStatus(experimentId, expectedSpanCounts) {
3044
3059
  return this.request(
3045
3060
  "/api/sdk/replay/status",
3046
- { testRunId, expectedSpanCounts },
3061
+ { ...experimentIdWireFields(experimentId), expectedSpanCounts },
3047
3062
  { timeout: 3e4 }
3048
3063
  );
3049
3064
  }
3050
3065
  /**
3051
- * Mark a replay test run as completed.
3066
+ * Mark a replay experiment as completed.
3052
3067
  * Blocking call.
3053
3068
  */
3054
- async completeReplay(testRunId) {
3069
+ async completeReplay(experimentId) {
3055
3070
  return this.request(
3056
3071
  "/api/sdk/replay/complete",
3057
- { testRunId },
3072
+ experimentIdWireFields(experimentId),
3058
3073
  { timeout: REPLAY_COMPLETE_REQUEST_TIMEOUT_MS }
3059
3074
  );
3060
3075
  }
@@ -3076,11 +3091,11 @@ var init_http = __esm({
3076
3091
  * snapshot + preview branch and polls operations to readiness, which
3077
3092
  * can take seconds.
3078
3093
  */
3079
- async resolveDbBranchLease(testRunId, traceId, dbBranchSettings, attempt) {
3094
+ async resolveDbBranchLease(experimentId, traceId, dbBranchSettings, attempt) {
3080
3095
  return this.request(
3081
3096
  "/api/sdk/replay/resolveDbBranchLease",
3082
3097
  {
3083
- testRunId,
3098
+ ...experimentIdWireFields(experimentId),
3084
3099
  traceId,
3085
3100
  dbBranchSettings,
3086
3101
  ...attempt !== void 0 && attempt > 0 ? { attempt } : {}
@@ -3669,7 +3684,7 @@ function replayJsonReplacer(_key, value) {
3669
3684
  function serializeReplayResult(result) {
3670
3685
  return JSON.stringify(result, replayJsonReplacer, 2);
3671
3686
  }
3672
- async function preserveReplayFailure(operation, items, testRunId, testRunUrl) {
3687
+ async function preserveReplayFailure(operation, items, experimentId, experimentUrl) {
3673
3688
  try {
3674
3689
  return await operation();
3675
3690
  } catch (cause) {
@@ -3679,8 +3694,8 @@ async function preserveReplayFailure(operation, items, testRunId, testRunUrl) {
3679
3694
  throw new ReplayError(
3680
3695
  errorMessage(cause),
3681
3696
  items,
3682
- testRunId,
3683
- testRunUrl,
3697
+ experimentId,
3698
+ experimentUrl,
3684
3699
  cause
3685
3700
  );
3686
3701
  }
@@ -3734,7 +3749,7 @@ function buildMockTree(rootNode) {
3734
3749
  }
3735
3750
  return { spans };
3736
3751
  }
3737
- async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy, resolvedOverrides, replayedTraceId, attempt, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun) {
3752
+ async function processItem(httpClient, serverItem, fn, experimentId, mockStrategy, resolvedOverrides, replayedTraceId, attempt, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun) {
3738
3753
  let lease = includeDbBranchLease ? serverItem.dbBranchLease : void 0;
3739
3754
  let leaseError = includeDbBranchLease ? serverItem.dbBranchLeaseError : void 0;
3740
3755
  let dbSnapshotRef = serverItem.dbSnapshotRef;
@@ -3754,7 +3769,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
3754
3769
  let resolved2;
3755
3770
  try {
3756
3771
  resolved2 = await httpClient.resolveDbBranchLease(
3757
- testRunId,
3772
+ experimentId,
3758
3773
  originalTraceId,
3759
3774
  dbBranchSettings,
3760
3775
  attempt
@@ -3866,7 +3881,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
3866
3881
  replayStarted = performance.now();
3867
3882
  const maybePromise = runWithReplayContext(
3868
3883
  {
3869
- testRunId,
3884
+ experimentId,
3870
3885
  traceId: replayedTraceId,
3871
3886
  inputSourceSpanId: span.id,
3872
3887
  inputSourceTraceId: span.externalTraceId,
@@ -3949,14 +3964,14 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
3949
3964
  originalTraceOutline: null
3950
3965
  };
3951
3966
  }
3952
- async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds) {
3967
+ async function waitForReplayPersistence(httpClient, experimentId, replayedTraceIds) {
3953
3968
  const deferredSettled = await httpClient.settleDeferredWork(
3954
3969
  REPLAY_PERSISTENCE_TIMEOUT_MS
3955
3970
  );
3956
3971
  if (!deferredSettled) {
3957
3972
  httpClient.takeTraceDeliveries(replayedTraceIds);
3958
3973
  throw new BitfabError(
3959
- `Replay could not settle deferred span work before the deadline, so the expected span counts are incomplete (testRunId ${testRunId}).`
3974
+ `Replay could not settle deferred span work before the deadline, so the expected span counts are incomplete (experiment ${experimentId}).`
3960
3975
  );
3961
3976
  }
3962
3977
  if (!httpClient.hasClosedDeliveries(replayedTraceIds)) {
@@ -3985,7 +4000,7 @@ async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds)
3985
4000
  let missing = Object.keys(expectedSpanCounts).length;
3986
4001
  while (true) {
3987
4002
  const status = await httpClient.getReplayStatus(
3988
- testRunId,
4003
+ experimentId,
3989
4004
  expectedSpanCounts
3990
4005
  );
3991
4006
  const ready = status.traceIds ?? {};
@@ -4004,7 +4019,7 @@ async function waitForReplayPersistence(httpClient, testRunId, replayedTraceIds)
4004
4019
  }
4005
4020
  const cause = flushed ? "" : " Delivery was also not confirmed before the flush deadline, so the spans likely never reached the server.";
4006
4021
  throw new BitfabError(
4007
- `Replay traces were not fully persisted before the delivery deadline (testRunId ${testRunId}, missing ${missing} of ${Object.keys(expectedSpanCounts).length} trace(s)).${cause}`
4022
+ `Replay traces were not fully persisted before the delivery deadline (experiment ${experimentId}, missing ${missing} of ${Object.keys(expectedSpanCounts).length} trace(s)).${cause}`
4008
4023
  );
4009
4024
  }
4010
4025
  function sleepForReplayPersistence(ms) {
@@ -4071,8 +4086,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4071
4086
  }
4072
4087
  }
4073
4088
  const {
4074
- testRunId,
4075
- testRunUrl,
4089
+ experimentId,
4090
+ experimentUrl,
4076
4091
  items: serverItems
4077
4092
  } = await httpClient.startReplay(
4078
4093
  traceFunctionKey,
@@ -4105,7 +4120,14 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4105
4120
  }
4106
4121
  }
4107
4122
  const mockStrategy = options?.mock ?? "marked";
4108
- const fullTestRunUrl = `${serviceUrl}${testRunUrl}`;
4123
+ const fullExperimentUrl = `${serviceUrl}${experimentUrl}`;
4124
+ try {
4125
+ options?.onExperimentStart?.({
4126
+ experimentId,
4127
+ experimentUrl: fullExperimentUrl
4128
+ });
4129
+ } catch {
4130
+ }
4109
4131
  const resolvedOverrides = [
4110
4132
  ...normalizeMockOverrides(options?.mockOverride),
4111
4133
  ...registeredOverrides
@@ -4118,7 +4140,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4118
4140
  httpClient,
4119
4141
  serverItem,
4120
4142
  fn,
4121
- testRunId,
4143
+ experimentId,
4122
4144
  mockStrategy,
4123
4145
  resolvedOverrides,
4124
4146
  replayedTraceIds[index],
@@ -4173,7 +4195,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4173
4195
  }
4174
4196
  try {
4175
4197
  onItemFinish?.({
4176
- testRunId,
4198
+ experimentId,
4199
+ testRunId: experimentId,
4177
4200
  completed,
4178
4201
  total,
4179
4202
  succeeded,
@@ -4221,7 +4244,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4221
4244
  try {
4222
4245
  options.onItemStart?.({
4223
4246
  type: "started",
4224
- testRunId,
4247
+ experimentId,
4248
+ testRunId: experimentId,
4225
4249
  started,
4226
4250
  completed,
4227
4251
  total,
@@ -4240,21 +4264,23 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4240
4264
  } : void 0
4241
4265
  );
4242
4266
  if (options?.dryRun === true) {
4243
- await httpClient.completeReplay(testRunId).catch(() => void 0);
4267
+ await httpClient.completeReplay(experimentId).catch(() => void 0);
4244
4268
  const dryResult = {
4245
4269
  items: resultItems,
4246
- testRunId,
4247
- testRunUrl: fullTestRunUrl,
4270
+ experimentId,
4271
+ experimentUrl: fullExperimentUrl,
4272
+ testRunId: experimentId,
4273
+ testRunUrl: fullExperimentUrl,
4248
4274
  attempts
4249
4275
  };
4250
4276
  await writeReplayResultFile(dryResult);
4251
4277
  return dryResult;
4252
4278
  }
4253
4279
  const deliveredTraceIds = await preserveReplayFailure(
4254
- () => waitForReplayPersistence(httpClient, testRunId, replayedTraceIds),
4280
+ () => waitForReplayPersistence(httpClient, experimentId, replayedTraceIds),
4255
4281
  resultItems,
4256
- testRunId,
4257
- fullTestRunUrl
4282
+ experimentId,
4283
+ fullExperimentUrl
4258
4284
  );
4259
4285
  for (let index = 0; index < resultItems.length; index += 1) {
4260
4286
  const localId = replayedTraceIds[index];
@@ -4264,10 +4290,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4264
4290
  }
4265
4291
  }
4266
4292
  const completeResult = await preserveReplayFailure(
4267
- () => httpClient.completeReplay(testRunId),
4293
+ () => httpClient.completeReplay(experimentId),
4268
4294
  resultItems,
4269
- testRunId,
4270
- fullTestRunUrl
4295
+ experimentId,
4296
+ fullExperimentUrl
4271
4297
  );
4272
4298
  const serverTraceIds = completeResult.traceIds;
4273
4299
  const replayTokens = completeResult.tokens;
@@ -4294,20 +4320,20 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4294
4320
  if (completedCount > 0 && missing.length === completedCount) {
4295
4321
  const serverCount = completeResult.traceCount !== void 0 ? ` The server persisted ${completeResult.traceCount} trace(s) for this run.` : "";
4296
4322
  const cause = new BitfabError(
4297
- `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.`
4323
+ `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.`
4298
4324
  );
4299
4325
  throw new ReplayError(
4300
4326
  cause.message,
4301
4327
  resultItems,
4302
- testRunId,
4303
- fullTestRunUrl,
4328
+ experimentId,
4329
+ fullExperimentUrl,
4304
4330
  cause
4305
4331
  );
4306
4332
  }
4307
4333
  if (missing.length > 0) {
4308
4334
  try {
4309
4335
  console.error(
4310
- `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.`
4336
+ `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.`
4311
4337
  );
4312
4338
  } catch {
4313
4339
  }
@@ -4315,8 +4341,10 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4315
4341
  }
4316
4342
  const result = {
4317
4343
  items: resultItems,
4318
- testRunId,
4319
- testRunUrl: fullTestRunUrl,
4344
+ experimentId,
4345
+ experimentUrl: fullExperimentUrl,
4346
+ testRunId: experimentId,
4347
+ testRunUrl: fullExperimentUrl,
4320
4348
  attempts
4321
4349
  };
4322
4350
  await writeReplayResultFile(result);
@@ -4324,7 +4352,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
4324
4352
  try {
4325
4353
  options?.onProgress?.({
4326
4354
  type: "complete",
4327
- testRunId,
4355
+ experimentId,
4356
+ testRunId: experimentId,
4328
4357
  completed: total,
4329
4358
  total,
4330
4359
  succeeded,
@@ -4374,13 +4403,15 @@ var init_replay = __esm({
4374
4403
  MAX_REPLAY_ATTEMPTS = 100;
4375
4404
  BITFAB_PROGRESS_PREFIX = "@@bitfab:progress ";
4376
4405
  ReplayError = class extends BitfabError {
4377
- constructor(message, items, testRunId, testRunUrl, cause) {
4378
- super(message, testRunUrl);
4406
+ constructor(message, items, experimentId, experimentUrl, cause) {
4407
+ super(message, experimentUrl);
4379
4408
  this.items = items;
4380
- this.testRunId = testRunId;
4381
- this.testRunUrl = testRunUrl;
4409
+ this.experimentId = experimentId;
4410
+ this.experimentUrl = experimentUrl;
4382
4411
  this.cause = cause;
4383
4412
  this.name = "ReplayError";
4413
+ this.testRunId = experimentId;
4414
+ this.testRunUrl = experimentUrl;
4384
4415
  }
4385
4416
  };
4386
4417
  DbBranchReplayError = class extends BitfabError {
@@ -4399,6 +4430,8 @@ var init_replay = __esm({
4399
4430
  var index_exports = {};
4400
4431
  __export(index_exports, {
4401
4432
  AssertionCategoriesClient: () => AssertionCategoriesClient,
4433
+ AssertionGenerationError: () => AssertionGenerationError,
4434
+ AssertionGenerationTimeoutError: () => AssertionGenerationTimeoutError,
4402
4435
  BITFAB_PROGRESS_PREFIX: () => BITFAB_PROGRESS_PREFIX,
4403
4436
  Bitfab: () => Bitfab,
4404
4437
  BitfabClaudeAgentHandler: () => BitfabClaudeAgentHandler,
@@ -4414,6 +4447,8 @@ __export(index_exports, {
4414
4447
  DEFAULT_SERVICE_URL: () => DEFAULT_SERVICE_URL,
4415
4448
  DatasetsClient: () => DatasetsClient,
4416
4449
  DbBranchReplayError: () => DbBranchReplayError,
4450
+ GraderRerunError: () => GraderRerunError,
4451
+ GraderRerunTimeoutError: () => GraderRerunTimeoutError,
4417
4452
  GradersClient: () => GradersClient,
4418
4453
  HttpClient: () => HttpClient,
4419
4454
  LabelsClient: () => LabelsClient,
@@ -5841,11 +5876,39 @@ init_constants();
5841
5876
 
5842
5877
  // src/datasets.ts
5843
5878
  var DEFAULT_RERUN_TIMEOUT_MS = 9e4;
5844
- var DEFAULT_RERUN_POLL_INTERVAL_MS = 1e3;
5879
+ var RERUN_POLL_INTERVAL_MS = 1e3;
5845
5880
  var TERMINAL_RERUN_STATUSES = /* @__PURE__ */ new Set([
5846
5881
  "completed",
5847
5882
  "errored"
5848
5883
  ]);
5884
+ var GraderRerunError = class extends Error {
5885
+ constructor(message, run) {
5886
+ super(message);
5887
+ this.run = run;
5888
+ this.name = "GraderRerunError";
5889
+ }
5890
+ };
5891
+ var GraderRerunTimeoutError = class extends GraderRerunError {
5892
+ constructor(datasetId, run, timeoutMs) {
5893
+ super(
5894
+ `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.`,
5895
+ run
5896
+ );
5897
+ this.name = "GraderRerunTimeoutError";
5898
+ }
5899
+ };
5900
+ function finishedRerun(datasetId, run, joinedExisting) {
5901
+ if (!TERMINAL_RERUN_STATUSES.has(run.status)) {
5902
+ return null;
5903
+ }
5904
+ if (run.status === "errored") {
5905
+ throw new GraderRerunError(
5906
+ `Grader re-run ${run.id} on dataset ${datasetId} errored: ${run.error ?? "no reason recorded"}`,
5907
+ run
5908
+ );
5909
+ }
5910
+ return { run, joinedExisting };
5911
+ }
5849
5912
  function sleep(ms) {
5850
5913
  return new Promise((resolve) => setTimeout(resolve, ms));
5851
5914
  }
@@ -5945,28 +6008,25 @@ var DatasetsClient = class {
5945
6008
  { graderIds }
5946
6009
  );
5947
6010
  }
5948
- /**
5949
- * Re-run graders over every trace in the dataset. Defaults to every assigned
5950
- * grader; an unassigned id is rejected. Waits for the run to finish (up to
5951
- * `timeoutMs`, default 90s) unless `wait` is `false`, and returns the last
5952
- * run state seen either way. A request matching an in-flight run joins it.
5953
- */
5954
6011
  async rerunGraders(datasetId, options = {}) {
5955
6012
  const started = await this.httpClient.request(
5956
6013
  datasetPath(datasetId, "/rerunGraders"),
5957
6014
  options.graderIds === void 0 ? {} : { graderIds: options.graderIds }
5958
6015
  );
5959
- if (options.wait === false) {
5960
- return started;
5961
- }
5962
- const deadline = Date.now() + (options.timeoutMs ?? DEFAULT_RERUN_TIMEOUT_MS);
5963
- const interval = options.pollIntervalMs ?? DEFAULT_RERUN_POLL_INTERVAL_MS;
6016
+ const timeoutMs = options.timeoutMs ?? DEFAULT_RERUN_TIMEOUT_MS;
6017
+ const deadline = Date.now() + timeoutMs;
5964
6018
  let run = started.run;
5965
- while (!TERMINAL_RERUN_STATUSES.has(run.status) && Date.now() < deadline) {
5966
- await sleep(interval);
6019
+ while (true) {
6020
+ const finished = finishedRerun(datasetId, run, started.joinedExisting);
6021
+ if (finished !== null) {
6022
+ return finished;
6023
+ }
6024
+ if (Date.now() >= deadline) {
6025
+ throw new GraderRerunTimeoutError(datasetId, run, timeoutMs);
6026
+ }
6027
+ await sleep(RERUN_POLL_INTERVAL_MS);
5967
6028
  run = await this.getGraderRerun(datasetId, run.id) ?? run;
5968
6029
  }
5969
- return { run, joinedExisting: started.joinedExisting };
5970
6030
  }
5971
6031
  /**
5972
6032
  * The dataset's active grader re-run, or the run named by `runId`. Returns
@@ -6037,6 +6097,7 @@ var GradersClient = class {
6037
6097
  init_http();
6038
6098
 
6039
6099
  // src/labels.ts
6100
+ init_http();
6040
6101
  var LABELS_PATH = "/api/sdk/traces/labels";
6041
6102
  var HUMAN_LABELS_PATH = "/api/sdk/traces/labels/human";
6042
6103
  function labelEvidencePath(traceId, assertionId) {
@@ -6046,27 +6107,27 @@ var LabelsClient = class {
6046
6107
  constructor(httpClient) {
6047
6108
  this.httpClient = httpClient;
6048
6109
  }
6049
- async save(update, testRunId) {
6050
- const [outcome] = await this.saveAll([update], testRunId);
6110
+ async save(update, experimentId) {
6111
+ const [outcome] = await this.saveAll([update], experimentId);
6051
6112
  return outcome;
6052
6113
  }
6053
- async saveAll(updates, testRunId) {
6114
+ async saveAll(updates, experimentId) {
6054
6115
  const response = await this.httpClient.request(
6055
6116
  LABELS_PATH,
6056
6117
  {
6057
6118
  labels: updates,
6058
- ...testRunId === void 0 ? {} : { testRunId }
6119
+ ...experimentId === void 0 ? {} : experimentIdWireFields(experimentId)
6059
6120
  }
6060
6121
  );
6061
6122
  return response.labels;
6062
6123
  }
6063
6124
  /** Withhold a verdict for a trace or replay attempt whose check did not run. */
6064
- async skip(target, testRunId) {
6065
- return this.save({ ...target, skip: true }, testRunId);
6125
+ async skip(target, experimentId) {
6126
+ return this.save({ ...target, skip: true }, experimentId);
6066
6127
  }
6067
6128
  /** Clear a previous verdict for a trace, replay attempt, or assertion. */
6068
- async archive(target, testRunId) {
6069
- return this.save({ ...target, archive: true }, testRunId);
6129
+ async archive(target, experimentId) {
6130
+ return this.save({ ...target, archive: true }, experimentId);
6070
6131
  }
6071
6132
  async saveHuman(update) {
6072
6133
  const [outcome] = await this.saveHumanAll([update]);
@@ -7822,6 +7883,46 @@ init_traceMetadata();
7822
7883
  // src/traces.ts
7823
7884
  var ASSERTIONS_PATH = "/api/sdk/traces/assertions";
7824
7885
  var SEARCH_PATH = "/api/sdk/traces/search";
7886
+ var DEFAULT_GENERATION_TIMEOUT_MS = 6e5;
7887
+ var GENERATION_POLL_INTERVAL_MS = 2e3;
7888
+ var AssertionGenerationError = class extends Error {
7889
+ constructor(message, traceId) {
7890
+ super(message);
7891
+ this.traceId = traceId;
7892
+ this.name = "AssertionGenerationError";
7893
+ }
7894
+ };
7895
+ var AssertionGenerationTimeoutError = class extends AssertionGenerationError {
7896
+ constructor(traceId, timeoutMs) {
7897
+ super(
7898
+ `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.`,
7899
+ traceId
7900
+ );
7901
+ this.name = "AssertionGenerationTimeoutError";
7902
+ }
7903
+ };
7904
+ function sleep2(ms) {
7905
+ return new Promise((resolve) => setTimeout(resolve, ms));
7906
+ }
7907
+ function isGenerationFinished(traceId, generation) {
7908
+ if (generation === null || generation.status === "running") {
7909
+ return false;
7910
+ }
7911
+ if (generation.status === "failed") {
7912
+ throw new AssertionGenerationError(
7913
+ `Assertion generation for trace ${traceId} failed: ${generation.error ?? "no reason recorded"}. Pass regenerate: true to try again.`,
7914
+ traceId
7915
+ );
7916
+ }
7917
+ return true;
7918
+ }
7919
+ function draftsInGenerationOrder(assertions, assertionIds) {
7920
+ const byId = new Map(assertions.map((assertion) => [assertion.id, assertion]));
7921
+ return assertionIds.flatMap((id) => {
7922
+ const assertion = byId.get(id);
7923
+ return assertion === void 0 ? [] : [assertion];
7924
+ });
7925
+ }
7825
7926
  function traceAssertionsPath(traceId, suffix = "") {
7826
7927
  return `/api/sdk/traces/${encodeURIComponent(traceId)}/assertions${suffix}`;
7827
7928
  }
@@ -7853,6 +7954,34 @@ var TracesClient = class {
7853
7954
  });
7854
7955
  return response.assertions;
7855
7956
  }
7957
+ async generateAssertions(traceId, options = {}) {
7958
+ const started = await this.httpClient.request(
7959
+ traceAssertionsPath(traceId, "/generate"),
7960
+ {
7961
+ ...options.guidance === void 0 ? {} : { guidance: options.guidance },
7962
+ ...options.categoryIds === void 0 ? {} : { categoryIds: options.categoryIds },
7963
+ ...options.regenerate === void 0 ? {} : { regenerate: options.regenerate }
7964
+ }
7965
+ );
7966
+ if (isGenerationFinished(traceId, started.generation)) {
7967
+ return started.assertions;
7968
+ }
7969
+ const timeoutMs = options.timeoutMs ?? DEFAULT_GENERATION_TIMEOUT_MS;
7970
+ const deadline = Date.now() + timeoutMs;
7971
+ while (true) {
7972
+ if (Date.now() >= deadline) {
7973
+ throw new AssertionGenerationTimeoutError(traceId, timeoutMs);
7974
+ }
7975
+ await sleep2(GENERATION_POLL_INTERVAL_MS);
7976
+ const read2 = await this.getAssertions(traceId);
7977
+ if (isGenerationFinished(traceId, read2.generation)) {
7978
+ return draftsInGenerationOrder(
7979
+ read2.assertions,
7980
+ read2.generation.assertionIds
7981
+ );
7982
+ }
7983
+ }
7984
+ }
7856
7985
  async archiveAssertions(params) {
7857
7986
  const response = await this.httpClient.request(
7858
7987
  traceAssertionsPath(params.traceId, "/archive"),
@@ -8356,6 +8485,15 @@ var BitfabVercelAiHandler = class {
8356
8485
 
8357
8486
  // src/client.ts
8358
8487
  init_warnOnce();
8488
+ function resolveSpanExperimentId(options) {
8489
+ const { experimentId, testRunId } = options;
8490
+ if (experimentId !== void 0 && testRunId !== void 0 && experimentId !== testRunId) {
8491
+ throw new BitfabError(
8492
+ `experimentId ("${experimentId}") and its deprecated alias testRunId ("${testRunId}") were both passed with different values. Pass only experimentId.`
8493
+ );
8494
+ }
8495
+ return experimentId ?? testRunId;
8496
+ }
8359
8497
  var TRUNCATION_LIMIT_ORDER = [
8360
8498
  "max_depth",
8361
8499
  "max_captured_subtree_spans"
@@ -8945,13 +9083,12 @@ var Bitfab = class {
8945
9083
  "bitfab.node({ capture: false }) cannot use mockOnReplay: true because an uncaptured node has no recorded output."
8946
9084
  );
8947
9085
  }
9086
+ const experimentId = resolveSpanExperimentId(options);
8948
9087
  return {
8949
9088
  capture,
8950
9089
  type: options.type ?? "custom",
8951
9090
  ...options.name !== void 0 && { name: options.name },
8952
- ...options.testRunId !== void 0 && {
8953
- testRunId: options.testRunId
8954
- },
9091
+ ...experimentId !== void 0 && { experimentId },
8955
9092
  ...options.mockOnReplay !== void 0 && {
8956
9093
  mockOnReplay: options.mockOnReplay
8957
9094
  },
@@ -9086,6 +9223,7 @@ var Bitfab = class {
9086
9223
  return invokeWithoutNode();
9087
9224
  }
9088
9225
  const mockOnReplay = nodeConfiguration?.mockOnReplay ?? options.mockOnReplayDefault;
9226
+ const nodeExperimentId = nodeConfiguration === void 0 ? void 0 : resolveSpanExperimentId(nodeConfiguration);
9089
9227
  const childOptions = {
9090
9228
  name: spanName,
9091
9229
  type: nodeConfiguration?.type ?? "function",
@@ -9096,8 +9234,8 @@ var Bitfab = class {
9096
9234
  captureContent: true,
9097
9235
  autoTraceDefinition: definition,
9098
9236
  ...declaredNode && { declaredNode },
9099
- ...nodeConfiguration?.testRunId !== void 0 && {
9100
- testRunId: nodeConfiguration.testRunId
9237
+ ...nodeExperimentId !== void 0 && {
9238
+ experimentId: nodeExperimentId
9101
9239
  },
9102
9240
  ...mockOnReplay !== void 0 && {
9103
9241
  mockOnReplay
@@ -9758,6 +9896,7 @@ var Bitfab = class {
9758
9896
  const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
9759
9897
  const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
9760
9898
  const self = this;
9899
+ const optionExperimentId = resolveSpanExperimentId(options);
9761
9900
  const fnIsAsyncFunction = fn.constructor.name === "AsyncFunction";
9762
9901
  const fnReturnsPromise = fnIsAsyncFunction || (() => {
9763
9902
  try {
@@ -9860,7 +9999,7 @@ var Bitfab = class {
9860
9999
  const inputs = args;
9861
10000
  const startedAt = nowIsoTimestamp();
9862
10001
  const replayCtxAtStart = getReplayContext();
9863
- const testRunId = replayCtxAtStart?.testRunId ?? options.testRunId;
10002
+ const experimentId = replayCtxAtStart?.experimentId ?? optionExperimentId;
9864
10003
  if (isRootSpan && !activeTraceStates.has(traceId)) {
9865
10004
  const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
9866
10005
  self.simulationPlan.refresh();
@@ -9869,7 +10008,7 @@ var Bitfab = class {
9869
10008
  traceFunctionKey,
9870
10009
  startedAt,
9871
10010
  contexts: [],
9872
- ...testRunId !== void 0 && { testRunId },
10011
+ ...experimentId !== void 0 && { experimentId },
9873
10012
  ...replayCtxAtStart?.inputSourceTraceId && {
9874
10013
  inputSourceTraceId: replayCtxAtStart.inputSourceTraceId
9875
10014
  },
@@ -9915,7 +10054,7 @@ var Bitfab = class {
9915
10054
  contexts: newContext.contexts,
9916
10055
  prompt: newContext.prompt,
9917
10056
  endedAt,
9918
- ...testRunId !== void 0 && { testRunId },
10057
+ ...experimentId !== void 0 && { experimentId },
9919
10058
  ...replayCtx?.inputSourceSpanId && {
9920
10059
  inputSourceSpanId: replayCtx.inputSourceSpanId
9921
10060
  }
@@ -9936,7 +10075,7 @@ var Bitfab = class {
9936
10075
  name: traceState?.name,
9937
10076
  metadata: callerTraceMetadata(traceId),
9938
10077
  contexts: traceState?.contexts ?? [],
9939
- testRunId: traceState?.testRunId,
10078
+ experimentId: traceState?.experimentId,
9940
10079
  inputSourceTraceId: traceState?.inputSourceTraceId,
9941
10080
  replayAttempt: traceState?.replayAttempt,
9942
10081
  dbSnapshotRef: traceState?.dbSnapshotRef,
@@ -10407,7 +10546,7 @@ var Bitfab = class {
10407
10546
  completed: true,
10408
10547
  ...params.dropped && { dropped: true },
10409
10548
  ...params.sessionId && { sessionId: params.sessionId },
10410
- ...params.testRunId && { testRunId: params.testRunId }
10549
+ ...params.experimentId !== void 0 && experimentIdWireFields(params.experimentId)
10411
10550
  });
10412
10551
  }
10413
10552
  sendsSpanOnCompletion(context) {
@@ -10519,7 +10658,7 @@ var Bitfab = class {
10519
10658
  },
10520
10659
  ...params.declaredNode === true && { [DECLARED_NODE_FIELD]: true },
10521
10660
  rawSpan: externalSpan,
10522
- ...params.testRunId && { testRunId: params.testRunId },
10661
+ ...params.experimentId !== void 0 && experimentIdWireFields(params.experimentId),
10523
10662
  ...params.mocked && { mocked: true },
10524
10663
  ...params.mockTarget && { mockTarget: params.mockTarget },
10525
10664
  ...params.mockSource && { mockSource: params.mockSource }
@@ -11071,6 +11210,8 @@ function resolveTraceFunctionKey(registration) {
11071
11210
  // Annotate the CommonJS export names for ESM import in node:
11072
11211
  0 && (module.exports = {
11073
11212
  AssertionCategoriesClient,
11213
+ AssertionGenerationError,
11214
+ AssertionGenerationTimeoutError,
11074
11215
  BITFAB_PROGRESS_PREFIX,
11075
11216
  Bitfab,
11076
11217
  BitfabClaudeAgentHandler,
@@ -11086,6 +11227,8 @@ function resolveTraceFunctionKey(registration) {
11086
11227
  DEFAULT_SERVICE_URL,
11087
11228
  DatasetsClient,
11088
11229
  DbBranchReplayError,
11230
+ GraderRerunError,
11231
+ GraderRerunTimeoutError,
11089
11232
  GradersClient,
11090
11233
  HttpClient,
11091
11234
  LabelsClient,