bitfab 0.41.0 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.41.0";
100
+ __version__ = "0.43.0";
101
101
  __packageName__ = "bitfab";
102
102
  }
103
103
  });
@@ -1353,7 +1353,7 @@ function sourceTraceIdOf(payload) {
1353
1353
  const id = rawTrace?.id;
1354
1354
  return typeof id === "string" ? id : void 0;
1355
1355
  }
1356
- var REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS, OTLP_TRACES_ENDPOINT, RETRYABLE_STATUSES, EXIT_FLUSH_TIMEOUT_MS, DEFAULT_LIFECYCLE_TIMEOUT_MS2, pendingTracePromises, carrierSeq, HttpClient;
1356
+ 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, carrierSeq, HttpClient;
1357
1357
  var init_http = __esm({
1358
1358
  "src/http.ts"() {
1359
1359
  "use strict";
@@ -1367,6 +1367,7 @@ var init_http = __esm({
1367
1367
  init_unrefTimer();
1368
1368
  init_warnOnce();
1369
1369
  REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS = 3e5;
1370
+ REPLAY_COMPLETE_REQUEST_TIMEOUT_MS = 12e4;
1370
1371
  OTLP_TRACES_ENDPOINT = "/api/sdk/otel/v1/traces";
1371
1372
  RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
1372
1373
  EXIT_FLUSH_TIMEOUT_MS = 5e3;
@@ -1856,7 +1857,7 @@ var init_http = __esm({
1856
1857
  * Start a replay session by fetching historical traces.
1857
1858
  * Blocking call - creates a test run and returns lightweight item references.
1858
1859
  */
1859
- async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetId, graderIds, dbBranchSettings) {
1860
+ async startReplay(traceFunctionKey, limit, traceIds, name, codeChangeDescription, codeChangeFiles, includeDbBranchLease, experimentGroupId, datasetId, graderIds, dbBranchSettings, attempts, includeOriginalMetadata) {
1860
1861
  const payload = { traceFunctionKey };
1861
1862
  if (limit !== void 0) {
1862
1863
  payload.limit = limit;
@@ -1889,6 +1890,12 @@ var init_http = __esm({
1889
1890
  if (dbBranchSettings !== void 0) {
1890
1891
  payload.dbBranchSettings = dbBranchSettings;
1891
1892
  }
1893
+ if (attempts !== void 0 && attempts > 1) {
1894
+ payload.attempts = attempts;
1895
+ }
1896
+ if (includeOriginalMetadata) {
1897
+ payload.includeOriginalMetadata = true;
1898
+ }
1892
1899
  const timeout = includeDbBranchLease ? REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS : 3e4;
1893
1900
  return this.request("/api/sdk/replay/start", payload, {
1894
1901
  timeout
@@ -2004,7 +2011,7 @@ var init_http = __esm({
2004
2011
  return this.request(
2005
2012
  "/api/sdk/replay/complete",
2006
2013
  { testRunId },
2007
- { timeout: 3e4 }
2014
+ { timeout: REPLAY_COMPLETE_REQUEST_TIMEOUT_MS }
2008
2015
  );
2009
2016
  }
2010
2017
  /**
@@ -2013,10 +2020,15 @@ var init_http = __esm({
2013
2020
  * snapshot + preview branch and polls operations to readiness, which
2014
2021
  * can take seconds.
2015
2022
  */
2016
- async resolveDbBranchLease(testRunId, traceId, dbBranchSettings) {
2023
+ async resolveDbBranchLease(testRunId, traceId, dbBranchSettings, attempt) {
2017
2024
  return this.request(
2018
2025
  "/api/sdk/replay/resolveDbBranchLease",
2019
- { testRunId, traceId, dbBranchSettings },
2026
+ {
2027
+ testRunId,
2028
+ traceId,
2029
+ dbBranchSettings,
2030
+ ...attempt !== void 0 && attempt > 0 ? { attempt } : {}
2031
+ },
2020
2032
  { timeout: REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS }
2021
2033
  );
2022
2034
  }
@@ -2452,6 +2464,23 @@ __export(replay_exports, {
2452
2464
  sleepForReplayPersistence: () => sleepForReplayPersistence,
2453
2465
  waitForReplayPersistence: () => waitForReplayPersistence
2454
2466
  });
2467
+ function expandReplayWork(serverItems, attempts) {
2468
+ return Array.from(
2469
+ { length: attempts },
2470
+ (_, attempt) => serverItems.map((serverItem) => ({ serverItem, attempt }))
2471
+ ).flat();
2472
+ }
2473
+ function resolveAttempts(attempts) {
2474
+ if (attempts === void 0) {
2475
+ return 1;
2476
+ }
2477
+ if (!Number.isInteger(attempts) || attempts < 1 || attempts > MAX_REPLAY_ATTEMPTS) {
2478
+ throw new BitfabError(
2479
+ `attempts must be an integer from 1 to ${MAX_REPLAY_ATTEMPTS} (got ${attempts}).`
2480
+ );
2481
+ }
2482
+ return attempts;
2483
+ }
2455
2484
  function dbBranchEnabled(dbBranch) {
2456
2485
  return dbBranch !== void 0 && dbBranch !== false;
2457
2486
  }
@@ -2480,6 +2509,9 @@ function reportReplayProgress(progress) {
2480
2509
  } catch {
2481
2510
  }
2482
2511
  }
2512
+ function isPlainRecord(value) {
2513
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2514
+ }
2483
2515
  function errorMessage(error) {
2484
2516
  return error instanceof Error ? error.message : String(error);
2485
2517
  }
@@ -2575,7 +2607,7 @@ function buildMockTree(rootNode) {
2575
2607
  }
2576
2608
  return { spans };
2577
2609
  }
2578
- async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy, resolvedOverrides, replayedTraceId, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun) {
2610
+ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy, resolvedOverrides, replayedTraceId, attempt, includeDbBranchLease, dbBranchSettings, adaptInputs, dryRun) {
2579
2611
  let lease = includeDbBranchLease ? serverItem.dbBranchLease : void 0;
2580
2612
  let leaseError = includeDbBranchLease ? serverItem.dbBranchLeaseError : void 0;
2581
2613
  let dbSnapshotRef = serverItem.dbSnapshotRef;
@@ -2597,7 +2629,8 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2597
2629
  resolved = await httpClient.resolveDbBranchLease(
2598
2630
  testRunId,
2599
2631
  originalTraceId,
2600
- dbBranchSettings
2632
+ dbBranchSettings,
2633
+ attempt
2601
2634
  );
2602
2635
  } catch (cause) {
2603
2636
  throw new DbBranchReplayError(
@@ -2626,12 +2659,14 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2626
2659
  inputs = deserializeInputs(spanData);
2627
2660
  originalOutput = deserializeOutput(spanData);
2628
2661
  if (adaptInputs) {
2662
+ const originalMetadata = serverItem.originalMetadata;
2629
2663
  inputs = adaptInputs(inputs, {
2630
2664
  originalTraceId,
2631
2665
  originalSpanId,
2632
2666
  // Deprecated aliases for originalTraceId/originalSpanId.
2633
2667
  sourceTraceId: originalTraceId,
2634
- sourceSpanId: originalSpanId
2668
+ sourceSpanId: originalSpanId,
2669
+ metadata: isPlainRecord(originalMetadata) ? { ...originalMetadata } : {}
2635
2670
  });
2636
2671
  }
2637
2672
  const hasOverrides = resolvedOverrides.length > 0;
@@ -2677,6 +2712,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2677
2712
  originalSpanId,
2678
2713
  sourceTraceId: originalTraceId,
2679
2714
  sourceSpanId: originalSpanId,
2715
+ attempt,
2680
2716
  input: inputs,
2681
2717
  result: void 0,
2682
2718
  originalOutput,
@@ -2705,6 +2741,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2705
2741
  inputSourceSpanId: span.id,
2706
2742
  inputSourceTraceId: span.externalTraceId,
2707
2743
  sourceBitfabTraceId: originalTraceId,
2744
+ replayAttempt: attempt,
2708
2745
  mockTree,
2709
2746
  callCounters: mockTree ? /* @__PURE__ */ new Map() : void 0,
2710
2747
  mockStrategy,
@@ -2756,6 +2793,7 @@ async function processItem(httpClient, serverItem, fn, testRunId, mockStrategy,
2756
2793
  // Deprecated aliases for originalTraceId/originalSpanId.
2757
2794
  sourceTraceId: originalTraceId,
2758
2795
  sourceSpanId: originalSpanId,
2796
+ attempt,
2759
2797
  input: inputs,
2760
2798
  result,
2761
2799
  originalOutput,
@@ -2884,6 +2922,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2884
2922
  } catch {
2885
2923
  }
2886
2924
  }
2925
+ const attempts = resolveAttempts(options?.attempts);
2887
2926
  await replayContextReady;
2888
2927
  let codeChangeDescription = options?.codeChangeDescription;
2889
2928
  let codeChangeFiles = options?.codeChangeFiles;
@@ -2916,7 +2955,9 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2916
2955
  options?.experimentGroupId,
2917
2956
  options?.datasetId,
2918
2957
  options?.graderIds,
2919
- resolveDbBranchSettings(options?.dbBranch)
2958
+ resolveDbBranchSettings(options?.dbBranch),
2959
+ attempts,
2960
+ options?.adaptInputs !== void 0
2920
2961
  );
2921
2962
  if (serverItems.length === 0) {
2922
2963
  try {
@@ -2933,10 +2974,11 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2933
2974
  ...normalizeMockOverrides(options?.mockOverride),
2934
2975
  ...registeredOverrides
2935
2976
  ];
2936
- const replayedTraceIds = serverItems.map(() => randomUuid());
2977
+ const workItems = expandReplayWork(serverItems, attempts);
2978
+ const replayedTraceIds = workItems.map(() => randomUuid());
2937
2979
  httpClient.trackTraceDeliveries(replayedTraceIds);
2938
- const tasks = serverItems.map(
2939
- (serverItem, index) => () => processItem(
2980
+ const tasks = workItems.map(
2981
+ ({ serverItem, attempt }, index) => () => processItem(
2940
2982
  httpClient,
2941
2983
  serverItem,
2942
2984
  fn,
@@ -2944,6 +2986,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
2944
2986
  mockStrategy,
2945
2987
  resolvedOverrides,
2946
2988
  replayedTraceIds[index],
2989
+ attempt,
2947
2990
  dbBranchEnabled(options?.dbBranch) && options?.dryRun !== true,
2948
2991
  resolveDbBranchSettings(options?.dbBranch),
2949
2992
  options?.adaptInputs,
@@ -3011,6 +3054,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
3011
3054
  // Deprecated aliases for originalTraceId/originalSpanId.
3012
3055
  sourceTraceId: item.originalTraceId ?? null,
3013
3056
  sourceSpanId: item.originalSpanId ?? null,
3057
+ attempt: item.attempt,
3014
3058
  input: item.input,
3015
3059
  result: item.result,
3016
3060
  originalOutput: item.originalOutput,
@@ -3032,7 +3076,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
3032
3076
  },
3033
3077
  options?.onItemStart ? (index) => {
3034
3078
  started += 1;
3035
- const serverItem = serverItems[index];
3079
+ const { serverItem, attempt } = workItems[index];
3036
3080
  const originalTraceId = serverItem.originalTraceId ?? serverItem.sourceTraceId;
3037
3081
  const originalSpanId = serverItem.originalSpanId ?? serverItem.sourceSpanId;
3038
3082
  try {
@@ -3048,7 +3092,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
3048
3092
  originalTraceId,
3049
3093
  originalSpanId,
3050
3094
  sourceTraceId: originalTraceId,
3051
- sourceSpanId: originalSpanId
3095
+ sourceSpanId: originalSpanId,
3096
+ attempt
3052
3097
  }
3053
3098
  });
3054
3099
  } catch {
@@ -3060,7 +3105,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
3060
3105
  const dryResult = {
3061
3106
  items: resultItems,
3062
3107
  testRunId,
3063
- testRunUrl: fullTestRunUrl
3108
+ testRunUrl: fullTestRunUrl,
3109
+ attempts
3064
3110
  };
3065
3111
  await writeReplayResultFile(dryResult);
3066
3112
  return dryResult;
@@ -3129,7 +3175,8 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options, reg
3129
3175
  const result = {
3130
3176
  items: resultItems,
3131
3177
  testRunId,
3132
- testRunUrl: fullTestRunUrl
3178
+ testRunUrl: fullTestRunUrl,
3179
+ attempts
3133
3180
  };
3134
3181
  await writeReplayResultFile(result);
3135
3182
  if (!options?.onItemFinish) {
@@ -3170,7 +3217,7 @@ async function writeReplayResultFile(result) {
3170
3217
  }
3171
3218
  }
3172
3219
  }
3173
- var REPLAY_PERSISTENCE_TIMEOUT_MS, BITFAB_PROGRESS_PREFIX, ReplayError, DbBranchReplayError;
3220
+ var REPLAY_PERSISTENCE_TIMEOUT_MS, MAX_REPLAY_ATTEMPTS, BITFAB_PROGRESS_PREFIX, ReplayError, DbBranchReplayError;
3174
3221
  var init_replay = __esm({
3175
3222
  "src/replay.ts"() {
3176
3223
  "use strict";
@@ -3182,6 +3229,7 @@ var init_replay = __esm({
3182
3229
  init_replayContext();
3183
3230
  init_serialize();
3184
3231
  REPLAY_PERSISTENCE_TIMEOUT_MS = 3e4;
3232
+ MAX_REPLAY_ATTEMPTS = 100;
3185
3233
  BITFAB_PROGRESS_PREFIX = "@@bitfab:progress ";
3186
3234
  ReplayError = class extends BitfabError {
3187
3235
  constructor(message, items, testRunId, testRunUrl, cause) {
@@ -5270,7 +5318,8 @@ var BitfabLangGraphIntegration = class {
5270
5318
  type: "function",
5271
5319
  captureWhen: "nested",
5272
5320
  mockOnReplay: shouldMock,
5273
- finalize: finalizeToolResult
5321
+ finalize: finalizeToolResult,
5322
+ surface: "inherit"
5274
5323
  },
5275
5324
  async (_args) => await originalInvoke(input, ...rest)
5276
5325
  );
@@ -5313,7 +5362,7 @@ var BitfabLangGraphIntegration = class {
5313
5362
  wrapInvoke(fn) {
5314
5363
  return this.client.withSpan(
5315
5364
  this.traceFunctionKey,
5316
- { name: this.traceFunctionKey, type: "agent" },
5365
+ { name: this.traceFunctionKey, type: "agent", surface: "inherit" },
5317
5366
  fn
5318
5367
  );
5319
5368
  }
@@ -5366,7 +5415,11 @@ var BitfabOpenAIAgentHandler = class {
5366
5415
  }
5367
5416
  return res?.finalOutput;
5368
5417
  };
5369
- const options_ = { type: "agent", finalize };
5418
+ const options_ = {
5419
+ type: "agent",
5420
+ finalize,
5421
+ surface: "inherit"
5422
+ };
5370
5423
  const traced = this.withSpanFn(
5371
5424
  this.traceFunctionKey,
5372
5425
  options_,
@@ -5430,6 +5483,38 @@ _context = new WeakMap();
5430
5483
 
5431
5484
  // src/client.ts
5432
5485
  init_replayContext();
5486
+
5487
+ // src/seedContext.ts
5488
+ init_asyncStorage();
5489
+ var seedContextStorage = null;
5490
+ var SEED_CONTEXT_STORAGE_SYMBOL = /* @__PURE__ */ Symbol.for("bitfab.seedContextStorage");
5491
+ var seedContextReady = asyncStorageReady.then(() => {
5492
+ const shared = globalThis;
5493
+ const existing = shared[SEED_CONTEXT_STORAGE_SYMBOL];
5494
+ if (existing) {
5495
+ seedContextStorage = existing;
5496
+ return;
5497
+ }
5498
+ const created = createAsyncLocalStorage();
5499
+ if (created) {
5500
+ shared[SEED_CONTEXT_STORAGE_SYMBOL] = created;
5501
+ seedContextStorage = created;
5502
+ }
5503
+ });
5504
+ function getSeedContext() {
5505
+ return seedContextStorage?.getStore() ?? null;
5506
+ }
5507
+ function inSeedScope() {
5508
+ return getSeedContext() !== null;
5509
+ }
5510
+ function runWithSeedContext(ctx, fn) {
5511
+ if (seedContextStorage) {
5512
+ return seedContextStorage.run(ctx, fn);
5513
+ }
5514
+ return fn();
5515
+ }
5516
+
5517
+ // src/client.ts
5433
5518
  init_serialize();
5434
5519
 
5435
5520
  // src/tracing.ts
@@ -5765,7 +5850,8 @@ var BitfabVercelAiHandler = class {
5765
5850
  key,
5766
5851
  {
5767
5852
  type: "llm",
5768
- finalize: (result) => summarizeGenerate(result ?? {}, label)
5853
+ finalize: (result) => summarizeGenerate(result ?? {}, label),
5854
+ surface: "inherit"
5769
5855
  },
5770
5856
  () => doGenerate()
5771
5857
  );
@@ -5783,7 +5869,7 @@ var BitfabVercelAiHandler = class {
5783
5869
  // The wrapped fn returns immediately with the live stream, so the span
5784
5870
  // output cannot be read from the return value. `finalize` instead
5785
5871
  // awaits the summary the accumulator resolves once the stream drains.
5786
- { type: "llm", finalize: () => summary },
5872
+ { type: "llm", finalize: () => summary, surface: "inherit" },
5787
5873
  async () => {
5788
5874
  const result = await doStream();
5789
5875
  const stream = result.stream.pipeThrough(
@@ -5800,6 +5886,18 @@ var BitfabVercelAiHandler = class {
5800
5886
 
5801
5887
  // src/client.ts
5802
5888
  init_warnOnce();
5889
+ var MIXED_SURFACE_REMEDY = {
5890
+ "opt-in": "Inside a withTrace/trace subtree, configure a discovered call with node()/withNode() instead, or trace this workflow with withSpan() only.",
5891
+ "opt-out": "Wrap the caller with withTrace()/trace() as well, or wrap this function with withSpan()."
5892
+ };
5893
+ function warnMixedTracingSurfaces(traceFunctionKey, entered, enclosing) {
5894
+ const enteredApi = entered === "opt-in" ? "withSpan()" : "withTrace()";
5895
+ const enclosingApi = enclosing === "opt-in" ? "withSpan()" : "withTrace()";
5896
+ warnOnce(
5897
+ `mixed-tracing-surface:${traceFunctionKey}:${entered}`,
5898
+ `opt-in and opt-out tracing are mixed in one call stack: ${enteredApi} (${entered}) for "${traceFunctionKey}" was entered inside a ${enclosingApi} call (${enclosing}). Both are recorded, and the spans nest as written. ${MIXED_SURFACE_REMEDY[entered]}`
5899
+ );
5900
+ }
5803
5901
  var activeTraceStates = /* @__PURE__ */ new Map();
5804
5902
  var asyncLocalStorage = null;
5805
5903
  var SPAN_CONTEXT_STORAGE_SYMBOL = /* @__PURE__ */ Symbol.for("bitfab.spanContextStorage");
@@ -6163,7 +6261,13 @@ var Bitfab = class {
6163
6261
  this.serviceUrl = config.serviceUrl ?? DEFAULT_SERVICE_URL;
6164
6262
  this.timeout = config.timeout ?? 12e4;
6165
6263
  this.envVars = config.envVars ?? {};
6166
- this.explicitlyEnabled = config.enabled ?? true;
6264
+ if (config.enabled !== void 0) {
6265
+ warnOnce(
6266
+ "deprecated-enabled-option",
6267
+ "Bitfab({ enabled }) is deprecated; pass captureEnabled instead."
6268
+ );
6269
+ }
6270
+ this.captureConfigured = (config.captureEnabled ?? true) && (config.enabled ?? true);
6167
6271
  this.strict = config.strict ?? false;
6168
6272
  this.bamlClient = config.bamlClient ?? null;
6169
6273
  if (config.dbSnapshot) {
@@ -6199,9 +6303,6 @@ var Bitfab = class {
6199
6303
  if (!descriptor || typeof descriptor.value !== "function") {
6200
6304
  throw new BitfabError("@bitfab.trace can only decorate methods");
6201
6305
  }
6202
- if (!this.explicitlyEnabled) {
6203
- return;
6204
- }
6205
6306
  descriptor.value = this.createAutoTraceRoot(
6206
6307
  traceFunctionKey,
6207
6308
  String(propertyKey),
@@ -6215,9 +6316,6 @@ var Bitfab = class {
6215
6316
  if (typeof method !== "function" || context?.kind !== "method" || context.name === void 0) {
6216
6317
  throw new BitfabError("@bitfab.trace can only decorate methods");
6217
6318
  }
6218
- if (!this.explicitlyEnabled) {
6219
- return method;
6220
- }
6221
6319
  return this.createAutoTraceRoot(
6222
6320
  traceFunctionKey,
6223
6321
  String(context.name),
@@ -6233,9 +6331,6 @@ var Bitfab = class {
6233
6331
  if (!fn) {
6234
6332
  throw new BitfabError("bitfab.withTrace requires a function");
6235
6333
  }
6236
- if (!this.explicitlyEnabled) {
6237
- return fn;
6238
- }
6239
6334
  const name = fn.name !== "" ? fn.name : traceFunctionKey;
6240
6335
  return this.createAutoTraceRoot(traceFunctionKey, name, options, fn);
6241
6336
  }
@@ -6259,9 +6354,6 @@ var Bitfab = class {
6259
6354
  if (!descriptor || typeof descriptor.value !== "function") {
6260
6355
  throw new BitfabError("@bitfab.node can only decorate methods");
6261
6356
  }
6262
- if (!this.explicitlyEnabled) {
6263
- return;
6264
- }
6265
6357
  descriptor.value = this.createAutoTraceNode(
6266
6358
  configuration,
6267
6359
  descriptor.value,
@@ -6274,9 +6366,6 @@ var Bitfab = class {
6274
6366
  if (typeof method !== "function" || context?.kind !== "method") {
6275
6367
  throw new BitfabError("@bitfab.node can only decorate methods");
6276
6368
  }
6277
- if (!this.explicitlyEnabled) {
6278
- return method;
6279
- }
6280
6369
  return this.createAutoTraceNode(
6281
6370
  configuration,
6282
6371
  method,
@@ -6292,9 +6381,6 @@ var Bitfab = class {
6292
6381
  throw new BitfabError("bitfab.withNode requires a function");
6293
6382
  }
6294
6383
  const configuration = this.resolveNodeConfiguration(options);
6295
- if (!this.explicitlyEnabled) {
6296
- return fn;
6297
- }
6298
6384
  const functionName = internalFunctionName ?? fn.name;
6299
6385
  if (functionName === "") {
6300
6386
  throw new BitfabError(
@@ -6347,9 +6433,14 @@ var Bitfab = class {
6347
6433
  );
6348
6434
  const excluded = new Set(options.exclude ?? []);
6349
6435
  const includeWrappers = options.includeWrappers ?? false;
6436
+ const rootOptions = {
6437
+ name: options.name ?? name,
6438
+ type: options.type ?? "custom",
6439
+ surface: "opt-out"
6440
+ };
6350
6441
  const tracedRoot = this.withSpan(
6351
6442
  traceFunctionKey,
6352
- { name: options.name ?? name, type: options.type ?? "custom" },
6443
+ rootOptions,
6353
6444
  function(...args) {
6354
6445
  const capturePolicy = getAutoTraceCapturePolicy(self, traceFunctionKey);
6355
6446
  self.refreshAutoTraceCapturePolicy(traceFunctionKey);
@@ -6393,6 +6484,7 @@ var Bitfab = class {
6393
6484
  name: nodeConfiguration?.name ?? definition.name,
6394
6485
  type: nodeConfiguration?.type ?? "function",
6395
6486
  captureWhen: "nested",
6487
+ surface: "opt-out",
6396
6488
  functionId: definition.id,
6397
6489
  captureContent: nodeConfiguration !== void 0 || capturePolicy === void 0 || capturePolicy.has(definition.id),
6398
6490
  autoTraceDefinition: definition,
@@ -6430,7 +6522,7 @@ var Bitfab = class {
6430
6522
  }
6431
6523
  );
6432
6524
  const autoTraceRoot = function(...args) {
6433
- if (!self.isTracingEnabled()) {
6525
+ if (!self.shouldRecord()) {
6434
6526
  return fn.apply(this, args);
6435
6527
  }
6436
6528
  return tracedRoot.apply(this, args);
@@ -6438,6 +6530,7 @@ var Bitfab = class {
6438
6530
  Object.defineProperty(autoTraceRoot, "_bitfabTraceFunctionKey", {
6439
6531
  value: traceFunctionKey
6440
6532
  });
6533
+ Object.defineProperty(autoTraceRoot, "_bitfabWrappedFn", { value: fn });
6441
6534
  return autoTraceRoot;
6442
6535
  }
6443
6536
  refreshAutoTraceCapturePolicy(traceFunctionKey) {
@@ -6521,7 +6614,7 @@ var Bitfab = class {
6521
6614
  "Bitfab: no API key resolved. Set BITFAB_API_KEY or pass apiKey to new Bitfab(). If a script loads env with dotenv, load it before the module that constructs the client is imported (e.g. `node --env-file=.env script.ts`), or pass `apiKey: () => process.env.BITFAB_API_KEY`."
6522
6615
  );
6523
6616
  }
6524
- if (this.explicitlyEnabled && !this.apiKeyWarned) {
6617
+ if (this.captureConfigured && !this.apiKeyWarned) {
6525
6618
  this.apiKeyWarned = true;
6526
6619
  console.warn(
6527
6620
  "Bitfab: apiKey is empty - tracing is disabled. Provide a valid API key to enable tracing."
@@ -6529,17 +6622,21 @@ var Bitfab = class {
6529
6622
  }
6530
6623
  return void 0;
6531
6624
  }
6532
- /**
6533
- * Whether tracing should run, decided lazily at call time (not frozen at
6534
- * construction). An explicit `enabled: false` short-circuits without ever
6535
- * touching the key.
6536
- */
6537
- isTracingEnabled() {
6538
- if (!this.explicitlyEnabled) {
6625
+ isCaptureEnabled() {
6626
+ if (!this.captureConfigured) {
6539
6627
  return false;
6540
6628
  }
6541
6629
  return this.resolveApiKey() !== void 0;
6542
6630
  }
6631
+ shouldRecord() {
6632
+ if (!this.captureConfigured && !getReplayContext() && !inSeedScope()) {
6633
+ return false;
6634
+ }
6635
+ return this.resolveApiKey() !== void 0;
6636
+ }
6637
+ get captureEnabled() {
6638
+ return this.isCaptureEnabled();
6639
+ }
6543
6640
  /**
6544
6641
  * Fetch the function with its current version and BAML prompt from the server.
6545
6642
  *
@@ -6951,9 +7048,6 @@ var Bitfab = class {
6951
7048
  * @returns A wrapped function with the same signature that creates spans for inputs and outputs
6952
7049
  */
6953
7050
  withSpan(traceFunctionKey, optionsOrFn, maybeFn) {
6954
- if (!this.explicitlyEnabled) {
6955
- return typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
6956
- }
6957
7051
  const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
6958
7052
  const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
6959
7053
  const self = this;
@@ -6967,7 +7061,7 @@ var Bitfab = class {
6967
7061
  }
6968
7062
  })();
6969
7063
  const wrappedFn = function(...args) {
6970
- if (!self.isTracingEnabled()) {
7064
+ if (!self.shouldRecord()) {
6971
7065
  return fn.apply(this, args);
6972
7066
  }
6973
7067
  initializeAsyncContext();
@@ -7015,11 +7109,26 @@ var Bitfab = class {
7015
7109
  const currentStack = getSpanStack();
7016
7110
  const parentContext = currentStack[currentStack.length - 1];
7017
7111
  const replayCtxForTraceId = parentContext ? null : getReplayContext();
7018
- const traceId = parentContext?.traceId ?? replayCtxForTraceId?.traceId ?? randomUuid();
7112
+ const seedCtxForTraceId = parentContext ? null : getSeedContext();
7113
+ const traceId = parentContext?.traceId ?? replayCtxForTraceId?.traceId ?? seedCtxForTraceId?.traceId ?? randomUuid();
7019
7114
  const spanId = randomUuid();
7020
7115
  const parentSpanId = parentContext?.spanId ?? null;
7021
7116
  const isRootSpan = parentSpanId === null;
7022
- const newContext = { traceId, spanId, contexts: [] };
7117
+ const requestedSurface = options.surface ?? "opt-in";
7118
+ const surface = requestedSurface === "inherit" ? parentContext?.surface ?? "opt-in" : requestedSurface;
7119
+ if (requestedSurface !== "inherit" && parentContext?.surface !== void 0 && parentContext.surface !== surface) {
7120
+ warnMixedTracingSurfaces(
7121
+ traceFunctionKey,
7122
+ surface,
7123
+ parentContext.surface
7124
+ );
7125
+ }
7126
+ const newContext = {
7127
+ traceId,
7128
+ spanId,
7129
+ contexts: [],
7130
+ surface
7131
+ };
7023
7132
  newStack = [...currentStack, newContext];
7024
7133
  const inputs = args;
7025
7134
  const startedAt = nowIsoTimestamp();
@@ -7035,6 +7144,9 @@ var Bitfab = class {
7035
7144
  ...replayCtxAtStart?.inputSourceTraceId && {
7036
7145
  inputSourceTraceId: replayCtxAtStart.inputSourceTraceId
7037
7146
  },
7147
+ ...replayCtxAtStart?.replayAttempt !== void 0 && {
7148
+ replayAttempt: replayCtxAtStart.replayAttempt
7149
+ },
7038
7150
  dbSnapshotRef
7039
7151
  });
7040
7152
  registeredTraceId = traceId;
@@ -7085,6 +7197,7 @@ var Bitfab = class {
7085
7197
  contexts: traceState?.contexts ?? [],
7086
7198
  testRunId: traceState?.testRunId,
7087
7199
  inputSourceTraceId: traceState?.inputSourceTraceId,
7200
+ replayAttempt: traceState?.replayAttempt,
7088
7201
  dbSnapshotRef: traceState?.dbSnapshotRef,
7089
7202
  dropped: traceState?.dropped,
7090
7203
  ingestionType: traceState?.ingestionType,
@@ -7297,7 +7410,7 @@ var Bitfab = class {
7297
7410
  if (registeredTraceId) {
7298
7411
  activeTraceStates.delete(registeredTraceId);
7299
7412
  }
7300
- if (getReplayContext()) {
7413
+ if (getReplayContext() || inSeedScope()) {
7301
7414
  throw setupError;
7302
7415
  }
7303
7416
  warnOnce(
@@ -7372,7 +7485,7 @@ var Bitfab = class {
7372
7485
  return {
7373
7486
  traceId,
7374
7487
  addContext: (context) => {
7375
- if (!this.isTracingEnabled()) {
7488
+ if (!this.shouldRecord()) {
7376
7489
  return Promise.resolve();
7377
7490
  }
7378
7491
  if (typeof context !== "object" || context === null) {
@@ -7383,7 +7496,7 @@ var Bitfab = class {
7383
7496
  });
7384
7497
  },
7385
7498
  setMetadata: (metadata) => {
7386
- if (!this.isTracingEnabled()) {
7499
+ if (!this.shouldRecord()) {
7387
7500
  return Promise.resolve();
7388
7501
  }
7389
7502
  if (typeof metadata !== "object" || metadata === null) {
@@ -7392,7 +7505,7 @@ var Bitfab = class {
7392
7505
  return this.httpClient.patchTrace(traceId, { mergeMetadata: metadata });
7393
7506
  },
7394
7507
  setSessionId: (sessionId) => {
7395
- if (!this.isTracingEnabled()) {
7508
+ if (!this.shouldRecord()) {
7396
7509
  return Promise.resolve();
7397
7510
  }
7398
7511
  if (typeof sessionId !== "string" || sessionId.length === 0) {
@@ -7401,7 +7514,7 @@ var Bitfab = class {
7401
7514
  return this.httpClient.patchTrace(traceId, { setSessionId: sessionId });
7402
7515
  },
7403
7516
  setName: (name) => {
7404
- if (!this.isTracingEnabled()) {
7517
+ if (!this.shouldRecord()) {
7405
7518
  return Promise.resolve();
7406
7519
  }
7407
7520
  if (typeof name !== "string" || name.length === 0) {
@@ -7482,6 +7595,9 @@ var Bitfab = class {
7482
7595
  if (params.inputSourceTraceId) {
7483
7596
  rawTrace.input_source_trace_id = params.inputSourceTraceId;
7484
7597
  }
7598
+ if (params.replayAttempt !== void 0) {
7599
+ rawTrace.replay_attempt = params.replayAttempt;
7600
+ }
7485
7601
  if (params.dbSnapshotRef) {
7486
7602
  rawTrace.db_snapshot_ref = params.dbSnapshotRef;
7487
7603
  }
@@ -7634,22 +7750,13 @@ var Bitfab = class {
7634
7750
  clearMockOverrides() {
7635
7751
  this.mockOverrides.length = 0;
7636
7752
  }
7637
- /**
7638
- * Write a replayable trace from a case, without running anything.
7639
- *
7640
- * Use this to turn a corpus you already hold (a Braintrust dataset, a
7641
- * spreadsheet, hand-written cases) into traces that {@link replay} can
7642
- * select. The recorded root span carries `input` as its input and `expected`
7643
- * as its output, so replay reports each item against the value you expected
7644
- * rather than against a previous run.
7645
- *
7646
- * A seeded trace has no child spans and no database pin, so replay mocking
7647
- * has nothing recorded to substitute and `dbBranch` refuses it. Pass
7648
- * `mockOverride` at replay time for calls that must not run.
7649
- *
7650
- * @returns The trace ID, usable with `replay({ traceIds: [...] })`.
7651
- */
7652
- seedTrace(traceFunctionKey, options) {
7753
+ seedTrace(traceFunctionKey, optionsOrFn, runOptions) {
7754
+ if (typeof optionsOrFn === "function") {
7755
+ return this.seedTraceByRunning(traceFunctionKey, optionsOrFn, runOptions);
7756
+ }
7757
+ return this.seedTraceFromCase(traceFunctionKey, optionsOrFn);
7758
+ }
7759
+ seedTraceFromCase(traceFunctionKey, options) {
7653
7760
  const { input } = options;
7654
7761
  const fn = options.fn?._bitfabWrappedFn ?? options.fn;
7655
7762
  if (fn && input.length < fn.length) {
@@ -7698,6 +7805,50 @@ var Bitfab = class {
7698
7805
  }
7699
7806
  return traceId;
7700
7807
  }
7808
+ async seedTraceByRunning(traceFunctionKey, fn, options) {
7809
+ const wrappedKey = fn._bitfabTraceFunctionKey;
7810
+ let target = fn;
7811
+ if (wrappedKey === void 0) {
7812
+ target = this.withSpan(
7813
+ traceFunctionKey,
7814
+ { name: traceFunctionKey, type: "agent" },
7815
+ fn
7816
+ );
7817
+ } else if (wrappedKey !== traceFunctionKey) {
7818
+ throw new BitfabError(
7819
+ `Function is wrapped with trace function key '${wrappedKey}' but seedTrace was called with '${traceFunctionKey}'. Pass matching keys, or pass the unwrapped function to seed it under the explicit key.`
7820
+ );
7821
+ }
7822
+ await seedContextReady;
7823
+ const traceId = randomUuid();
7824
+ activeTraceStates.set(traceId, {
7825
+ traceId,
7826
+ startedAt: nowIsoTimestamp(),
7827
+ contexts: [],
7828
+ ingestionType: "seeded",
7829
+ ...options?.sessionId !== void 0 && { sessionId: options.sessionId },
7830
+ ...options?.name !== void 0 && { name: options.name },
7831
+ ...options?.metadata !== void 0 && { metadata: options.metadata }
7832
+ });
7833
+ const args = options?.args ?? [];
7834
+ let unrecorded = true;
7835
+ try {
7836
+ await runWithSeedContext({ traceId }, async () => target(...args));
7837
+ } finally {
7838
+ try {
7839
+ const { flushTraces: flushTraces2 } = await Promise.resolve().then(() => (init_http(), http_exports));
7840
+ await flushTraces2(3e4);
7841
+ } finally {
7842
+ unrecorded = activeTraceStates.delete(traceId);
7843
+ }
7844
+ }
7845
+ if (unrecorded) {
7846
+ throw new BitfabError(
7847
+ `seedTrace recorded nothing for '${traceFunctionKey}': the call finished without a root span. Check that an API key resolves (BITFAB_API_KEY or apiKey), and that fn is a regular or async function rather than a generator.`
7848
+ );
7849
+ }
7850
+ return traceId;
7851
+ }
7701
7852
  async replay(traceFunctionKey, fn, options) {
7702
7853
  const wrappedKey = fn._bitfabTraceFunctionKey;
7703
7854
  let replayFn = fn;
@@ -7958,7 +8109,7 @@ init_replay();
7958
8109
  function defineReplayRegistry(registry) {
7959
8110
  return registry;
7960
8111
  }
7961
- async function seedFromRegistry(registry, pipeline, cases) {
8112
+ async function seedFromRegistry(registry, pipeline, cases, options = {}) {
7962
8113
  const registration = registry[pipeline];
7963
8114
  if (registration === void 0) {
7964
8115
  throw new BitfabError(
@@ -7966,6 +8117,19 @@ async function seedFromRegistry(registry, pipeline, cases) {
7966
8117
  );
7967
8118
  }
7968
8119
  const traceFunctionKey = resolveTraceFunctionKey(registration);
8120
+ if (options.run === true) {
8121
+ const traceIds2 = [];
8122
+ for (const seedCase of cases) {
8123
+ traceIds2.push(
8124
+ await registration.client.seedTrace(traceFunctionKey, registration.fn, {
8125
+ args: seedCase.input,
8126
+ metadata: seedCase.metadata,
8127
+ sessionId: seedCase.sessionId
8128
+ })
8129
+ );
8130
+ }
8131
+ return { pipeline, traceFunctionKey, traceIds: traceIds2 };
8132
+ }
7969
8133
  const traceIds = cases.map(
7970
8134
  (seedCase) => registration.client.seedTrace(traceFunctionKey, {
7971
8135
  input: seedCase.input,