bitfab 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
@@ -23,7 +23,7 @@ import {
23
23
  flushTraces,
24
24
  getCurrentSpan,
25
25
  getCurrentTrace
26
- } from "./chunk-VQ4GMKQ7.js";
26
+ } from "./chunk-ZBWTCBVQ.js";
27
27
  import {
28
28
  BITFAB_PROGRESS_PREFIX,
29
29
  BitfabError,
package/dist/node.cjs CHANGED
@@ -706,7 +706,7 @@ registerAsyncLocalStorageClass(
706
706
  );
707
707
 
708
708
  // src/version.generated.ts
709
- var __version__ = "0.28.9";
709
+ var __version__ = "0.28.11";
710
710
 
711
711
  // src/constants.ts
712
712
  var DEFAULT_SERVICE_URL = "https://bitfab.ai";
@@ -946,6 +946,50 @@ var HttpClient = class {
946
946
  async lookupFunction(name) {
947
947
  return this.request("/api/sdk/functions/lookup", { name });
948
948
  }
949
+ async getTraceSpan(traceId, lookup) {
950
+ const searchParams = new URLSearchParams();
951
+ if (lookup.id !== void 0) {
952
+ searchParams.set("id", lookup.id);
953
+ } else {
954
+ searchParams.set("name", lookup.name);
955
+ searchParams.set("occurrence", String(lookup.occurrence ?? "last"));
956
+ }
957
+ const endpoint = `/api/sdk/traces/${encodeURIComponent(traceId)}/span?${searchParams.toString()}`;
958
+ const response = await this.get(endpoint);
959
+ return response.span;
960
+ }
961
+ async get(endpoint) {
962
+ const url = `${this.serviceUrl}${endpoint}`;
963
+ const controller = new AbortController();
964
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
965
+ try {
966
+ const response = await fetch(url, {
967
+ method: "GET",
968
+ headers: { Authorization: `Bearer ${this.resolveApiKey() ?? ""}` },
969
+ signal: controller.signal
970
+ });
971
+ if (!response.ok) {
972
+ const errorText = await response.text();
973
+ throw new BitfabError(
974
+ `HTTP ${response.status}: ${errorText.slice(0, 500)}`
975
+ );
976
+ }
977
+ return await response.json();
978
+ } catch (error) {
979
+ if (error instanceof BitfabError) {
980
+ throw error;
981
+ }
982
+ if (error instanceof Error) {
983
+ if (error.name === "AbortError") {
984
+ throw new BitfabError(`Request timed out after ${this.timeout}ms`);
985
+ }
986
+ throw new BitfabError(error.message);
987
+ }
988
+ throw new BitfabError("Unknown error occurred");
989
+ } finally {
990
+ clearTimeout(timeoutId);
991
+ }
992
+ }
949
993
  /**
950
994
  * Send an internal trace (from BAML execution).
951
995
  * Fire-and-forget with awaitOnExit - doesn't block the caller.
@@ -1002,12 +1046,12 @@ var HttpClient = class {
1002
1046
  });
1003
1047
  }
1004
1048
  /**
1005
- * Partial update of an existing external trace identified by sourceTraceId.
1049
+ * Partial update of an existing trace identified by its Bitfab trace ID.
1006
1050
  * Used by the detached `client.getTrace(id)` handle. Fire-and-forget;
1007
1051
  * returns a tracked promise that callers may optionally await.
1008
1052
  */
1009
- patchTrace(sourceTraceId, payload) {
1010
- const endpoint = `/api/sdk/externalTraces/${encodeURIComponent(sourceTraceId)}`;
1053
+ patchTrace(traceId, payload) {
1054
+ const endpoint = `/api/sdk/traces/${encodeURIComponent(traceId)}`;
1011
1055
  return awaitOnExit(
1012
1056
  this.request(endpoint, payload, { method: "PATCH" })
1013
1057
  ).catch((error) => {
@@ -1345,6 +1389,7 @@ var BitfabClaudeAgentHandler = class {
1345
1389
  const traceId = this.ensureTrace();
1346
1390
  const { safe: safeInput, dropped: inputDropped } = toJsonSafeReport(inputData);
1347
1391
  const spanInfo = {
1392
+ id: randomUuid(),
1348
1393
  spanId,
1349
1394
  traceId,
1350
1395
  parentId: parentId ?? null,
@@ -1408,6 +1453,8 @@ var BitfabClaudeAgentHandler = class {
1408
1453
  rawSpan.parent_id = spanInfo.parentId;
1409
1454
  }
1410
1455
  const payload = {
1456
+ id: spanInfo.id,
1457
+ traceId: spanInfo.traceId,
1411
1458
  type: "sdk-function",
1412
1459
  source: "typescript-sdk-claude-agent-sdk",
1413
1460
  traceFunctionKey: this.traceFunctionKey,
@@ -1437,6 +1484,7 @@ var BitfabClaudeAgentHandler = class {
1437
1484
  externalTrace.metadata = metadata;
1438
1485
  }
1439
1486
  const traceData = {
1487
+ id: traceId,
1440
1488
  type: "sdk-function",
1441
1489
  source: "typescript-sdk-claude-agent-sdk",
1442
1490
  traceFunctionKey: this.traceFunctionKey,
@@ -1705,6 +1753,7 @@ var BitfabClaudeAgentHandler = class {
1705
1753
  }
1706
1754
  Object.assign(llmContext, this.currentLlmUsage);
1707
1755
  const spanInfo = {
1756
+ id: randomUuid(),
1708
1757
  spanId,
1709
1758
  traceId,
1710
1759
  parentId,
@@ -2351,6 +2400,7 @@ var BitfabLangGraphCallbackHandler = class {
2351
2400
  const contexts = Object.keys(lgMetadata).length > 0 ? [lgMetadata] : [];
2352
2401
  const { safe: safeInput, dropped: inputDropped } = toJsonSafeReport(inputData);
2353
2402
  const spanInfo = {
2403
+ id: randomUuid(),
2354
2404
  spanId: runId,
2355
2405
  traceId: invocation.traceId,
2356
2406
  rootRunId: invocation.rootRunId,
@@ -2429,6 +2479,8 @@ var BitfabLangGraphCallbackHandler = class {
2429
2479
  rawSpan.parent_id = spanInfo.parentId;
2430
2480
  }
2431
2481
  const payload = {
2482
+ id: spanInfo.id,
2483
+ traceId: spanInfo.traceId,
2432
2484
  type: "sdk-function",
2433
2485
  source: "typescript-sdk-langgraph",
2434
2486
  traceFunctionKey: this.traceFunctionKey,
@@ -2444,6 +2496,7 @@ var BitfabLangGraphCallbackHandler = class {
2444
2496
  sendTraceCompletion(rootSpan, activeContext) {
2445
2497
  const completed = activeContext === null;
2446
2498
  const traceData = {
2499
+ id: rootSpan.traceId,
2447
2500
  type: "sdk-function",
2448
2501
  source: "typescript-sdk-langgraph",
2449
2502
  traceFunctionKey: this.traceFunctionKey,
@@ -2463,6 +2516,7 @@ var BitfabLangGraphCallbackHandler = class {
2463
2516
  }
2464
2517
  sendTraceStart(rootSpan) {
2465
2518
  const traceData = {
2519
+ id: rootSpan.traceId,
2466
2520
  type: "sdk-function",
2467
2521
  source: "typescript-sdk-langgraph",
2468
2522
  traceFunctionKey: this.traceFunctionKey,
@@ -2821,6 +2875,7 @@ var ReplayEnvironment = class {
2821
2875
  init_serialize();
2822
2876
 
2823
2877
  // src/tracing.ts
2878
+ init_randomUuid();
2824
2879
  var BitfabOpenAITracingProcessor = class {
2825
2880
  /**
2826
2881
  * Initialize the tracing processor.
@@ -2830,6 +2885,7 @@ var BitfabOpenAITracingProcessor = class {
2830
2885
  constructor(config) {
2831
2886
  this.activeTraces = {};
2832
2887
  this.activeSpanMappings = {};
2888
+ this.canonicalTraceIds = {};
2833
2889
  this.httpClient = new HttpClient({
2834
2890
  apiKey: config.apiKey,
2835
2891
  serviceUrl: config.serviceUrl ?? DEFAULT_SERVICE_URL,
@@ -2837,6 +2893,15 @@ var BitfabOpenAITracingProcessor = class {
2837
2893
  });
2838
2894
  this.getActiveSpanContext = config.getActiveSpanContext ?? null;
2839
2895
  }
2896
+ getCanonicalTraceId(sourceTraceId) {
2897
+ const existing = this.canonicalTraceIds[sourceTraceId];
2898
+ if (existing) {
2899
+ return existing;
2900
+ }
2901
+ const created = randomUuid();
2902
+ this.canonicalTraceIds[sourceTraceId] = created;
2903
+ return created;
2904
+ }
2840
2905
  /**
2841
2906
  * Called when a trace is started.
2842
2907
  * If there's an active withSpan context, the trace ID is remapped to the
@@ -2848,7 +2913,12 @@ var BitfabOpenAITracingProcessor = class {
2848
2913
  if (activeContext) {
2849
2914
  this.activeSpanMappings[trace.traceId] = activeContext;
2850
2915
  }
2851
- this.sendTrace(trace, activeContext ? { id: activeContext.traceId } : {});
2916
+ const canonicalTraceId = activeContext?.traceId ?? this.getCanonicalTraceId(trace.traceId);
2917
+ this.canonicalTraceIds[trace.traceId] = canonicalTraceId;
2918
+ this.sendTrace(trace, {
2919
+ id: canonicalTraceId,
2920
+ sourceTraceId: activeContext?.traceId
2921
+ });
2852
2922
  }
2853
2923
  /**
2854
2924
  * Called when a trace is ended.
@@ -2857,11 +2927,13 @@ var BitfabOpenAITracingProcessor = class {
2857
2927
  */
2858
2928
  async onTraceEnd(trace) {
2859
2929
  const mapping = this.activeSpanMappings[trace.traceId];
2860
- this.sendTrace(
2861
- trace,
2862
- mapping ? { id: mapping.traceId } : { completed: true }
2863
- );
2930
+ this.sendTrace(trace, {
2931
+ completed: mapping === void 0,
2932
+ id: mapping?.traceId ?? this.getCanonicalTraceId(trace.traceId),
2933
+ sourceTraceId: mapping?.traceId
2934
+ });
2864
2935
  delete this.activeSpanMappings[trace.traceId];
2936
+ delete this.canonicalTraceIds[trace.traceId];
2865
2937
  delete this.activeTraces[trace.traceId];
2866
2938
  }
2867
2939
  /**
@@ -2891,22 +2963,25 @@ var BitfabOpenAITracingProcessor = class {
2891
2963
  async shutdown(_timeout) {
2892
2964
  this.activeTraces = {};
2893
2965
  this.activeSpanMappings = {};
2966
+ this.canonicalTraceIds = {};
2894
2967
  }
2895
2968
  /**
2896
2969
  * Send trace to Bitfab API (fire-and-forget).
2897
2970
  * When traceIdOverride is provided, the trace ID is remapped to link
2898
2971
  * the OpenAI trace into an outer withSpan trace.
2899
2972
  */
2900
- sendTrace(trace, overrides = {}) {
2973
+ sendTrace(trace, options = {}) {
2901
2974
  try {
2902
- const { completed, ...traceOverrides } = overrides;
2903
2975
  const traceData = trace.toJSON();
2904
- Object.assign(traceData, traceOverrides);
2976
+ if (options.sourceTraceId) {
2977
+ traceData.id = options.sourceTraceId;
2978
+ }
2905
2979
  this.httpClient.sendExternalTrace({
2980
+ ...options.id && { id: options.id },
2906
2981
  type: "openai",
2907
2982
  source: "typescript-sdk-openai-tracing",
2908
2983
  externalTrace: traceData,
2909
- completed: completed ?? false
2984
+ completed: options.completed ?? false
2910
2985
  });
2911
2986
  } catch {
2912
2987
  }
@@ -2992,6 +3067,7 @@ var BitfabOpenAITracingProcessor = class {
2992
3067
  */
2993
3068
  buildSpanPayload(serializedSpan, errors) {
2994
3069
  const payload = {
3070
+ id: randomUuid(),
2995
3071
  type: "openai",
2996
3072
  source: "typescript-sdk-openai-tracing",
2997
3073
  sourceTraceId: serializedSpan.trace_id ?? "unknown",
@@ -3014,6 +3090,10 @@ var BitfabOpenAITracingProcessor = class {
3014
3090
  this.extractSpanInputResponse(span, serializedSpan, errors);
3015
3091
  this.applySpanOverrides(serializedSpan, span.traceId ?? "");
3016
3092
  const payload = this.buildSpanPayload(serializedSpan, errors);
3093
+ const canonicalTraceId = span.traceId ? this.getCanonicalTraceId(span.traceId) : void 0;
3094
+ if (canonicalTraceId) {
3095
+ payload.traceId = canonicalTraceId;
3096
+ }
3017
3097
  this.httpClient.sendExternalSpan(payload);
3018
3098
  }
3019
3099
  };
@@ -3152,8 +3232,11 @@ init_warnOnce();
3152
3232
  var activeTraceStates = /* @__PURE__ */ new Map();
3153
3233
  var pendingSpanPromises = /* @__PURE__ */ new Map();
3154
3234
  var asyncLocalStorage = null;
3235
+ var initializeAsyncContext = () => {
3236
+ asyncLocalStorage ?? (asyncLocalStorage = createAsyncLocalStorage());
3237
+ };
3155
3238
  var asyncLocalStorageReady = asyncStorageReady.then(() => {
3156
- asyncLocalStorage = createAsyncLocalStorage();
3239
+ initializeAsyncContext();
3157
3240
  });
3158
3241
  var browserSpanStack = [];
3159
3242
  function getSpanStack() {
@@ -3327,24 +3410,19 @@ function extractContextFromCollector(collector) {
3327
3410
  return null;
3328
3411
  }
3329
3412
  }
3330
- var TRACE_ID_PATTERN = /^[a-zA-Z0-9_\-.:]+$/;
3331
- var TRACE_ID_MAX_LENGTH = 256;
3413
+ 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;
3332
3414
  function validateTraceId(traceId) {
3333
- if (typeof traceId !== "string" || traceId.length === 0) {
3334
- throw new BitfabError("traceId is required and must be a non-empty string");
3415
+ if (typeof traceId !== "string" || !UUID_PATTERN.test(traceId)) {
3416
+ throw new BitfabError("traceId must be a valid Bitfab trace ID");
3335
3417
  }
3336
- if (traceId.length > TRACE_ID_MAX_LENGTH) {
3337
- throw new BitfabError(
3338
- `traceId must be ${TRACE_ID_MAX_LENGTH} characters or fewer`
3339
- );
3340
- }
3341
- if (!TRACE_ID_PATTERN.test(traceId)) {
3342
- throw new BitfabError(
3343
- `traceId may only contain letters, digits, "_", "-", ".", ":"`
3344
- );
3418
+ }
3419
+ function validateSpanId(id) {
3420
+ if (typeof id !== "string" || !UUID_PATTERN.test(id)) {
3421
+ throw new BitfabError("id must be a valid Bitfab span ID");
3345
3422
  }
3346
3423
  }
3347
3424
  var noOpSpan = {
3425
+ id: "",
3348
3426
  traceId: "",
3349
3427
  addContext() {
3350
3428
  },
@@ -3368,6 +3446,7 @@ function getCurrentSpan() {
3368
3446
  return noOpSpan;
3369
3447
  }
3370
3448
  return {
3449
+ id: current.spanId,
3371
3450
  traceId: current.traceId,
3372
3451
  addContext(context) {
3373
3452
  try {
@@ -3922,6 +4001,7 @@ var Bitfab = class {
3922
4001
  if (!self.isTracingEnabled()) {
3923
4002
  return fn.apply(this, args);
3924
4003
  }
4004
+ initializeAsyncContext();
3925
4005
  if (!asyncLocalStorage && !isAsyncStorageInitDone()) {
3926
4006
  return asyncLocalStorageReady.then(
3927
4007
  () => wrappedFn.apply(this, args)
@@ -4163,20 +4243,19 @@ var Bitfab = class {
4163
4243
  }
4164
4244
  /**
4165
4245
  * Get a detached handle to a previously-created trace, looked up by the
4166
- * caller-supplied id (the same id passed at trace creation).
4246
+ * canonical Bitfab trace ID.
4167
4247
  *
4168
4248
  * The returned handle is not tied to AsyncLocalStorage - each method sends
4169
4249
  * to the server immediately. Useful for adding context to a trace from a
4170
4250
  * different process or thread than the one that created it.
4171
4251
  *
4172
- * Throws synchronously if `traceId` is malformed (empty, too long, or
4173
- * contains characters outside `[a-zA-Z0-9_\-.:]`). Server returns 404 if
4174
- * no trace exists with that id in the org; the failure surfaces as a
4252
+ * Throws synchronously if `traceId` is not a valid Bitfab trace ID. The
4253
+ * server returns 404 if no trace exists with that ID in the org; the failure surfaces as a
4175
4254
  * logged warning (fire-and-forget) or via the awaited promise.
4176
4255
  *
4177
4256
  * Example:
4178
4257
  * ```typescript
4179
- * const trace = client.getTrace("order_abc_123");
4258
+ * const trace = client.getTrace(traceId);
4180
4259
  * await trace.addContext({ refund_status: "approved" });
4181
4260
  * await trace.setMetadata({ region: "us-west" });
4182
4261
  * ```
@@ -4216,6 +4295,33 @@ var Bitfab = class {
4216
4295
  }
4217
4296
  };
4218
4297
  }
4298
+ /**
4299
+ * Fetch one persisted span from a trace without loading the full trace.
4300
+ * Name lookups return the last matching span by default. Pass `occurrence`
4301
+ * as `"first"` or a zero-based index to select a different match.
4302
+ */
4303
+ async getTraceSpan(traceId, lookup) {
4304
+ validateTraceId(traceId);
4305
+ const hasId = lookup.id !== void 0;
4306
+ const hasName = lookup.name !== void 0;
4307
+ if (hasId === hasName) {
4308
+ throw new BitfabError("Provide exactly one of id or name");
4309
+ }
4310
+ if (hasId) {
4311
+ validateSpanId(lookup.id);
4312
+ } else {
4313
+ if (lookup.name.length === 0) {
4314
+ throw new BitfabError("name must be a non-empty string");
4315
+ }
4316
+ const occurrence = lookup.occurrence ?? "last";
4317
+ if (occurrence !== "first" && occurrence !== "last" && (!Number.isInteger(occurrence) || occurrence < 0)) {
4318
+ throw new BitfabError(
4319
+ 'occurrence must be "first", "last", or a non-negative integer'
4320
+ );
4321
+ }
4322
+ }
4323
+ return this.httpClient.getTraceSpan(traceId, lookup);
4324
+ }
4219
4325
  /**
4220
4326
  * Get a function wrapper for a specific trace function key.
4221
4327
  *
@@ -4274,6 +4380,7 @@ var Bitfab = class {
4274
4380
  };
4275
4381
  }
4276
4382
  return this.httpClient.sendExternalTrace({
4383
+ id: params.traceId,
4277
4384
  type: "sdk-function",
4278
4385
  source: "typescript-sdk-function",
4279
4386
  traceFunctionKey: params.traceFunctionKey,
@@ -4329,6 +4436,8 @@ var Bitfab = class {
4329
4436
  externalSpan.input_source_span_id = params.inputSourceSpanId;
4330
4437
  }
4331
4438
  return this.httpClient.sendExternalSpan({
4439
+ id: params.spanId,
4440
+ traceId: params.traceId,
4332
4441
  type: "sdk-function",
4333
4442
  source: "typescript-sdk-function",
4334
4443
  sourceTraceId: params.traceId,