@bitfab/sdk 0.26.1 → 0.27.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/index.cjs CHANGED
@@ -64,35 +64,6 @@ var init_warnOnce = __esm({
64
64
  }
65
65
  });
66
66
 
67
- // src/randomUuid.ts
68
- function randomUuid() {
69
- const globalCrypto = globalThis.crypto;
70
- if (typeof globalCrypto?.randomUUID === "function") {
71
- try {
72
- return globalCrypto.randomUUID();
73
- } catch {
74
- }
75
- }
76
- warnOnce(
77
- "crypto-unavailable",
78
- "global crypto.randomUUID is unavailable; using a non-cryptographic fallback for trace/span ids. Tracing works normally (ids are correlation-only, not security-sensitive)."
79
- );
80
- return fallbackUuidV4();
81
- }
82
- function fallbackUuidV4() {
83
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
84
- const rand = Math.random() * 16 | 0;
85
- const value = char === "x" ? rand : rand & 3 | 8;
86
- return value.toString(16);
87
- });
88
- }
89
- var init_randomUuid = __esm({
90
- "src/randomUuid.ts"() {
91
- "use strict";
92
- init_warnOnce();
93
- }
94
- });
95
-
96
67
  // src/serialize.ts
97
68
  function describeValue(value) {
98
69
  try {
@@ -148,7 +119,11 @@ function deserializeValue(serialized) {
148
119
  });
149
120
  }
150
121
  function toJsonSafe(value) {
151
- const safe = toJsonSafeInner(value, 0, /* @__PURE__ */ new WeakSet());
122
+ return toJsonSafeReport(value).safe;
123
+ }
124
+ function toJsonSafeReport(value) {
125
+ const dropped = [];
126
+ const safe = toJsonSafeInner(value, 0, /* @__PURE__ */ new WeakSet(), dropped);
152
127
  try {
153
128
  const size = JSON.stringify(safe)?.length ?? 0;
154
129
  if (size > MAX_FRAMEWORK_SERIALIZED_BYTES) {
@@ -156,13 +131,16 @@ function toJsonSafe(value) {
156
131
  "toJsonSafe:too_large",
157
132
  `a framework payload exceeded ${MAX_FRAMEWORK_SERIALIZED_BYTES} bytes and was replaced with a placeholder so the span still ships. The captured state for this span is incomplete.`
158
133
  );
159
- return `<unserializable: too_large_${size}_bytes>`;
134
+ return {
135
+ safe: `<unserializable: too_large_${size}_bytes>`,
136
+ dropped: [...dropped, `too_large_${size}_bytes`]
137
+ };
160
138
  }
161
139
  } catch {
162
140
  }
163
- return safe;
141
+ return { safe, dropped };
164
142
  }
165
- function toJsonSafeInner(value, depth, seen) {
143
+ function toJsonSafeInner(value, depth, seen, dropped) {
166
144
  if (value === null || value === void 0) {
167
145
  return value;
168
146
  }
@@ -171,30 +149,40 @@ function toJsonSafeInner(value, depth, seen) {
171
149
  }
172
150
  const className = value?.constructor?.name ?? typeof value;
173
151
  if (depth > MAX_SAFE_DEPTH) {
152
+ dropped.push(className);
174
153
  return `<${className}>`;
175
154
  }
176
155
  if (typeof value !== "object") {
156
+ if (typeof value === "function" || typeof value === "symbol") {
157
+ dropped.push(className);
158
+ }
177
159
  try {
178
160
  return String(value);
179
161
  } catch {
162
+ dropped.push(className);
180
163
  return `<${className}>`;
181
164
  }
182
165
  }
183
166
  if (seen.has(value)) {
167
+ dropped.push(className);
184
168
  return `<cycle ${className}>`;
185
169
  }
186
170
  seen.add(value);
187
171
  let result;
188
172
  if (Array.isArray(value)) {
189
- result = value.map((item) => toJsonSafeInner(item, depth + 1, seen));
173
+ result = value.map(
174
+ (item) => toJsonSafeInner(item, depth + 1, seen, dropped)
175
+ );
190
176
  } else if (typeof value.toJSON === "function") {
191
177
  try {
192
178
  result = toJsonSafeInner(
193
179
  value.toJSON(),
194
180
  depth + 1,
195
- seen
181
+ seen,
182
+ dropped
196
183
  );
197
184
  } catch {
185
+ dropped.push(className);
198
186
  result = `<${className}>`;
199
187
  }
200
188
  } else {
@@ -202,11 +190,12 @@ function toJsonSafeInner(value, depth, seen) {
202
190
  const obj = {};
203
191
  for (const [k, v] of Object.entries(value)) {
204
192
  if (!k.startsWith("_")) {
205
- obj[k] = toJsonSafeInner(v, depth + 1, seen);
193
+ obj[k] = toJsonSafeInner(v, depth + 1, seen, dropped);
206
194
  }
207
195
  }
208
196
  result = obj;
209
197
  } catch {
198
+ dropped.push(className);
210
199
  result = `<${className}>`;
211
200
  }
212
201
  }
@@ -225,6 +214,35 @@ var init_serialize = __esm({
225
214
  }
226
215
  });
227
216
 
217
+ // src/randomUuid.ts
218
+ function randomUuid() {
219
+ const globalCrypto = globalThis.crypto;
220
+ if (typeof globalCrypto?.randomUUID === "function") {
221
+ try {
222
+ return globalCrypto.randomUUID();
223
+ } catch {
224
+ }
225
+ }
226
+ warnOnce(
227
+ "crypto-unavailable",
228
+ "global crypto.randomUUID is unavailable; using a non-cryptographic fallback for trace/span ids. Tracing works normally (ids are correlation-only, not security-sensitive)."
229
+ );
230
+ return fallbackUuidV4();
231
+ }
232
+ function fallbackUuidV4() {
233
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
234
+ const rand = Math.random() * 16 | 0;
235
+ const value = char === "x" ? rand : rand & 3 | 8;
236
+ return value.toString(16);
237
+ });
238
+ }
239
+ var init_randomUuid = __esm({
240
+ "src/randomUuid.ts"() {
241
+ "use strict";
242
+ init_warnOnce();
243
+ }
244
+ });
245
+
228
246
  // src/asyncStorage.ts
229
247
  function registerAsyncLocalStorageClass(cls) {
230
248
  if (!AsyncLocalStorageClass) {
@@ -289,8 +307,21 @@ var init_replayContext = __esm({
289
307
  // src/replay.ts
290
308
  var replay_exports = {};
291
309
  __export(replay_exports, {
292
- replay: () => replay
310
+ BITFAB_PROGRESS_PREFIX: () => BITFAB_PROGRESS_PREFIX,
311
+ replay: () => replay,
312
+ reportReplayProgress: () => reportReplayProgress
293
313
  });
314
+ function reportReplayProgress(progress) {
315
+ const stderr = typeof process !== "undefined" ? process.stderr : void 0;
316
+ if (!stderr) {
317
+ return;
318
+ }
319
+ try {
320
+ stderr.write(`${BITFAB_PROGRESS_PREFIX}${JSON.stringify(progress)}
321
+ `);
322
+ } catch {
323
+ }
324
+ }
294
325
  function deserializeInputs(spanData) {
295
326
  const inputMeta = spanData.input_meta;
296
327
  const rawInput = spanData.input;
@@ -488,7 +519,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
488
519
  const resultItems = await mapWithConcurrency(
489
520
  tasks,
490
521
  maxConcurrency,
491
- options?.onProgress ? (item) => {
522
+ options?.onProgress ? (item, index) => {
492
523
  completed += 1;
493
524
  if (item.error === null) {
494
525
  succeeded += 1;
@@ -496,7 +527,20 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
496
527
  errored += 1;
497
528
  }
498
529
  try {
499
- options?.onProgress?.({ completed, total, succeeded, errored });
530
+ options?.onProgress?.({
531
+ completed,
532
+ total,
533
+ succeeded,
534
+ errored,
535
+ item: {
536
+ // Source (historical) trace id, so a UI can identify the trace
537
+ // that just settled. The item's own traceId is the new replay
538
+ // trace and is assigned later (below), so use the server item.
539
+ traceId: serverItems[index]?.traceId ?? null,
540
+ error: item.error,
541
+ durationMs: item.durationMs
542
+ }
543
+ });
500
544
  } catch {
501
545
  }
502
546
  } : void 0
@@ -553,6 +597,7 @@ async function replay(httpClient, serviceUrl, traceFunctionKey, fn, options) {
553
597
  testRunUrl: `${serviceUrl}${testRunUrl}`
554
598
  };
555
599
  }
600
+ var BITFAB_PROGRESS_PREFIX;
556
601
  var init_replay = __esm({
557
602
  "src/replay.ts"() {
558
603
  "use strict";
@@ -560,12 +605,14 @@ var init_replay = __esm({
560
605
  init_randomUuid();
561
606
  init_replayContext();
562
607
  init_serialize();
608
+ BITFAB_PROGRESS_PREFIX = "@@bitfab:progress ";
563
609
  }
564
610
  });
565
611
 
566
612
  // src/index.ts
567
613
  var index_exports = {};
568
614
  __export(index_exports, {
615
+ BITFAB_PROGRESS_PREFIX: () => BITFAB_PROGRESS_PREFIX,
569
616
  Bitfab: () => Bitfab,
570
617
  BitfabClaudeAgentHandler: () => BitfabClaudeAgentHandler,
571
618
  BitfabError: () => BitfabError,
@@ -582,12 +629,13 @@ __export(index_exports, {
582
629
  finalizers: () => finalizers,
583
630
  flushTraces: () => flushTraces,
584
631
  getCurrentSpan: () => getCurrentSpan,
585
- getCurrentTrace: () => getCurrentTrace
632
+ getCurrentTrace: () => getCurrentTrace,
633
+ reportReplayProgress: () => reportReplayProgress
586
634
  });
587
635
  module.exports = __toCommonJS(index_exports);
588
636
 
589
637
  // src/version.generated.ts
590
- var __version__ = "0.26.1";
638
+ var __version__ = "0.27.0";
591
639
 
592
640
  // src/constants.ts
593
641
  var DEFAULT_SERVICE_URL = "https://bitfab.ai";
@@ -1029,6 +1077,62 @@ var HttpClient = class {
1029
1077
  }
1030
1078
  };
1031
1079
 
1080
+ // src/processorPayload.ts
1081
+ init_serialize();
1082
+ init_warnOnce();
1083
+ var SERIALIZATION_DEGRADED_STEP = "serialization_degraded";
1084
+ function degradedError(dropped) {
1085
+ const names = [...new Set(dropped)].sort().join(", ");
1086
+ return {
1087
+ source: "sdk",
1088
+ step: SERIALIZATION_DEGRADED_STEP,
1089
+ error: `non-replayable: could not faithfully capture ${names}`
1090
+ };
1091
+ }
1092
+ var ENVELOPE_FIELDS = [
1093
+ "type",
1094
+ "source",
1095
+ "traceFunctionKey",
1096
+ "sourceTraceId",
1097
+ "completed"
1098
+ ];
1099
+ function rebuildEnvelope(payload, bodyKey, placeholder) {
1100
+ const rebuilt = {};
1101
+ for (const k of ENVELOPE_FIELDS) {
1102
+ if (k in payload) {
1103
+ rebuilt[k] = payload[k];
1104
+ }
1105
+ }
1106
+ rebuilt[bodyKey] = { serialized: placeholder };
1107
+ return rebuilt;
1108
+ }
1109
+ function finalizeSpanPayload(payload, extraDropped) {
1110
+ const { safe, dropped } = toJsonSafeReport(payload);
1111
+ const allDropped = [...extraDropped ?? [], ...dropped];
1112
+ const collapsed = safe === null || typeof safe !== "object" || Array.isArray(safe);
1113
+ const result = collapsed ? rebuildEnvelope(payload, "rawSpan", safe) : safe;
1114
+ if (allDropped.length > 0) {
1115
+ const existing = result.errors;
1116
+ const errors = Array.isArray(existing) ? existing : [];
1117
+ errors.push(degradedError(allDropped));
1118
+ result.errors = errors;
1119
+ }
1120
+ return result;
1121
+ }
1122
+ function finalizeTracePayload(payload) {
1123
+ const { safe, dropped } = toJsonSafeReport(payload);
1124
+ const collapsed = safe === null || typeof safe !== "object" || Array.isArray(safe);
1125
+ const result = collapsed ? rebuildEnvelope(payload, "externalTrace", safe) : safe;
1126
+ if (dropped.length > 0 || collapsed) {
1127
+ const names = dropped.length > 0 ? [...new Set(dropped)].sort().join(", ") : "trace";
1128
+ warnOnce(
1129
+ `finalizeTrace:${names.replace(/\d+/g, "N")}`,
1130
+ `a trace held non-serializable value(s) (${names}); they were captured as placeholders, so the trace may not be replayable.`
1131
+ );
1132
+ }
1133
+ return result;
1134
+ }
1135
+
1032
1136
  // src/claudeAgentSdk.ts
1033
1137
  init_randomUuid();
1034
1138
  init_serialize();
@@ -1158,6 +1262,7 @@ var BitfabClaudeAgentHandler = class {
1158
1262
  // ── span helpers ─────────────────────────────────────────────
1159
1263
  startSpan(spanId, name, spanType, inputData, parentId) {
1160
1264
  const traceId = this.ensureTrace();
1265
+ const { safe: safeInput, dropped: inputDropped } = toJsonSafeReport(inputData);
1161
1266
  const spanInfo = {
1162
1267
  spanId,
1163
1268
  traceId,
@@ -1165,9 +1270,12 @@ var BitfabClaudeAgentHandler = class {
1165
1270
  startedAt: nowIso(),
1166
1271
  name,
1167
1272
  type: spanType,
1168
- input: safeSerialize(inputData),
1273
+ input: safeInput,
1169
1274
  contexts: []
1170
1275
  };
1276
+ if (inputDropped.length > 0) {
1277
+ spanInfo.dropped = [...inputDropped];
1278
+ }
1171
1279
  this.runToSpan.set(spanId, spanInfo);
1172
1280
  return spanInfo;
1173
1281
  }
@@ -1178,7 +1286,11 @@ var BitfabClaudeAgentHandler = class {
1178
1286
  }
1179
1287
  this.runToSpan.delete(spanId);
1180
1288
  spanInfo.endedAt = nowIso();
1181
- spanInfo.output = safeSerialize(output);
1289
+ const { safe: safeOutput, dropped: outputDropped } = toJsonSafeReport(output);
1290
+ spanInfo.output = safeOutput;
1291
+ if (outputDropped.length > 0) {
1292
+ spanInfo.dropped = [...spanInfo.dropped ?? [], ...outputDropped];
1293
+ }
1182
1294
  if (error !== void 0) {
1183
1295
  spanInfo.error = error;
1184
1296
  }
@@ -1221,8 +1333,9 @@ var BitfabClaudeAgentHandler = class {
1221
1333
  sourceTraceId: spanInfo.traceId,
1222
1334
  rawSpan
1223
1335
  };
1336
+ const finalized = finalizeSpanPayload(payload, spanInfo.dropped);
1224
1337
  try {
1225
- this.httpClient.sendExternalSpan(payload);
1338
+ this.httpClient.sendExternalSpan(finalized);
1226
1339
  } catch {
1227
1340
  }
1228
1341
  }
@@ -1249,8 +1362,9 @@ var BitfabClaudeAgentHandler = class {
1249
1362
  externalTrace,
1250
1363
  completed
1251
1364
  };
1365
+ const finalized = finalizeTracePayload(traceData);
1252
1366
  try {
1253
- this.httpClient.sendExternalTrace(traceData);
1367
+ this.httpClient.sendExternalTrace(finalized);
1254
1368
  } catch {
1255
1369
  }
1256
1370
  }
@@ -1888,7 +2002,6 @@ var LANGGRAPH_METADATA_KEYS = [
1888
2002
  function nowIso2() {
1889
2003
  return (/* @__PURE__ */ new Date()).toISOString();
1890
2004
  }
1891
- var safeSerialize2 = toJsonSafe;
1892
2005
  function convertMessage(message) {
1893
2006
  if (typeof message !== "object" || message === null) {
1894
2007
  return { role: "unknown", content: String(message) };
@@ -2133,6 +2246,7 @@ var BitfabLangGraphCallbackHandler = class {
2133
2246
  }
2134
2247
  const lgMetadata = extractLangGraphMetadata(metadata);
2135
2248
  const contexts = Object.keys(lgMetadata).length > 0 ? [lgMetadata] : [];
2249
+ const { safe: safeInput, dropped: inputDropped } = toJsonSafeReport(inputData);
2136
2250
  const spanInfo = {
2137
2251
  spanId: runId,
2138
2252
  traceId: invocation.traceId,
@@ -2141,9 +2255,12 @@ var BitfabLangGraphCallbackHandler = class {
2141
2255
  startedAt: nowIso2(),
2142
2256
  name,
2143
2257
  type: spanType,
2144
- input: safeSerialize2(inputData),
2258
+ input: safeInput,
2145
2259
  contexts
2146
2260
  };
2261
+ if (inputDropped.length > 0) {
2262
+ spanInfo.dropped = [...inputDropped];
2263
+ }
2147
2264
  if (willHide) {
2148
2265
  spanInfo.hidden = true;
2149
2266
  }
@@ -2157,7 +2274,11 @@ var BitfabLangGraphCallbackHandler = class {
2157
2274
  }
2158
2275
  this.runToSpan.delete(runId);
2159
2276
  spanInfo.endedAt = nowIso2();
2160
- spanInfo.output = safeSerialize2(output);
2277
+ const { safe: safeOutput, dropped: outputDropped } = toJsonSafeReport(output);
2278
+ spanInfo.output = safeOutput;
2279
+ if (outputDropped.length > 0) {
2280
+ spanInfo.dropped = [...spanInfo.dropped ?? [], ...outputDropped];
2281
+ }
2161
2282
  if (error !== void 0) {
2162
2283
  spanInfo.error = error;
2163
2284
  }
@@ -2208,8 +2329,9 @@ var BitfabLangGraphCallbackHandler = class {
2208
2329
  sourceTraceId: spanInfo.traceId,
2209
2330
  rawSpan
2210
2331
  };
2332
+ const finalized = finalizeSpanPayload(payload, spanInfo.dropped);
2211
2333
  try {
2212
- this.httpClient.sendExternalSpan(payload);
2334
+ this.httpClient.sendExternalSpan(finalized);
2213
2335
  } catch {
2214
2336
  }
2215
2337
  }
@@ -2227,8 +2349,9 @@ var BitfabLangGraphCallbackHandler = class {
2227
2349
  },
2228
2350
  completed
2229
2351
  };
2352
+ const finalized = finalizeTracePayload(traceData);
2230
2353
  try {
2231
- this.httpClient.sendExternalTrace(traceData);
2354
+ this.httpClient.sendExternalTrace(finalized);
2232
2355
  } catch {
2233
2356
  }
2234
2357
  }
@@ -2743,7 +2866,7 @@ var BitfabOpenAITracingProcessor = class {
2743
2866
  if (errors.length > 0) {
2744
2867
  payload.errors = errors;
2745
2868
  }
2746
- return payload;
2869
+ return finalizeSpanPayload(payload);
2747
2870
  }
2748
2871
  /**
2749
2872
  * Send span to Bitfab API (fire-and-forget).
@@ -4253,8 +4376,12 @@ var finalizers = {
4253
4376
  aiSdk,
4254
4377
  readableStream
4255
4378
  };
4379
+
4380
+ // src/index.ts
4381
+ init_replay();
4256
4382
  // Annotate the CommonJS export names for ESM import in node:
4257
4383
  0 && (module.exports = {
4384
+ BITFAB_PROGRESS_PREFIX,
4258
4385
  Bitfab,
4259
4386
  BitfabClaudeAgentHandler,
4260
4387
  BitfabError,
@@ -4271,6 +4398,7 @@ var finalizers = {
4271
4398
  finalizers,
4272
4399
  flushTraces,
4273
4400
  getCurrentSpan,
4274
- getCurrentTrace
4401
+ getCurrentTrace,
4402
+ reportReplayProgress
4275
4403
  });
4276
4404
  //# sourceMappingURL=index.cjs.map