@bitfab/sdk 0.28.10 → 0.28.11

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.
@@ -13,7 +13,7 @@ import {
13
13
  } from "./chunk-5E4BUIYA.js";
14
14
 
15
15
  // src/version.generated.ts
16
- var __version__ = "0.28.10";
16
+ var __version__ = "0.28.11";
17
17
 
18
18
  // src/constants.ts
19
19
  var DEFAULT_SERVICE_URL = "https://bitfab.ai";
@@ -249,6 +249,50 @@ var HttpClient = class {
249
249
  async lookupFunction(name) {
250
250
  return this.request("/api/sdk/functions/lookup", { name });
251
251
  }
252
+ async getTraceSpan(traceId, lookup) {
253
+ const searchParams = new URLSearchParams();
254
+ if (lookup.id !== void 0) {
255
+ searchParams.set("id", lookup.id);
256
+ } else {
257
+ searchParams.set("name", lookup.name);
258
+ searchParams.set("occurrence", String(lookup.occurrence ?? "last"));
259
+ }
260
+ const endpoint = `/api/sdk/traces/${encodeURIComponent(traceId)}/span?${searchParams.toString()}`;
261
+ const response = await this.get(endpoint);
262
+ return response.span;
263
+ }
264
+ async get(endpoint) {
265
+ const url = `${this.serviceUrl}${endpoint}`;
266
+ const controller = new AbortController();
267
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
268
+ try {
269
+ const response = await fetch(url, {
270
+ method: "GET",
271
+ headers: { Authorization: `Bearer ${this.resolveApiKey() ?? ""}` },
272
+ signal: controller.signal
273
+ });
274
+ if (!response.ok) {
275
+ const errorText = await response.text();
276
+ throw new BitfabError(
277
+ `HTTP ${response.status}: ${errorText.slice(0, 500)}`
278
+ );
279
+ }
280
+ return await response.json();
281
+ } catch (error) {
282
+ if (error instanceof BitfabError) {
283
+ throw error;
284
+ }
285
+ if (error instanceof Error) {
286
+ if (error.name === "AbortError") {
287
+ throw new BitfabError(`Request timed out after ${this.timeout}ms`);
288
+ }
289
+ throw new BitfabError(error.message);
290
+ }
291
+ throw new BitfabError("Unknown error occurred");
292
+ } finally {
293
+ clearTimeout(timeoutId);
294
+ }
295
+ }
252
296
  /**
253
297
  * Send an internal trace (from BAML execution).
254
298
  * Fire-and-forget with awaitOnExit - doesn't block the caller.
@@ -305,12 +349,12 @@ var HttpClient = class {
305
349
  });
306
350
  }
307
351
  /**
308
- * Partial update of an existing external trace identified by sourceTraceId.
352
+ * Partial update of an existing trace identified by its Bitfab trace ID.
309
353
  * Used by the detached `client.getTrace(id)` handle. Fire-and-forget;
310
354
  * returns a tracked promise that callers may optionally await.
311
355
  */
312
- patchTrace(sourceTraceId, payload) {
313
- const endpoint = `/api/sdk/externalTraces/${encodeURIComponent(sourceTraceId)}`;
356
+ patchTrace(traceId, payload) {
357
+ const endpoint = `/api/sdk/traces/${encodeURIComponent(traceId)}`;
314
358
  return awaitOnExit(
315
359
  this.request(endpoint, payload, { method: "PATCH" })
316
360
  ).catch((error) => {
@@ -644,6 +688,7 @@ var BitfabClaudeAgentHandler = class {
644
688
  const traceId = this.ensureTrace();
645
689
  const { safe: safeInput, dropped: inputDropped } = toJsonSafeReport(inputData);
646
690
  const spanInfo = {
691
+ id: randomUuid(),
647
692
  spanId,
648
693
  traceId,
649
694
  parentId: parentId ?? null,
@@ -707,6 +752,8 @@ var BitfabClaudeAgentHandler = class {
707
752
  rawSpan.parent_id = spanInfo.parentId;
708
753
  }
709
754
  const payload = {
755
+ id: spanInfo.id,
756
+ traceId: spanInfo.traceId,
710
757
  type: "sdk-function",
711
758
  source: "typescript-sdk-claude-agent-sdk",
712
759
  traceFunctionKey: this.traceFunctionKey,
@@ -736,6 +783,7 @@ var BitfabClaudeAgentHandler = class {
736
783
  externalTrace.metadata = metadata;
737
784
  }
738
785
  const traceData = {
786
+ id: traceId,
739
787
  type: "sdk-function",
740
788
  source: "typescript-sdk-claude-agent-sdk",
741
789
  traceFunctionKey: this.traceFunctionKey,
@@ -1004,6 +1052,7 @@ var BitfabClaudeAgentHandler = class {
1004
1052
  }
1005
1053
  Object.assign(llmContext, this.currentLlmUsage);
1006
1054
  const spanInfo = {
1055
+ id: randomUuid(),
1007
1056
  spanId,
1008
1057
  traceId,
1009
1058
  parentId,
@@ -1644,6 +1693,7 @@ var BitfabLangGraphCallbackHandler = class {
1644
1693
  const contexts = Object.keys(lgMetadata).length > 0 ? [lgMetadata] : [];
1645
1694
  const { safe: safeInput, dropped: inputDropped } = toJsonSafeReport(inputData);
1646
1695
  const spanInfo = {
1696
+ id: randomUuid(),
1647
1697
  spanId: runId,
1648
1698
  traceId: invocation.traceId,
1649
1699
  rootRunId: invocation.rootRunId,
@@ -1722,6 +1772,8 @@ var BitfabLangGraphCallbackHandler = class {
1722
1772
  rawSpan.parent_id = spanInfo.parentId;
1723
1773
  }
1724
1774
  const payload = {
1775
+ id: spanInfo.id,
1776
+ traceId: spanInfo.traceId,
1725
1777
  type: "sdk-function",
1726
1778
  source: "typescript-sdk-langgraph",
1727
1779
  traceFunctionKey: this.traceFunctionKey,
@@ -1737,6 +1789,7 @@ var BitfabLangGraphCallbackHandler = class {
1737
1789
  sendTraceCompletion(rootSpan, activeContext) {
1738
1790
  const completed = activeContext === null;
1739
1791
  const traceData = {
1792
+ id: rootSpan.traceId,
1740
1793
  type: "sdk-function",
1741
1794
  source: "typescript-sdk-langgraph",
1742
1795
  traceFunctionKey: this.traceFunctionKey,
@@ -1756,6 +1809,7 @@ var BitfabLangGraphCallbackHandler = class {
1756
1809
  }
1757
1810
  sendTraceStart(rootSpan) {
1758
1811
  const traceData = {
1812
+ id: rootSpan.traceId,
1759
1813
  type: "sdk-function",
1760
1814
  source: "typescript-sdk-langgraph",
1761
1815
  traceFunctionKey: this.traceFunctionKey,
@@ -2115,6 +2169,7 @@ var BitfabOpenAITracingProcessor = class {
2115
2169
  constructor(config) {
2116
2170
  this.activeTraces = {};
2117
2171
  this.activeSpanMappings = {};
2172
+ this.canonicalTraceIds = {};
2118
2173
  this.httpClient = new HttpClient({
2119
2174
  apiKey: config.apiKey,
2120
2175
  serviceUrl: config.serviceUrl ?? DEFAULT_SERVICE_URL,
@@ -2122,6 +2177,15 @@ var BitfabOpenAITracingProcessor = class {
2122
2177
  });
2123
2178
  this.getActiveSpanContext = config.getActiveSpanContext ?? null;
2124
2179
  }
2180
+ getCanonicalTraceId(sourceTraceId) {
2181
+ const existing = this.canonicalTraceIds[sourceTraceId];
2182
+ if (existing) {
2183
+ return existing;
2184
+ }
2185
+ const created = randomUuid();
2186
+ this.canonicalTraceIds[sourceTraceId] = created;
2187
+ return created;
2188
+ }
2125
2189
  /**
2126
2190
  * Called when a trace is started.
2127
2191
  * If there's an active withSpan context, the trace ID is remapped to the
@@ -2133,7 +2197,12 @@ var BitfabOpenAITracingProcessor = class {
2133
2197
  if (activeContext) {
2134
2198
  this.activeSpanMappings[trace.traceId] = activeContext;
2135
2199
  }
2136
- this.sendTrace(trace, activeContext ? { id: activeContext.traceId } : {});
2200
+ const canonicalTraceId = activeContext?.traceId ?? this.getCanonicalTraceId(trace.traceId);
2201
+ this.canonicalTraceIds[trace.traceId] = canonicalTraceId;
2202
+ this.sendTrace(trace, {
2203
+ id: canonicalTraceId,
2204
+ sourceTraceId: activeContext?.traceId
2205
+ });
2137
2206
  }
2138
2207
  /**
2139
2208
  * Called when a trace is ended.
@@ -2142,11 +2211,13 @@ var BitfabOpenAITracingProcessor = class {
2142
2211
  */
2143
2212
  async onTraceEnd(trace) {
2144
2213
  const mapping = this.activeSpanMappings[trace.traceId];
2145
- this.sendTrace(
2146
- trace,
2147
- mapping ? { id: mapping.traceId } : { completed: true }
2148
- );
2214
+ this.sendTrace(trace, {
2215
+ completed: mapping === void 0,
2216
+ id: mapping?.traceId ?? this.getCanonicalTraceId(trace.traceId),
2217
+ sourceTraceId: mapping?.traceId
2218
+ });
2149
2219
  delete this.activeSpanMappings[trace.traceId];
2220
+ delete this.canonicalTraceIds[trace.traceId];
2150
2221
  delete this.activeTraces[trace.traceId];
2151
2222
  }
2152
2223
  /**
@@ -2176,22 +2247,25 @@ var BitfabOpenAITracingProcessor = class {
2176
2247
  async shutdown(_timeout) {
2177
2248
  this.activeTraces = {};
2178
2249
  this.activeSpanMappings = {};
2250
+ this.canonicalTraceIds = {};
2179
2251
  }
2180
2252
  /**
2181
2253
  * Send trace to Bitfab API (fire-and-forget).
2182
2254
  * When traceIdOverride is provided, the trace ID is remapped to link
2183
2255
  * the OpenAI trace into an outer withSpan trace.
2184
2256
  */
2185
- sendTrace(trace, overrides = {}) {
2257
+ sendTrace(trace, options = {}) {
2186
2258
  try {
2187
- const { completed, ...traceOverrides } = overrides;
2188
2259
  const traceData = trace.toJSON();
2189
- Object.assign(traceData, traceOverrides);
2260
+ if (options.sourceTraceId) {
2261
+ traceData.id = options.sourceTraceId;
2262
+ }
2190
2263
  this.httpClient.sendExternalTrace({
2264
+ ...options.id && { id: options.id },
2191
2265
  type: "openai",
2192
2266
  source: "typescript-sdk-openai-tracing",
2193
2267
  externalTrace: traceData,
2194
- completed: completed ?? false
2268
+ completed: options.completed ?? false
2195
2269
  });
2196
2270
  } catch {
2197
2271
  }
@@ -2277,6 +2351,7 @@ var BitfabOpenAITracingProcessor = class {
2277
2351
  */
2278
2352
  buildSpanPayload(serializedSpan, errors) {
2279
2353
  const payload = {
2354
+ id: randomUuid(),
2280
2355
  type: "openai",
2281
2356
  source: "typescript-sdk-openai-tracing",
2282
2357
  sourceTraceId: serializedSpan.trace_id ?? "unknown",
@@ -2299,6 +2374,10 @@ var BitfabOpenAITracingProcessor = class {
2299
2374
  this.extractSpanInputResponse(span, serializedSpan, errors);
2300
2375
  this.applySpanOverrides(serializedSpan, span.traceId ?? "");
2301
2376
  const payload = this.buildSpanPayload(serializedSpan, errors);
2377
+ const canonicalTraceId = span.traceId ? this.getCanonicalTraceId(span.traceId) : void 0;
2378
+ if (canonicalTraceId) {
2379
+ payload.traceId = canonicalTraceId;
2380
+ }
2302
2381
  this.httpClient.sendExternalSpan(payload);
2303
2382
  }
2304
2383
  };
@@ -2614,24 +2693,19 @@ function extractContextFromCollector(collector) {
2614
2693
  return null;
2615
2694
  }
2616
2695
  }
2617
- var TRACE_ID_PATTERN = /^[a-zA-Z0-9_\-.:]+$/;
2618
- var TRACE_ID_MAX_LENGTH = 256;
2696
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
2619
2697
  function validateTraceId(traceId) {
2620
- if (typeof traceId !== "string" || traceId.length === 0) {
2621
- throw new BitfabError("traceId is required and must be a non-empty string");
2698
+ if (typeof traceId !== "string" || !UUID_PATTERN.test(traceId)) {
2699
+ throw new BitfabError("traceId must be a valid Bitfab trace ID");
2622
2700
  }
2623
- if (traceId.length > TRACE_ID_MAX_LENGTH) {
2624
- throw new BitfabError(
2625
- `traceId must be ${TRACE_ID_MAX_LENGTH} characters or fewer`
2626
- );
2627
- }
2628
- if (!TRACE_ID_PATTERN.test(traceId)) {
2629
- throw new BitfabError(
2630
- `traceId may only contain letters, digits, "_", "-", ".", ":"`
2631
- );
2701
+ }
2702
+ function validateSpanId(id) {
2703
+ if (typeof id !== "string" || !UUID_PATTERN.test(id)) {
2704
+ throw new BitfabError("id must be a valid Bitfab span ID");
2632
2705
  }
2633
2706
  }
2634
2707
  var noOpSpan = {
2708
+ id: "",
2635
2709
  traceId: "",
2636
2710
  addContext() {
2637
2711
  },
@@ -2655,6 +2729,7 @@ function getCurrentSpan() {
2655
2729
  return noOpSpan;
2656
2730
  }
2657
2731
  return {
2732
+ id: current.spanId,
2658
2733
  traceId: current.traceId,
2659
2734
  addContext(context) {
2660
2735
  try {
@@ -3451,20 +3526,19 @@ var Bitfab = class {
3451
3526
  }
3452
3527
  /**
3453
3528
  * Get a detached handle to a previously-created trace, looked up by the
3454
- * caller-supplied id (the same id passed at trace creation).
3529
+ * canonical Bitfab trace ID.
3455
3530
  *
3456
3531
  * The returned handle is not tied to AsyncLocalStorage - each method sends
3457
3532
  * to the server immediately. Useful for adding context to a trace from a
3458
3533
  * different process or thread than the one that created it.
3459
3534
  *
3460
- * Throws synchronously if `traceId` is malformed (empty, too long, or
3461
- * contains characters outside `[a-zA-Z0-9_\-.:]`). Server returns 404 if
3462
- * no trace exists with that id in the org; the failure surfaces as a
3535
+ * Throws synchronously if `traceId` is not a valid Bitfab trace ID. The
3536
+ * server returns 404 if no trace exists with that ID in the org; the failure surfaces as a
3463
3537
  * logged warning (fire-and-forget) or via the awaited promise.
3464
3538
  *
3465
3539
  * Example:
3466
3540
  * ```typescript
3467
- * const trace = client.getTrace("order_abc_123");
3541
+ * const trace = client.getTrace(traceId);
3468
3542
  * await trace.addContext({ refund_status: "approved" });
3469
3543
  * await trace.setMetadata({ region: "us-west" });
3470
3544
  * ```
@@ -3504,6 +3578,33 @@ var Bitfab = class {
3504
3578
  }
3505
3579
  };
3506
3580
  }
3581
+ /**
3582
+ * Fetch one persisted span from a trace without loading the full trace.
3583
+ * Name lookups return the last matching span by default. Pass `occurrence`
3584
+ * as `"first"` or a zero-based index to select a different match.
3585
+ */
3586
+ async getTraceSpan(traceId, lookup) {
3587
+ validateTraceId(traceId);
3588
+ const hasId = lookup.id !== void 0;
3589
+ const hasName = lookup.name !== void 0;
3590
+ if (hasId === hasName) {
3591
+ throw new BitfabError("Provide exactly one of id or name");
3592
+ }
3593
+ if (hasId) {
3594
+ validateSpanId(lookup.id);
3595
+ } else {
3596
+ if (lookup.name.length === 0) {
3597
+ throw new BitfabError("name must be a non-empty string");
3598
+ }
3599
+ const occurrence = lookup.occurrence ?? "last";
3600
+ if (occurrence !== "first" && occurrence !== "last" && (!Number.isInteger(occurrence) || occurrence < 0)) {
3601
+ throw new BitfabError(
3602
+ 'occurrence must be "first", "last", or a non-negative integer'
3603
+ );
3604
+ }
3605
+ }
3606
+ return this.httpClient.getTraceSpan(traceId, lookup);
3607
+ }
3507
3608
  /**
3508
3609
  * Get a function wrapper for a specific trace function key.
3509
3610
  *
@@ -3562,6 +3663,7 @@ var Bitfab = class {
3562
3663
  };
3563
3664
  }
3564
3665
  return this.httpClient.sendExternalTrace({
3666
+ id: params.traceId,
3565
3667
  type: "sdk-function",
3566
3668
  source: "typescript-sdk-function",
3567
3669
  traceFunctionKey: params.traceFunctionKey,
@@ -3617,6 +3719,8 @@ var Bitfab = class {
3617
3719
  externalSpan.input_source_span_id = params.inputSourceSpanId;
3618
3720
  }
3619
3721
  return this.httpClient.sendExternalSpan({
3722
+ id: params.spanId,
3723
+ traceId: params.traceId,
3620
3724
  type: "sdk-function",
3621
3725
  source: "typescript-sdk-function",
3622
3726
  sourceTraceId: params.traceId,
@@ -3885,4 +3989,4 @@ export {
3885
3989
  BitfabFunction,
3886
3990
  finalizers
3887
3991
  };
3888
- //# sourceMappingURL=chunk-SK4TNXRC.js.map
3992
+ //# sourceMappingURL=chunk-ZBWTCBVQ.js.map