@bitfab/sdk 0.28.9 → 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.
package/dist/index.d.cts CHANGED
@@ -96,6 +96,32 @@ declare function flushTraces(timeoutMs?: number): Promise<void>;
96
96
  * case) is still picked up.
97
97
  */
98
98
  type ApiKeyInput = string | (() => string | undefined);
99
+ type SpanOccurrence = "first" | "last" | number;
100
+ type SpanLookup = {
101
+ id: string;
102
+ name?: never;
103
+ occurrence?: never;
104
+ } | {
105
+ name: string;
106
+ id?: never;
107
+ occurrence?: SpanOccurrence;
108
+ };
109
+ interface CapturedSpan {
110
+ id: string;
111
+ traceId: string;
112
+ parentSpanId: string | null;
113
+ name: string | null;
114
+ type: string;
115
+ input: unknown;
116
+ output: unknown;
117
+ contexts: Record<string, unknown>[];
118
+ prompt: string | null;
119
+ metadata: Record<string, unknown>;
120
+ metrics: Record<string, unknown> | null;
121
+ errors: unknown;
122
+ startedAt: string | null;
123
+ endedAt: string | null;
124
+ }
99
125
  interface TokenUsage {
100
126
  input: number | null;
101
127
  output: number | null;
@@ -861,6 +887,8 @@ declare class BitfabOpenAITracingProcessor implements TracingProcessor {
861
887
  private activeTraces;
862
888
  private readonly getActiveSpanContext;
863
889
  private activeSpanMappings;
890
+ private canonicalTraceIds;
891
+ private getCanonicalTraceId;
864
892
  /**
865
893
  * Initialize the tracing processor.
866
894
  *
@@ -951,6 +979,8 @@ interface WrappedBamlFn<TArgs extends unknown[], TReturn> {
951
979
  * A handle to the current active span, allowing context to be added.
952
980
  */
953
981
  interface CurrentSpan {
982
+ /** The Bitfab ID for the current span. */
983
+ readonly id: string;
954
984
  /** The trace ID for the current span. */
955
985
  readonly traceId: string;
956
986
  /**
@@ -966,7 +996,7 @@ interface CurrentSpan {
966
996
  }
967
997
  /**
968
998
  * A detached handle to a previously-created trace, looked up by its
969
- * caller-supplied id (the same id passed when the trace was started).
999
+ * canonical Bitfab trace ID.
970
1000
  *
971
1001
  * Unlike `getCurrentTrace()`, this handle is not tied to AsyncLocalStorage -
972
1002
  * each method sends to the server immediately. Useful for adding context
@@ -974,7 +1004,7 @@ interface CurrentSpan {
974
1004
  * agent that wants to annotate the original conversation's trace).
975
1005
  */
976
1006
  interface DetachedTrace {
977
- /** The caller-supplied trace id this handle resolves. */
1007
+ /** The canonical Bitfab trace ID this handle resolves. */
978
1008
  readonly traceId: string;
979
1009
  /**
980
1010
  * Append a context entry to this trace. Each call adds one entry to the
@@ -1394,25 +1424,30 @@ declare class Bitfab {
1394
1424
  withSpan<TArgs extends unknown[], TReturn>(traceFunctionKey: string, optionsOrFn: SpanOptions | ((...args: TArgs) => TReturn), maybeFn?: (...args: TArgs) => TReturn): (...args: TArgs) => TReturn;
1395
1425
  /**
1396
1426
  * Get a detached handle to a previously-created trace, looked up by the
1397
- * caller-supplied id (the same id passed at trace creation).
1427
+ * canonical Bitfab trace ID.
1398
1428
  *
1399
1429
  * The returned handle is not tied to AsyncLocalStorage - each method sends
1400
1430
  * to the server immediately. Useful for adding context to a trace from a
1401
1431
  * different process or thread than the one that created it.
1402
1432
  *
1403
- * Throws synchronously if `traceId` is malformed (empty, too long, or
1404
- * contains characters outside `[a-zA-Z0-9_\-.:]`). Server returns 404 if
1405
- * no trace exists with that id in the org; the failure surfaces as a
1433
+ * Throws synchronously if `traceId` is not a valid Bitfab trace ID. The
1434
+ * server returns 404 if no trace exists with that ID in the org; the failure surfaces as a
1406
1435
  * logged warning (fire-and-forget) or via the awaited promise.
1407
1436
  *
1408
1437
  * Example:
1409
1438
  * ```typescript
1410
- * const trace = client.getTrace("order_abc_123");
1439
+ * const trace = client.getTrace(traceId);
1411
1440
  * await trace.addContext({ refund_status: "approved" });
1412
1441
  * await trace.setMetadata({ region: "us-west" });
1413
1442
  * ```
1414
1443
  */
1415
1444
  getTrace(traceId: string): DetachedTrace;
1445
+ /**
1446
+ * Fetch one persisted span from a trace without loading the full trace.
1447
+ * Name lookups return the last matching span by default. Pass `occurrence`
1448
+ * as `"first"` or a zero-based index to select a different match.
1449
+ */
1450
+ getTraceSpan(traceId: string, lookup: SpanLookup): Promise<CapturedSpan | null>;
1416
1451
  /**
1417
1452
  * Get a function wrapper for a specific trace function key.
1418
1453
  *
@@ -1614,7 +1649,7 @@ declare class BitfabFunction {
1614
1649
  /**
1615
1650
  * SDK version from package.json (injected at build time)
1616
1651
  */
1617
- declare const __version__ = "0.28.9";
1652
+ declare const __version__ = "0.28.11";
1618
1653
 
1619
1654
  /**
1620
1655
  * Constants for the Bitfab SDK.
@@ -1682,4 +1717,4 @@ declare const finalizers: {
1682
1717
  readableStream: typeof readableStream;
1683
1718
  };
1684
1719
 
1685
- export { type ActiveSpanContext, type AdaptContext, type AdaptInputsFn, type AllowedEnvVars, BITFAB_PROGRESS_PREFIX, type BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, type BitfabConfig, BitfabError, BitfabFunction, BitfabLangGraphCallbackHandler as BitfabLangChainCallbackHandler, BitfabLangGraphCallbackHandler, type BitfabLanguageModelMiddleware, BitfabOpenAIAgentHandler, BitfabOpenAITracingProcessor, BitfabVercelAiHandler, type CodeChangeFile, type CurrentSpan, type CurrentTrace, DEFAULT_SERVICE_URL, type DbSnapshotConfig, type DbSnapshotProvider, type DbSnapshotRef, type DetachedTrace, type MockStrategy, type ProviderDefinition, ReplayEnvironment, type ReplayEnvironmentSnapshot, type ReplayItem, type ReplayOptions, type ReplayProgress, type ReplayResult, SUPPORTED_PROVIDERS, type SpanOptions, type SpanType, type TokenUsage, type TraceResponse, type TracingProcessor, type VercelCallParams, type VercelGenerateResult, type VercelStreamResult, type WrapBAMLOptions, type WrappedBamlFn, __version__, finalizers, flushTraces, getCurrentSpan, getCurrentTrace, reportReplayProgress };
1720
+ export { type ActiveSpanContext, type AdaptContext, type AdaptInputsFn, type AllowedEnvVars, BITFAB_PROGRESS_PREFIX, type BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, type BitfabConfig, BitfabError, BitfabFunction, BitfabLangGraphCallbackHandler as BitfabLangChainCallbackHandler, BitfabLangGraphCallbackHandler, type BitfabLanguageModelMiddleware, BitfabOpenAIAgentHandler, BitfabOpenAITracingProcessor, BitfabVercelAiHandler, type CapturedSpan, type CodeChangeFile, type CurrentSpan, type CurrentTrace, DEFAULT_SERVICE_URL, type DbSnapshotConfig, type DbSnapshotProvider, type DbSnapshotRef, type DetachedTrace, type MockStrategy, type ProviderDefinition, ReplayEnvironment, type ReplayEnvironmentSnapshot, type ReplayItem, type ReplayOptions, type ReplayProgress, type ReplayResult, SUPPORTED_PROVIDERS, type SpanLookup, type SpanOccurrence, type SpanOptions, type SpanType, type TokenUsage, type TraceResponse, type TracingProcessor, type VercelCallParams, type VercelGenerateResult, type VercelStreamResult, type WrapBAMLOptions, type WrappedBamlFn, __version__, finalizers, flushTraces, getCurrentSpan, getCurrentTrace, reportReplayProgress };
package/dist/index.d.ts CHANGED
@@ -96,6 +96,32 @@ declare function flushTraces(timeoutMs?: number): Promise<void>;
96
96
  * case) is still picked up.
97
97
  */
98
98
  type ApiKeyInput = string | (() => string | undefined);
99
+ type SpanOccurrence = "first" | "last" | number;
100
+ type SpanLookup = {
101
+ id: string;
102
+ name?: never;
103
+ occurrence?: never;
104
+ } | {
105
+ name: string;
106
+ id?: never;
107
+ occurrence?: SpanOccurrence;
108
+ };
109
+ interface CapturedSpan {
110
+ id: string;
111
+ traceId: string;
112
+ parentSpanId: string | null;
113
+ name: string | null;
114
+ type: string;
115
+ input: unknown;
116
+ output: unknown;
117
+ contexts: Record<string, unknown>[];
118
+ prompt: string | null;
119
+ metadata: Record<string, unknown>;
120
+ metrics: Record<string, unknown> | null;
121
+ errors: unknown;
122
+ startedAt: string | null;
123
+ endedAt: string | null;
124
+ }
99
125
  interface TokenUsage {
100
126
  input: number | null;
101
127
  output: number | null;
@@ -861,6 +887,8 @@ declare class BitfabOpenAITracingProcessor implements TracingProcessor {
861
887
  private activeTraces;
862
888
  private readonly getActiveSpanContext;
863
889
  private activeSpanMappings;
890
+ private canonicalTraceIds;
891
+ private getCanonicalTraceId;
864
892
  /**
865
893
  * Initialize the tracing processor.
866
894
  *
@@ -951,6 +979,8 @@ interface WrappedBamlFn<TArgs extends unknown[], TReturn> {
951
979
  * A handle to the current active span, allowing context to be added.
952
980
  */
953
981
  interface CurrentSpan {
982
+ /** The Bitfab ID for the current span. */
983
+ readonly id: string;
954
984
  /** The trace ID for the current span. */
955
985
  readonly traceId: string;
956
986
  /**
@@ -966,7 +996,7 @@ interface CurrentSpan {
966
996
  }
967
997
  /**
968
998
  * A detached handle to a previously-created trace, looked up by its
969
- * caller-supplied id (the same id passed when the trace was started).
999
+ * canonical Bitfab trace ID.
970
1000
  *
971
1001
  * Unlike `getCurrentTrace()`, this handle is not tied to AsyncLocalStorage -
972
1002
  * each method sends to the server immediately. Useful for adding context
@@ -974,7 +1004,7 @@ interface CurrentSpan {
974
1004
  * agent that wants to annotate the original conversation's trace).
975
1005
  */
976
1006
  interface DetachedTrace {
977
- /** The caller-supplied trace id this handle resolves. */
1007
+ /** The canonical Bitfab trace ID this handle resolves. */
978
1008
  readonly traceId: string;
979
1009
  /**
980
1010
  * Append a context entry to this trace. Each call adds one entry to the
@@ -1394,25 +1424,30 @@ declare class Bitfab {
1394
1424
  withSpan<TArgs extends unknown[], TReturn>(traceFunctionKey: string, optionsOrFn: SpanOptions | ((...args: TArgs) => TReturn), maybeFn?: (...args: TArgs) => TReturn): (...args: TArgs) => TReturn;
1395
1425
  /**
1396
1426
  * Get a detached handle to a previously-created trace, looked up by the
1397
- * caller-supplied id (the same id passed at trace creation).
1427
+ * canonical Bitfab trace ID.
1398
1428
  *
1399
1429
  * The returned handle is not tied to AsyncLocalStorage - each method sends
1400
1430
  * to the server immediately. Useful for adding context to a trace from a
1401
1431
  * different process or thread than the one that created it.
1402
1432
  *
1403
- * Throws synchronously if `traceId` is malformed (empty, too long, or
1404
- * contains characters outside `[a-zA-Z0-9_\-.:]`). Server returns 404 if
1405
- * no trace exists with that id in the org; the failure surfaces as a
1433
+ * Throws synchronously if `traceId` is not a valid Bitfab trace ID. The
1434
+ * server returns 404 if no trace exists with that ID in the org; the failure surfaces as a
1406
1435
  * logged warning (fire-and-forget) or via the awaited promise.
1407
1436
  *
1408
1437
  * Example:
1409
1438
  * ```typescript
1410
- * const trace = client.getTrace("order_abc_123");
1439
+ * const trace = client.getTrace(traceId);
1411
1440
  * await trace.addContext({ refund_status: "approved" });
1412
1441
  * await trace.setMetadata({ region: "us-west" });
1413
1442
  * ```
1414
1443
  */
1415
1444
  getTrace(traceId: string): DetachedTrace;
1445
+ /**
1446
+ * Fetch one persisted span from a trace without loading the full trace.
1447
+ * Name lookups return the last matching span by default. Pass `occurrence`
1448
+ * as `"first"` or a zero-based index to select a different match.
1449
+ */
1450
+ getTraceSpan(traceId: string, lookup: SpanLookup): Promise<CapturedSpan | null>;
1416
1451
  /**
1417
1452
  * Get a function wrapper for a specific trace function key.
1418
1453
  *
@@ -1614,7 +1649,7 @@ declare class BitfabFunction {
1614
1649
  /**
1615
1650
  * SDK version from package.json (injected at build time)
1616
1651
  */
1617
- declare const __version__ = "0.28.9";
1652
+ declare const __version__ = "0.28.11";
1618
1653
 
1619
1654
  /**
1620
1655
  * Constants for the Bitfab SDK.
@@ -1682,4 +1717,4 @@ declare const finalizers: {
1682
1717
  readableStream: typeof readableStream;
1683
1718
  };
1684
1719
 
1685
- export { type ActiveSpanContext, type AdaptContext, type AdaptInputsFn, type AllowedEnvVars, BITFAB_PROGRESS_PREFIX, type BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, type BitfabConfig, BitfabError, BitfabFunction, BitfabLangGraphCallbackHandler as BitfabLangChainCallbackHandler, BitfabLangGraphCallbackHandler, type BitfabLanguageModelMiddleware, BitfabOpenAIAgentHandler, BitfabOpenAITracingProcessor, BitfabVercelAiHandler, type CodeChangeFile, type CurrentSpan, type CurrentTrace, DEFAULT_SERVICE_URL, type DbSnapshotConfig, type DbSnapshotProvider, type DbSnapshotRef, type DetachedTrace, type MockStrategy, type ProviderDefinition, ReplayEnvironment, type ReplayEnvironmentSnapshot, type ReplayItem, type ReplayOptions, type ReplayProgress, type ReplayResult, SUPPORTED_PROVIDERS, type SpanOptions, type SpanType, type TokenUsage, type TraceResponse, type TracingProcessor, type VercelCallParams, type VercelGenerateResult, type VercelStreamResult, type WrapBAMLOptions, type WrappedBamlFn, __version__, finalizers, flushTraces, getCurrentSpan, getCurrentTrace, reportReplayProgress };
1720
+ export { type ActiveSpanContext, type AdaptContext, type AdaptInputsFn, type AllowedEnvVars, BITFAB_PROGRESS_PREFIX, type BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, type BitfabConfig, BitfabError, BitfabFunction, BitfabLangGraphCallbackHandler as BitfabLangChainCallbackHandler, BitfabLangGraphCallbackHandler, type BitfabLanguageModelMiddleware, BitfabOpenAIAgentHandler, BitfabOpenAITracingProcessor, BitfabVercelAiHandler, type CapturedSpan, type CodeChangeFile, type CurrentSpan, type CurrentTrace, DEFAULT_SERVICE_URL, type DbSnapshotConfig, type DbSnapshotProvider, type DbSnapshotRef, type DetachedTrace, type MockStrategy, type ProviderDefinition, ReplayEnvironment, type ReplayEnvironmentSnapshot, type ReplayItem, type ReplayOptions, type ReplayProgress, type ReplayResult, SUPPORTED_PROVIDERS, type SpanLookup, type SpanOccurrence, type SpanOptions, type SpanType, type TokenUsage, type TraceResponse, type TracingProcessor, type VercelCallParams, type VercelGenerateResult, type VercelStreamResult, type WrapBAMLOptions, type WrappedBamlFn, __version__, finalizers, flushTraces, getCurrentSpan, getCurrentTrace, reportReplayProgress };
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  flushTraces,
15
15
  getCurrentSpan,
16
16
  getCurrentTrace
17
- } from "./chunk-VQ4GMKQ7.js";
17
+ } from "./chunk-ZBWTCBVQ.js";
18
18
  import {
19
19
  BITFAB_PROGRESS_PREFIX,
20
20
  BitfabError,
package/dist/node.cjs CHANGED
@@ -697,7 +697,7 @@ registerAsyncLocalStorageClass(
697
697
  );
698
698
 
699
699
  // src/version.generated.ts
700
- var __version__ = "0.28.9";
700
+ var __version__ = "0.28.11";
701
701
 
702
702
  // src/constants.ts
703
703
  var DEFAULT_SERVICE_URL = "https://bitfab.ai";
@@ -937,6 +937,50 @@ var HttpClient = class {
937
937
  async lookupFunction(name) {
938
938
  return this.request("/api/sdk/functions/lookup", { name });
939
939
  }
940
+ async getTraceSpan(traceId, lookup) {
941
+ const searchParams = new URLSearchParams();
942
+ if (lookup.id !== void 0) {
943
+ searchParams.set("id", lookup.id);
944
+ } else {
945
+ searchParams.set("name", lookup.name);
946
+ searchParams.set("occurrence", String(lookup.occurrence ?? "last"));
947
+ }
948
+ const endpoint = `/api/sdk/traces/${encodeURIComponent(traceId)}/span?${searchParams.toString()}`;
949
+ const response = await this.get(endpoint);
950
+ return response.span;
951
+ }
952
+ async get(endpoint) {
953
+ const url = `${this.serviceUrl}${endpoint}`;
954
+ const controller = new AbortController();
955
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
956
+ try {
957
+ const response = await fetch(url, {
958
+ method: "GET",
959
+ headers: { Authorization: `Bearer ${this.resolveApiKey() ?? ""}` },
960
+ signal: controller.signal
961
+ });
962
+ if (!response.ok) {
963
+ const errorText = await response.text();
964
+ throw new BitfabError(
965
+ `HTTP ${response.status}: ${errorText.slice(0, 500)}`
966
+ );
967
+ }
968
+ return await response.json();
969
+ } catch (error) {
970
+ if (error instanceof BitfabError) {
971
+ throw error;
972
+ }
973
+ if (error instanceof Error) {
974
+ if (error.name === "AbortError") {
975
+ throw new BitfabError(`Request timed out after ${this.timeout}ms`);
976
+ }
977
+ throw new BitfabError(error.message);
978
+ }
979
+ throw new BitfabError("Unknown error occurred");
980
+ } finally {
981
+ clearTimeout(timeoutId);
982
+ }
983
+ }
940
984
  /**
941
985
  * Send an internal trace (from BAML execution).
942
986
  * Fire-and-forget with awaitOnExit - doesn't block the caller.
@@ -993,12 +1037,12 @@ var HttpClient = class {
993
1037
  });
994
1038
  }
995
1039
  /**
996
- * Partial update of an existing external trace identified by sourceTraceId.
1040
+ * Partial update of an existing trace identified by its Bitfab trace ID.
997
1041
  * Used by the detached `client.getTrace(id)` handle. Fire-and-forget;
998
1042
  * returns a tracked promise that callers may optionally await.
999
1043
  */
1000
- patchTrace(sourceTraceId, payload) {
1001
- const endpoint = `/api/sdk/externalTraces/${encodeURIComponent(sourceTraceId)}`;
1044
+ patchTrace(traceId, payload) {
1045
+ const endpoint = `/api/sdk/traces/${encodeURIComponent(traceId)}`;
1002
1046
  return awaitOnExit(
1003
1047
  this.request(endpoint, payload, { method: "PATCH" })
1004
1048
  ).catch((error) => {
@@ -1336,6 +1380,7 @@ var BitfabClaudeAgentHandler = class {
1336
1380
  const traceId = this.ensureTrace();
1337
1381
  const { safe: safeInput, dropped: inputDropped } = toJsonSafeReport(inputData);
1338
1382
  const spanInfo = {
1383
+ id: randomUuid(),
1339
1384
  spanId,
1340
1385
  traceId,
1341
1386
  parentId: parentId ?? null,
@@ -1399,6 +1444,8 @@ var BitfabClaudeAgentHandler = class {
1399
1444
  rawSpan.parent_id = spanInfo.parentId;
1400
1445
  }
1401
1446
  const payload = {
1447
+ id: spanInfo.id,
1448
+ traceId: spanInfo.traceId,
1402
1449
  type: "sdk-function",
1403
1450
  source: "typescript-sdk-claude-agent-sdk",
1404
1451
  traceFunctionKey: this.traceFunctionKey,
@@ -1428,6 +1475,7 @@ var BitfabClaudeAgentHandler = class {
1428
1475
  externalTrace.metadata = metadata;
1429
1476
  }
1430
1477
  const traceData = {
1478
+ id: traceId,
1431
1479
  type: "sdk-function",
1432
1480
  source: "typescript-sdk-claude-agent-sdk",
1433
1481
  traceFunctionKey: this.traceFunctionKey,
@@ -1696,6 +1744,7 @@ var BitfabClaudeAgentHandler = class {
1696
1744
  }
1697
1745
  Object.assign(llmContext, this.currentLlmUsage);
1698
1746
  const spanInfo = {
1747
+ id: randomUuid(),
1699
1748
  spanId,
1700
1749
  traceId,
1701
1750
  parentId,
@@ -2342,6 +2391,7 @@ var BitfabLangGraphCallbackHandler = class {
2342
2391
  const contexts = Object.keys(lgMetadata).length > 0 ? [lgMetadata] : [];
2343
2392
  const { safe: safeInput, dropped: inputDropped } = toJsonSafeReport(inputData);
2344
2393
  const spanInfo = {
2394
+ id: randomUuid(),
2345
2395
  spanId: runId,
2346
2396
  traceId: invocation.traceId,
2347
2397
  rootRunId: invocation.rootRunId,
@@ -2420,6 +2470,8 @@ var BitfabLangGraphCallbackHandler = class {
2420
2470
  rawSpan.parent_id = spanInfo.parentId;
2421
2471
  }
2422
2472
  const payload = {
2473
+ id: spanInfo.id,
2474
+ traceId: spanInfo.traceId,
2423
2475
  type: "sdk-function",
2424
2476
  source: "typescript-sdk-langgraph",
2425
2477
  traceFunctionKey: this.traceFunctionKey,
@@ -2435,6 +2487,7 @@ var BitfabLangGraphCallbackHandler = class {
2435
2487
  sendTraceCompletion(rootSpan, activeContext) {
2436
2488
  const completed = activeContext === null;
2437
2489
  const traceData = {
2490
+ id: rootSpan.traceId,
2438
2491
  type: "sdk-function",
2439
2492
  source: "typescript-sdk-langgraph",
2440
2493
  traceFunctionKey: this.traceFunctionKey,
@@ -2454,6 +2507,7 @@ var BitfabLangGraphCallbackHandler = class {
2454
2507
  }
2455
2508
  sendTraceStart(rootSpan) {
2456
2509
  const traceData = {
2510
+ id: rootSpan.traceId,
2457
2511
  type: "sdk-function",
2458
2512
  source: "typescript-sdk-langgraph",
2459
2513
  traceFunctionKey: this.traceFunctionKey,
@@ -2812,6 +2866,7 @@ var ReplayEnvironment = class {
2812
2866
  init_serialize();
2813
2867
 
2814
2868
  // src/tracing.ts
2869
+ init_randomUuid();
2815
2870
  var BitfabOpenAITracingProcessor = class {
2816
2871
  /**
2817
2872
  * Initialize the tracing processor.
@@ -2821,6 +2876,7 @@ var BitfabOpenAITracingProcessor = class {
2821
2876
  constructor(config) {
2822
2877
  this.activeTraces = {};
2823
2878
  this.activeSpanMappings = {};
2879
+ this.canonicalTraceIds = {};
2824
2880
  this.httpClient = new HttpClient({
2825
2881
  apiKey: config.apiKey,
2826
2882
  serviceUrl: config.serviceUrl ?? DEFAULT_SERVICE_URL,
@@ -2828,6 +2884,15 @@ var BitfabOpenAITracingProcessor = class {
2828
2884
  });
2829
2885
  this.getActiveSpanContext = config.getActiveSpanContext ?? null;
2830
2886
  }
2887
+ getCanonicalTraceId(sourceTraceId) {
2888
+ const existing = this.canonicalTraceIds[sourceTraceId];
2889
+ if (existing) {
2890
+ return existing;
2891
+ }
2892
+ const created = randomUuid();
2893
+ this.canonicalTraceIds[sourceTraceId] = created;
2894
+ return created;
2895
+ }
2831
2896
  /**
2832
2897
  * Called when a trace is started.
2833
2898
  * If there's an active withSpan context, the trace ID is remapped to the
@@ -2839,7 +2904,12 @@ var BitfabOpenAITracingProcessor = class {
2839
2904
  if (activeContext) {
2840
2905
  this.activeSpanMappings[trace.traceId] = activeContext;
2841
2906
  }
2842
- this.sendTrace(trace, activeContext ? { id: activeContext.traceId } : {});
2907
+ const canonicalTraceId = activeContext?.traceId ?? this.getCanonicalTraceId(trace.traceId);
2908
+ this.canonicalTraceIds[trace.traceId] = canonicalTraceId;
2909
+ this.sendTrace(trace, {
2910
+ id: canonicalTraceId,
2911
+ sourceTraceId: activeContext?.traceId
2912
+ });
2843
2913
  }
2844
2914
  /**
2845
2915
  * Called when a trace is ended.
@@ -2848,11 +2918,13 @@ var BitfabOpenAITracingProcessor = class {
2848
2918
  */
2849
2919
  async onTraceEnd(trace) {
2850
2920
  const mapping = this.activeSpanMappings[trace.traceId];
2851
- this.sendTrace(
2852
- trace,
2853
- mapping ? { id: mapping.traceId } : { completed: true }
2854
- );
2921
+ this.sendTrace(trace, {
2922
+ completed: mapping === void 0,
2923
+ id: mapping?.traceId ?? this.getCanonicalTraceId(trace.traceId),
2924
+ sourceTraceId: mapping?.traceId
2925
+ });
2855
2926
  delete this.activeSpanMappings[trace.traceId];
2927
+ delete this.canonicalTraceIds[trace.traceId];
2856
2928
  delete this.activeTraces[trace.traceId];
2857
2929
  }
2858
2930
  /**
@@ -2882,22 +2954,25 @@ var BitfabOpenAITracingProcessor = class {
2882
2954
  async shutdown(_timeout) {
2883
2955
  this.activeTraces = {};
2884
2956
  this.activeSpanMappings = {};
2957
+ this.canonicalTraceIds = {};
2885
2958
  }
2886
2959
  /**
2887
2960
  * Send trace to Bitfab API (fire-and-forget).
2888
2961
  * When traceIdOverride is provided, the trace ID is remapped to link
2889
2962
  * the OpenAI trace into an outer withSpan trace.
2890
2963
  */
2891
- sendTrace(trace, overrides = {}) {
2964
+ sendTrace(trace, options = {}) {
2892
2965
  try {
2893
- const { completed, ...traceOverrides } = overrides;
2894
2966
  const traceData = trace.toJSON();
2895
- Object.assign(traceData, traceOverrides);
2967
+ if (options.sourceTraceId) {
2968
+ traceData.id = options.sourceTraceId;
2969
+ }
2896
2970
  this.httpClient.sendExternalTrace({
2971
+ ...options.id && { id: options.id },
2897
2972
  type: "openai",
2898
2973
  source: "typescript-sdk-openai-tracing",
2899
2974
  externalTrace: traceData,
2900
- completed: completed ?? false
2975
+ completed: options.completed ?? false
2901
2976
  });
2902
2977
  } catch {
2903
2978
  }
@@ -2983,6 +3058,7 @@ var BitfabOpenAITracingProcessor = class {
2983
3058
  */
2984
3059
  buildSpanPayload(serializedSpan, errors) {
2985
3060
  const payload = {
3061
+ id: randomUuid(),
2986
3062
  type: "openai",
2987
3063
  source: "typescript-sdk-openai-tracing",
2988
3064
  sourceTraceId: serializedSpan.trace_id ?? "unknown",
@@ -3005,6 +3081,10 @@ var BitfabOpenAITracingProcessor = class {
3005
3081
  this.extractSpanInputResponse(span, serializedSpan, errors);
3006
3082
  this.applySpanOverrides(serializedSpan, span.traceId ?? "");
3007
3083
  const payload = this.buildSpanPayload(serializedSpan, errors);
3084
+ const canonicalTraceId = span.traceId ? this.getCanonicalTraceId(span.traceId) : void 0;
3085
+ if (canonicalTraceId) {
3086
+ payload.traceId = canonicalTraceId;
3087
+ }
3008
3088
  this.httpClient.sendExternalSpan(payload);
3009
3089
  }
3010
3090
  };
@@ -3143,8 +3223,11 @@ init_warnOnce();
3143
3223
  var activeTraceStates = /* @__PURE__ */ new Map();
3144
3224
  var pendingSpanPromises = /* @__PURE__ */ new Map();
3145
3225
  var asyncLocalStorage = null;
3226
+ var initializeAsyncContext = () => {
3227
+ asyncLocalStorage ?? (asyncLocalStorage = createAsyncLocalStorage());
3228
+ };
3146
3229
  var asyncLocalStorageReady = asyncStorageReady.then(() => {
3147
- asyncLocalStorage = createAsyncLocalStorage();
3230
+ initializeAsyncContext();
3148
3231
  });
3149
3232
  var browserSpanStack = [];
3150
3233
  function getSpanStack() {
@@ -3318,24 +3401,19 @@ function extractContextFromCollector(collector) {
3318
3401
  return null;
3319
3402
  }
3320
3403
  }
3321
- var TRACE_ID_PATTERN = /^[a-zA-Z0-9_\-.:]+$/;
3322
- var TRACE_ID_MAX_LENGTH = 256;
3404
+ 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;
3323
3405
  function validateTraceId(traceId) {
3324
- if (typeof traceId !== "string" || traceId.length === 0) {
3325
- throw new BitfabError("traceId is required and must be a non-empty string");
3406
+ if (typeof traceId !== "string" || !UUID_PATTERN.test(traceId)) {
3407
+ throw new BitfabError("traceId must be a valid Bitfab trace ID");
3326
3408
  }
3327
- if (traceId.length > TRACE_ID_MAX_LENGTH) {
3328
- throw new BitfabError(
3329
- `traceId must be ${TRACE_ID_MAX_LENGTH} characters or fewer`
3330
- );
3331
- }
3332
- if (!TRACE_ID_PATTERN.test(traceId)) {
3333
- throw new BitfabError(
3334
- `traceId may only contain letters, digits, "_", "-", ".", ":"`
3335
- );
3409
+ }
3410
+ function validateSpanId(id) {
3411
+ if (typeof id !== "string" || !UUID_PATTERN.test(id)) {
3412
+ throw new BitfabError("id must be a valid Bitfab span ID");
3336
3413
  }
3337
3414
  }
3338
3415
  var noOpSpan = {
3416
+ id: "",
3339
3417
  traceId: "",
3340
3418
  addContext() {
3341
3419
  },
@@ -3359,6 +3437,7 @@ function getCurrentSpan() {
3359
3437
  return noOpSpan;
3360
3438
  }
3361
3439
  return {
3440
+ id: current.spanId,
3362
3441
  traceId: current.traceId,
3363
3442
  addContext(context) {
3364
3443
  try {
@@ -3913,6 +3992,7 @@ var Bitfab = class {
3913
3992
  if (!self.isTracingEnabled()) {
3914
3993
  return fn.apply(this, args);
3915
3994
  }
3995
+ initializeAsyncContext();
3916
3996
  if (!asyncLocalStorage && !isAsyncStorageInitDone()) {
3917
3997
  return asyncLocalStorageReady.then(
3918
3998
  () => wrappedFn.apply(this, args)
@@ -4154,20 +4234,19 @@ var Bitfab = class {
4154
4234
  }
4155
4235
  /**
4156
4236
  * Get a detached handle to a previously-created trace, looked up by the
4157
- * caller-supplied id (the same id passed at trace creation).
4237
+ * canonical Bitfab trace ID.
4158
4238
  *
4159
4239
  * The returned handle is not tied to AsyncLocalStorage - each method sends
4160
4240
  * to the server immediately. Useful for adding context to a trace from a
4161
4241
  * different process or thread than the one that created it.
4162
4242
  *
4163
- * Throws synchronously if `traceId` is malformed (empty, too long, or
4164
- * contains characters outside `[a-zA-Z0-9_\-.:]`). Server returns 404 if
4165
- * no trace exists with that id in the org; the failure surfaces as a
4243
+ * Throws synchronously if `traceId` is not a valid Bitfab trace ID. The
4244
+ * server returns 404 if no trace exists with that ID in the org; the failure surfaces as a
4166
4245
  * logged warning (fire-and-forget) or via the awaited promise.
4167
4246
  *
4168
4247
  * Example:
4169
4248
  * ```typescript
4170
- * const trace = client.getTrace("order_abc_123");
4249
+ * const trace = client.getTrace(traceId);
4171
4250
  * await trace.addContext({ refund_status: "approved" });
4172
4251
  * await trace.setMetadata({ region: "us-west" });
4173
4252
  * ```
@@ -4207,6 +4286,33 @@ var Bitfab = class {
4207
4286
  }
4208
4287
  };
4209
4288
  }
4289
+ /**
4290
+ * Fetch one persisted span from a trace without loading the full trace.
4291
+ * Name lookups return the last matching span by default. Pass `occurrence`
4292
+ * as `"first"` or a zero-based index to select a different match.
4293
+ */
4294
+ async getTraceSpan(traceId, lookup) {
4295
+ validateTraceId(traceId);
4296
+ const hasId = lookup.id !== void 0;
4297
+ const hasName = lookup.name !== void 0;
4298
+ if (hasId === hasName) {
4299
+ throw new BitfabError("Provide exactly one of id or name");
4300
+ }
4301
+ if (hasId) {
4302
+ validateSpanId(lookup.id);
4303
+ } else {
4304
+ if (lookup.name.length === 0) {
4305
+ throw new BitfabError("name must be a non-empty string");
4306
+ }
4307
+ const occurrence = lookup.occurrence ?? "last";
4308
+ if (occurrence !== "first" && occurrence !== "last" && (!Number.isInteger(occurrence) || occurrence < 0)) {
4309
+ throw new BitfabError(
4310
+ 'occurrence must be "first", "last", or a non-negative integer'
4311
+ );
4312
+ }
4313
+ }
4314
+ return this.httpClient.getTraceSpan(traceId, lookup);
4315
+ }
4210
4316
  /**
4211
4317
  * Get a function wrapper for a specific trace function key.
4212
4318
  *
@@ -4265,6 +4371,7 @@ var Bitfab = class {
4265
4371
  };
4266
4372
  }
4267
4373
  return this.httpClient.sendExternalTrace({
4374
+ id: params.traceId,
4268
4375
  type: "sdk-function",
4269
4376
  source: "typescript-sdk-function",
4270
4377
  traceFunctionKey: params.traceFunctionKey,
@@ -4320,6 +4427,8 @@ var Bitfab = class {
4320
4427
  externalSpan.input_source_span_id = params.inputSourceSpanId;
4321
4428
  }
4322
4429
  return this.httpClient.sendExternalSpan({
4430
+ id: params.spanId,
4431
+ traceId: params.traceId,
4323
4432
  type: "sdk-function",
4324
4433
  source: "typescript-sdk-function",
4325
4434
  sourceTraceId: params.traceId,