bitfab 0.12.2 → 0.13.1

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
@@ -467,6 +467,38 @@ interface CurrentSpan {
467
467
  */
468
468
  setPrompt(prompt: string): void;
469
469
  }
470
+ /**
471
+ * A detached handle to a previously-created trace, looked up by its
472
+ * caller-supplied id (the same id passed when the trace was started).
473
+ *
474
+ * Unlike `getCurrentTrace()`, this handle is not tied to AsyncLocalStorage —
475
+ * each method sends to the server immediately. Useful for adding context
476
+ * to a trace from a different process, request, or thread (e.g. a forked
477
+ * agent that wants to annotate the original conversation's trace).
478
+ */
479
+ interface DetachedTrace {
480
+ /** The caller-supplied trace id this handle resolves. */
481
+ readonly traceId: string;
482
+ /**
483
+ * Append a context entry to this trace. Each call adds one entry to the
484
+ * server-side contexts array; existing entries are preserved.
485
+ *
486
+ * Returns a promise that the caller may await for confirmation, or ignore
487
+ * to fire-and-forget. The pending request is tracked so `flushTraces()`
488
+ * waits for it.
489
+ */
490
+ addContext(context: Record<string, unknown>): Promise<unknown>;
491
+ /**
492
+ * Merge metadata into this trace. Server-side shallow-merges the new keys
493
+ * into the existing metadata object; existing keys are preserved unless
494
+ * overwritten by the new values.
495
+ */
496
+ setMetadata(metadata: Record<string, unknown>): Promise<unknown>;
497
+ /**
498
+ * Set the sessionId for this trace. Replaces any existing sessionId.
499
+ */
500
+ setSessionId(sessionId: string): Promise<unknown>;
501
+ }
470
502
  /**
471
503
  * A handle to the current active trace, allowing trace-level context to be set.
472
504
  */
@@ -707,6 +739,27 @@ declare class Bitfab {
707
739
  * @returns A wrapped function with the same signature that creates spans for inputs and outputs
708
740
  */
709
741
  withSpan<TArgs extends unknown[], TReturn>(traceFunctionKey: string, optionsOrFn: SpanOptions | ((...args: TArgs) => TReturn), maybeFn?: (...args: TArgs) => TReturn): (...args: TArgs) => TReturn;
742
+ /**
743
+ * Get a detached handle to a previously-created trace, looked up by the
744
+ * caller-supplied id (the same id passed at trace creation).
745
+ *
746
+ * The returned handle is not tied to AsyncLocalStorage — each method sends
747
+ * to the server immediately. Useful for adding context to a trace from a
748
+ * different process or thread than the one that created it.
749
+ *
750
+ * Throws synchronously if `traceId` is malformed (empty, too long, or
751
+ * contains characters outside `[a-zA-Z0-9_\-.:]`). Server returns 404 if
752
+ * no trace exists with that id in the org; the failure surfaces as a
753
+ * logged warning (fire-and-forget) or via the awaited promise.
754
+ *
755
+ * Example:
756
+ * ```typescript
757
+ * const trace = client.getTrace("order_abc_123");
758
+ * await trace.addContext({ refund_status: "approved" });
759
+ * await trace.setMetadata({ region: "us-west" });
760
+ * ```
761
+ */
762
+ getTrace(traceId: string): DetachedTrace;
710
763
  /**
711
764
  * Get a function wrapper for a specific trace function key.
712
765
  *
@@ -822,7 +875,7 @@ declare class BitfabFunction {
822
875
  /**
823
876
  * SDK version from package.json (injected at build time)
824
877
  */
825
- declare const __version__ = "0.12.2";
878
+ declare const __version__ = "0.13.1";
826
879
 
827
880
  /**
828
881
  * Constants for the Bitfab SDK.
@@ -832,4 +885,4 @@ declare const __version__ = "0.12.2";
832
885
  */
833
886
  declare const DEFAULT_SERVICE_URL = "https://bitfab.ai";
834
887
 
835
- export { type ActiveSpanContext, type AllowedEnvVars, type BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, type BitfabConfig, BitfabError, BitfabFunction, BitfabLangGraphCallbackHandler, BitfabOpenAITracingProcessor, type CodeChangeFile, type CurrentSpan, type CurrentTrace, DEFAULT_SERVICE_URL, type MockStrategy, type ProviderDefinition, type ReplayItem, type ReplayOptions, type ReplayResult, type SpanOptions, type SpanType, type TokenUsage, type TraceResponse, type TracingProcessor, type WrapBAMLOptions, type WrappedBamlFn, __version__, flushTraces, getCurrentSpan, getCurrentTrace };
888
+ export { type ActiveSpanContext, type AllowedEnvVars, type BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, type BitfabConfig, BitfabError, BitfabFunction, BitfabLangGraphCallbackHandler, BitfabOpenAITracingProcessor, type CodeChangeFile, type CurrentSpan, type CurrentTrace, DEFAULT_SERVICE_URL, type DetachedTrace, type MockStrategy, type ProviderDefinition, type ReplayItem, type ReplayOptions, type ReplayResult, type SpanOptions, type SpanType, type TokenUsage, type TraceResponse, type TracingProcessor, type WrapBAMLOptions, type WrappedBamlFn, __version__, flushTraces, getCurrentSpan, getCurrentTrace };
package/dist/index.d.ts CHANGED
@@ -467,6 +467,38 @@ interface CurrentSpan {
467
467
  */
468
468
  setPrompt(prompt: string): void;
469
469
  }
470
+ /**
471
+ * A detached handle to a previously-created trace, looked up by its
472
+ * caller-supplied id (the same id passed when the trace was started).
473
+ *
474
+ * Unlike `getCurrentTrace()`, this handle is not tied to AsyncLocalStorage —
475
+ * each method sends to the server immediately. Useful for adding context
476
+ * to a trace from a different process, request, or thread (e.g. a forked
477
+ * agent that wants to annotate the original conversation's trace).
478
+ */
479
+ interface DetachedTrace {
480
+ /** The caller-supplied trace id this handle resolves. */
481
+ readonly traceId: string;
482
+ /**
483
+ * Append a context entry to this trace. Each call adds one entry to the
484
+ * server-side contexts array; existing entries are preserved.
485
+ *
486
+ * Returns a promise that the caller may await for confirmation, or ignore
487
+ * to fire-and-forget. The pending request is tracked so `flushTraces()`
488
+ * waits for it.
489
+ */
490
+ addContext(context: Record<string, unknown>): Promise<unknown>;
491
+ /**
492
+ * Merge metadata into this trace. Server-side shallow-merges the new keys
493
+ * into the existing metadata object; existing keys are preserved unless
494
+ * overwritten by the new values.
495
+ */
496
+ setMetadata(metadata: Record<string, unknown>): Promise<unknown>;
497
+ /**
498
+ * Set the sessionId for this trace. Replaces any existing sessionId.
499
+ */
500
+ setSessionId(sessionId: string): Promise<unknown>;
501
+ }
470
502
  /**
471
503
  * A handle to the current active trace, allowing trace-level context to be set.
472
504
  */
@@ -707,6 +739,27 @@ declare class Bitfab {
707
739
  * @returns A wrapped function with the same signature that creates spans for inputs and outputs
708
740
  */
709
741
  withSpan<TArgs extends unknown[], TReturn>(traceFunctionKey: string, optionsOrFn: SpanOptions | ((...args: TArgs) => TReturn), maybeFn?: (...args: TArgs) => TReturn): (...args: TArgs) => TReturn;
742
+ /**
743
+ * Get a detached handle to a previously-created trace, looked up by the
744
+ * caller-supplied id (the same id passed at trace creation).
745
+ *
746
+ * The returned handle is not tied to AsyncLocalStorage — each method sends
747
+ * to the server immediately. Useful for adding context to a trace from a
748
+ * different process or thread than the one that created it.
749
+ *
750
+ * Throws synchronously if `traceId` is malformed (empty, too long, or
751
+ * contains characters outside `[a-zA-Z0-9_\-.:]`). Server returns 404 if
752
+ * no trace exists with that id in the org; the failure surfaces as a
753
+ * logged warning (fire-and-forget) or via the awaited promise.
754
+ *
755
+ * Example:
756
+ * ```typescript
757
+ * const trace = client.getTrace("order_abc_123");
758
+ * await trace.addContext({ refund_status: "approved" });
759
+ * await trace.setMetadata({ region: "us-west" });
760
+ * ```
761
+ */
762
+ getTrace(traceId: string): DetachedTrace;
710
763
  /**
711
764
  * Get a function wrapper for a specific trace function key.
712
765
  *
@@ -822,7 +875,7 @@ declare class BitfabFunction {
822
875
  /**
823
876
  * SDK version from package.json (injected at build time)
824
877
  */
825
- declare const __version__ = "0.12.2";
878
+ declare const __version__ = "0.13.1";
826
879
 
827
880
  /**
828
881
  * Constants for the Bitfab SDK.
@@ -832,4 +885,4 @@ declare const __version__ = "0.12.2";
832
885
  */
833
886
  declare const DEFAULT_SERVICE_URL = "https://bitfab.ai";
834
887
 
835
- export { type ActiveSpanContext, type AllowedEnvVars, type BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, type BitfabConfig, BitfabError, BitfabFunction, BitfabLangGraphCallbackHandler, BitfabOpenAITracingProcessor, type CodeChangeFile, type CurrentSpan, type CurrentTrace, DEFAULT_SERVICE_URL, type MockStrategy, type ProviderDefinition, type ReplayItem, type ReplayOptions, type ReplayResult, type SpanOptions, type SpanType, type TokenUsage, type TraceResponse, type TracingProcessor, type WrapBAMLOptions, type WrappedBamlFn, __version__, flushTraces, getCurrentSpan, getCurrentTrace };
888
+ export { type ActiveSpanContext, type AllowedEnvVars, type BamlExecutionResult, Bitfab, BitfabClaudeAgentHandler, type BitfabConfig, BitfabError, BitfabFunction, BitfabLangGraphCallbackHandler, BitfabOpenAITracingProcessor, type CodeChangeFile, type CurrentSpan, type CurrentTrace, DEFAULT_SERVICE_URL, type DetachedTrace, type MockStrategy, type ProviderDefinition, type ReplayItem, type ReplayOptions, type ReplayResult, type SpanOptions, type SpanType, type TokenUsage, type TraceResponse, type TracingProcessor, type WrapBAMLOptions, type WrappedBamlFn, __version__, flushTraces, getCurrentSpan, getCurrentTrace };
package/dist/index.js CHANGED
@@ -6,13 +6,13 @@ import {
6
6
  BitfabOpenAITracingProcessor,
7
7
  getCurrentSpan,
8
8
  getCurrentTrace
9
- } from "./chunk-3HBV4GQO.js";
9
+ } from "./chunk-HIDM5UYA.js";
10
10
  import {
11
11
  BitfabError,
12
12
  DEFAULT_SERVICE_URL,
13
13
  __version__,
14
14
  flushTraces
15
- } from "./chunk-HUSTKOQI.js";
15
+ } from "./chunk-3JCQD7E5.js";
16
16
  export {
17
17
  Bitfab,
18
18
  BitfabClaudeAgentHandler,
package/dist/node.cjs CHANGED
@@ -81,7 +81,7 @@ var __version__;
81
81
  var init_version_generated = __esm({
82
82
  "src/version.generated.ts"() {
83
83
  "use strict";
84
- __version__ = "0.12.2";
84
+ __version__ = "0.13.1";
85
85
  }
86
86
  });
87
87
 
@@ -151,7 +151,8 @@ var init_http = __esm({
151
151
  this.timeout = config.timeout ?? 12e4;
152
152
  }
153
153
  /**
154
- * Make an HTTP POST request to the Bitfab API.
154
+ * Make an HTTP request to the Bitfab API. Defaults to POST; pass
155
+ * `options.method` to use a different verb (e.g. "PATCH").
155
156
  *
156
157
  * @param endpoint - The API endpoint (without base URL)
157
158
  * @param payload - The request body
@@ -162,6 +163,7 @@ var init_http = __esm({
162
163
  async request(endpoint, payload, options) {
163
164
  const url = `${this.serviceUrl}${endpoint}`;
164
165
  const timeout = options?.timeout ?? this.timeout;
166
+ const method = options?.method ?? "POST";
165
167
  const controller = new AbortController();
166
168
  const timeoutId = setTimeout(() => controller.abort(), timeout);
167
169
  let body;
@@ -182,7 +184,7 @@ var init_http = __esm({
182
184
  }
183
185
  try {
184
186
  const response = await fetch(url, {
185
- method: "POST",
187
+ method,
186
188
  headers: {
187
189
  "Content-Type": "application/json",
188
190
  Authorization: `Bearer ${this.apiKey}`
@@ -281,6 +283,22 @@ var init_http = __esm({
281
283
  }
282
284
  });
283
285
  }
286
+ /**
287
+ * Partial update of an existing external trace identified by sourceTraceId.
288
+ * Used by the detached `client.getTrace(id)` handle. Fire-and-forget;
289
+ * returns a tracked promise that callers may optionally await.
290
+ */
291
+ patchTrace(sourceTraceId, payload) {
292
+ const endpoint = `/api/sdk/externalTraces/${encodeURIComponent(sourceTraceId)}`;
293
+ return awaitOnExit(
294
+ this.request(endpoint, payload, { method: "PATCH" })
295
+ ).catch((error) => {
296
+ try {
297
+ console.error("Bitfab: Failed to patch trace:", error);
298
+ } catch {
299
+ }
300
+ });
301
+ }
284
302
  /**
285
303
  * Start a replay session by fetching historical traces.
286
304
  * Blocking call — creates a test run and returns lightweight item references.
@@ -410,15 +428,43 @@ var init_replayContext = __esm({
410
428
  });
411
429
 
412
430
  // src/serialize.ts
431
+ function describeValue(value) {
432
+ try {
433
+ const ctorName = value?.constructor?.name;
434
+ if (ctorName && ctorName !== "Object") {
435
+ return ctorName;
436
+ }
437
+ } catch {
438
+ }
439
+ return typeof value;
440
+ }
441
+ function unserializableStub(value, reason) {
442
+ let summary;
443
+ try {
444
+ summary = `<unserializable: ${describeValue(value)} (${reason})>`;
445
+ } catch {
446
+ summary = `<unserializable (${reason})>`;
447
+ }
448
+ return { json: summary };
449
+ }
413
450
  function serializeValue(value) {
414
451
  try {
415
452
  const { json, meta } = import_superjson.default.serialize(value);
453
+ let size;
454
+ try {
455
+ size = JSON.stringify(json).length;
456
+ } catch {
457
+ return unserializableStub(value, "stringify_failed_after_superjson");
458
+ }
459
+ if (size > MAX_SERIALIZED_BYTES) {
460
+ return unserializableStub(value, `too_large_${size}_bytes`);
461
+ }
416
462
  return meta ? { json, meta } : { json };
417
463
  } catch {
418
464
  try {
419
465
  return { json: JSON.parse(JSON.stringify(value)) };
420
466
  } catch {
421
- return { json: String(value) };
467
+ return unserializableStub(value, "json_stringify_failed");
422
468
  }
423
469
  }
424
470
  }
@@ -431,11 +477,12 @@ function deserializeValue(serialized) {
431
477
  meta: serialized.meta
432
478
  });
433
479
  }
434
- var import_superjson;
480
+ var import_superjson, MAX_SERIALIZED_BYTES;
435
481
  var init_serialize = __esm({
436
482
  "src/serialize.ts"() {
437
483
  "use strict";
438
484
  import_superjson = __toESM(require("superjson"), 1);
485
+ MAX_SERIALIZED_BYTES = 512e3;
439
486
  }
440
487
  });
441
488
 
@@ -2227,6 +2274,23 @@ function extractContextFromCollector(collector) {
2227
2274
  return null;
2228
2275
  }
2229
2276
  }
2277
+ var TRACE_ID_PATTERN = /^[a-zA-Z0-9_\-.:]+$/;
2278
+ var TRACE_ID_MAX_LENGTH = 256;
2279
+ function validateTraceId(traceId) {
2280
+ if (typeof traceId !== "string" || traceId.length === 0) {
2281
+ throw new BitfabError("traceId is required and must be a non-empty string");
2282
+ }
2283
+ if (traceId.length > TRACE_ID_MAX_LENGTH) {
2284
+ throw new BitfabError(
2285
+ `traceId must be ${TRACE_ID_MAX_LENGTH} characters or fewer`
2286
+ );
2287
+ }
2288
+ if (!TRACE_ID_PATTERN.test(traceId)) {
2289
+ throw new BitfabError(
2290
+ `traceId may only contain letters, digits, "_", "-", ".", ":"`
2291
+ );
2292
+ }
2293
+ }
2230
2294
  var noOpSpan = {
2231
2295
  traceId: "",
2232
2296
  addContext() {
@@ -2768,6 +2832,61 @@ var Bitfab = class {
2768
2832
  };
2769
2833
  return wrappedFn;
2770
2834
  }
2835
+ /**
2836
+ * Get a detached handle to a previously-created trace, looked up by the
2837
+ * caller-supplied id (the same id passed at trace creation).
2838
+ *
2839
+ * The returned handle is not tied to AsyncLocalStorage — each method sends
2840
+ * to the server immediately. Useful for adding context to a trace from a
2841
+ * different process or thread than the one that created it.
2842
+ *
2843
+ * Throws synchronously if `traceId` is malformed (empty, too long, or
2844
+ * contains characters outside `[a-zA-Z0-9_\-.:]`). Server returns 404 if
2845
+ * no trace exists with that id in the org; the failure surfaces as a
2846
+ * logged warning (fire-and-forget) or via the awaited promise.
2847
+ *
2848
+ * Example:
2849
+ * ```typescript
2850
+ * const trace = client.getTrace("order_abc_123");
2851
+ * await trace.addContext({ refund_status: "approved" });
2852
+ * await trace.setMetadata({ region: "us-west" });
2853
+ * ```
2854
+ */
2855
+ getTrace(traceId) {
2856
+ validateTraceId(traceId);
2857
+ return {
2858
+ traceId,
2859
+ addContext: (context) => {
2860
+ if (!this.enabled) {
2861
+ return Promise.resolve();
2862
+ }
2863
+ if (typeof context !== "object" || context === null) {
2864
+ return Promise.resolve();
2865
+ }
2866
+ return this.httpClient.patchTrace(traceId, {
2867
+ appendContexts: [context]
2868
+ });
2869
+ },
2870
+ setMetadata: (metadata) => {
2871
+ if (!this.enabled) {
2872
+ return Promise.resolve();
2873
+ }
2874
+ if (typeof metadata !== "object" || metadata === null) {
2875
+ return Promise.resolve();
2876
+ }
2877
+ return this.httpClient.patchTrace(traceId, { mergeMetadata: metadata });
2878
+ },
2879
+ setSessionId: (sessionId) => {
2880
+ if (!this.enabled) {
2881
+ return Promise.resolve();
2882
+ }
2883
+ if (typeof sessionId !== "string" || sessionId.length === 0) {
2884
+ return Promise.resolve();
2885
+ }
2886
+ return this.httpClient.patchTrace(traceId, { setSessionId: sessionId });
2887
+ }
2888
+ };
2889
+ }
2771
2890
  /**
2772
2891
  * Get a function wrapper for a specific trace function key.
2773
2892
  *