bitfab 0.21.1 → 0.21.2

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.
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-EQI6ZJC3.js";
11
11
 
12
12
  // src/version.generated.ts
13
- var __version__ = "0.21.1";
13
+ var __version__ = "0.21.2";
14
14
 
15
15
  // src/constants.ts
16
16
  var DEFAULT_SERVICE_URL = "https://bitfab.ai";
@@ -479,6 +479,13 @@ var BitfabClaudeAgentHandler = class {
479
479
  this.currentLlmHistorySnapshot = [];
480
480
  // Subagent tracking
481
481
  this.activeSubagentSpans = /* @__PURE__ */ new Map();
482
+ // Synthetic root span (handler-only replay). When an `input` is supplied to
483
+ // wrapQuery/wrapResponse, the handler emits a root `agent` span carrying that
484
+ // input, so a handler-instrumented run is replayable WITHOUT an enclosing
485
+ // withSpan — matching the LangGraph handler, which records the graph input as
486
+ // its root. The prompt is not present anywhere in the message stream, so it
487
+ // must be handed in explicitly.
488
+ this.hasRootInput = false;
482
489
  this.httpClient = new HttpClient({
483
490
  apiKey: config.apiKey,
484
491
  serviceUrl: config.serviceUrl ?? DEFAULT_SERVICE_URL,
@@ -513,7 +520,30 @@ var BitfabClaudeAgentHandler = class {
513
520
  return subagentSpanId;
514
521
  }
515
522
  }
516
- return this.activeContext?.spanId ?? this.rootSpanId ?? null;
523
+ return this.rootSpanId ?? this.activeContext?.spanId ?? null;
524
+ }
525
+ // Emit the synthetic root `agent` span once, before any child spans. No-op
526
+ // unless an `input` was supplied AND there is no enclosing withSpan (in which
527
+ // case that outer span is already the replayable root).
528
+ maybeStartRootSpan() {
529
+ if (!this.hasRootInput || this.rootSpanId !== null) {
530
+ return;
531
+ }
532
+ this.ensureTrace();
533
+ if (this.activeContext !== null) {
534
+ return;
535
+ }
536
+ const spanId = crypto.randomUUID();
537
+ this.startSpan(spanId, this.traceFunctionKey, "agent", this.rootInput, null);
538
+ this.rootSpanId = spanId;
539
+ }
540
+ completeRootSpan() {
541
+ if (this.rootSpanId === null) {
542
+ return;
543
+ }
544
+ const spanId = this.rootSpanId;
545
+ this.rootSpanId = null;
546
+ this.completeSpan(spanId, this.rootOutput);
517
547
  }
518
548
  // ── span helpers ─────────────────────────────────────────────
519
549
  startSpan(spanId, name, spanType, inputData, parentId) {
@@ -709,26 +739,53 @@ var BitfabClaudeAgentHandler = class {
709
739
  return options;
710
740
  }
711
741
  /**
712
- * Wrap a `ClaudeSDKClient.receiveResponse()` stream to capture LLM turns.
742
+ * Wrap any Claude Agent SDK message stream to capture LLM turns.
713
743
  *
714
- * Yields every message unchanged while capturing AssistantMessage
715
- * content as LLM turn spans.
744
+ * Yields every message unchanged while capturing assistant message
745
+ * content as LLM turn spans. Kept for naming symmetry with the Python
746
+ * SDK's `wrapResponse` (which wraps `ClaudeSDKClient.receiveResponse()`);
747
+ * in TypeScript, prefer `wrapQuery` around `query()`.
748
+ *
749
+ * Pass `{ input }` (the prompt) to record a replayable root span — see
750
+ * `wrapQuery`.
716
751
  */
717
- async *wrapResponse(stream) {
752
+ async *wrapResponse(stream, opts) {
753
+ this.setRootInput(opts);
718
754
  yield* this.processStream(stream);
719
755
  }
720
756
  /**
721
757
  * Wrap a `query()` async iterator to capture LLM turns.
722
758
  *
723
- * Same as `wrapResponse` but for the simpler `query()` API
724
- * which does not support hooks (no tool/subagent spans).
759
+ * Tool and subagent spans are captured separately via the hooks injected
760
+ * by `instrumentOptions` into the `options` passed to `query()`.
761
+ *
762
+ * Pass `{ input }` — the prompt (or the serializable args that produced it)
763
+ * — to make a handler-only run replayable: the handler records a root `agent`
764
+ * span with that input, so `replay(key, fn)` can re-feed it. Omit it only
765
+ * when an enclosing `withSpan` already supplies the replayable root.
766
+ *
767
+ * ```typescript
768
+ * handler.wrapQuery(query({ prompt, options }), { input: prompt })
769
+ * ```
725
770
  */
726
- async *wrapQuery(stream) {
771
+ async *wrapQuery(stream, opts) {
772
+ this.setRootInput(opts);
727
773
  yield* this.processStream(stream);
728
774
  }
775
+ setRootInput(opts) {
776
+ if (opts && opts.input !== void 0) {
777
+ this.hasRootInput = true;
778
+ this.rootInput = opts.input;
779
+ } else {
780
+ this.hasRootInput = false;
781
+ this.rootInput = void 0;
782
+ }
783
+ this.rootOutput = void 0;
784
+ }
729
785
  // ── stream processing ────────────────────────────────────────
730
786
  async *processStream(stream) {
731
787
  try {
788
+ this.maybeStartRootSpan();
732
789
  for await (const message of stream) {
733
790
  try {
734
791
  this.processMessage(message);
@@ -739,6 +796,7 @@ var BitfabClaudeAgentHandler = class {
739
796
  } finally {
740
797
  try {
741
798
  this.flushLlmTurn();
799
+ this.completeRootSpan();
742
800
  this.sendTraceCompletion();
743
801
  } catch {
744
802
  }
@@ -746,18 +804,19 @@ var BitfabClaudeAgentHandler = class {
746
804
  }
747
805
  }
748
806
  processMessage(message) {
749
- const typeName = message.constructor?.name ?? "";
750
- if (typeName === "AssistantMessage") {
807
+ const typeName = message.type;
808
+ if (typeName === "assistant") {
751
809
  this.handleAssistantMessage(message);
752
- } else if (typeName === "UserMessage") {
810
+ } else if (typeName === "user") {
753
811
  this.handleUserMessage(message);
754
- } else if (typeName === "ResultMessage") {
812
+ } else if (typeName === "result") {
755
813
  this.handleResultMessage(message);
756
814
  }
757
815
  }
758
816
  handleAssistantMessage(message) {
759
817
  this.ensureTrace();
760
- const messageId = message.message_id;
818
+ const inner = message.message ?? {};
819
+ const messageId = inner.id ?? message.uuid;
761
820
  if (messageId !== this.currentLlmMessageId) {
762
821
  this.flushLlmTurn();
763
822
  this.conversationHistory.push(...this.pendingMessages);
@@ -765,26 +824,27 @@ var BitfabClaudeAgentHandler = class {
765
824
  this.currentLlmSpanId = crypto.randomUUID();
766
825
  this.currentLlmMessageId = messageId ?? null;
767
826
  this.currentLlmContent = [];
768
- this.currentLlmModel = message.model ?? null;
827
+ this.currentLlmModel = inner.model ?? null;
769
828
  this.currentLlmUsage = {};
770
829
  this.currentLlmStartedAt = nowIso();
771
830
  this.currentLlmHistorySnapshot = [...this.conversationHistory];
772
831
  }
773
- const content = message.content;
832
+ const content = inner.content;
774
833
  if (Array.isArray(content)) {
775
834
  this.currentLlmContent.push(...extractContentBlocks(content));
776
835
  }
777
- const usage = extractUsage(message);
836
+ const usage = extractUsage(inner);
778
837
  if (Object.keys(usage).length > 0) {
779
838
  Object.assign(this.currentLlmUsage, usage);
780
839
  }
781
- const model = message.model;
840
+ const model = inner.model;
782
841
  if (model) {
783
842
  this.currentLlmModel = model;
784
843
  }
785
844
  }
786
845
  handleUserMessage(message) {
787
- const content = message.content;
846
+ const inner = message.message ?? {};
847
+ const content = inner.content;
788
848
  const toolUseResult = message.tool_use_result;
789
849
  if (toolUseResult !== void 0) {
790
850
  this.pendingMessages.push({
@@ -801,6 +861,10 @@ var BitfabClaudeAgentHandler = class {
801
861
  }
802
862
  handleResultMessage(message) {
803
863
  this.flushLlmTurn();
864
+ if (message.result !== void 0) {
865
+ this.rootOutput = message.result;
866
+ }
867
+ this.completeRootSpan();
804
868
  const metadata = {};
805
869
  for (const attr of [
806
870
  "num_turns",
@@ -864,6 +928,9 @@ var BitfabClaudeAgentHandler = class {
864
928
  this.runToSpan.clear();
865
929
  this.traceId = null;
866
930
  this.rootSpanId = null;
931
+ this.hasRootInput = false;
932
+ this.rootInput = void 0;
933
+ this.rootOutput = void 0;
867
934
  this.activeContext = null;
868
935
  this.traceStartedAt = null;
869
936
  this.conversationHistory = [];
@@ -2457,14 +2524,15 @@ var Bitfab = class {
2457
2524
  * execution as Bitfab spans with proper parent-child hierarchy.
2458
2525
  *
2459
2526
  * ```typescript
2527
+ * import { query } from "@anthropic-ai/claude-agent-sdk";
2528
+ *
2460
2529
  * const handler = client.getClaudeAgentHandler("my-agent");
2461
2530
  * const options = handler.instrumentOptions({
2462
2531
  * model: "claude-sonnet-4-5-...",
2463
2532
  * });
2464
- * const sdkClient = new ClaudeSDKClient(options);
2465
- * await sdkClient.connect();
2466
- * await sdkClient.query("Do something");
2467
- * for await (const msg of handler.wrapResponse(sdkClient.receiveResponse())) {
2533
+ * for await (const msg of handler.wrapQuery(
2534
+ * query({ prompt: "Do something", options })
2535
+ * )) {
2468
2536
  * // process messages
2469
2537
  * }
2470
2538
  * ```
@@ -3161,4 +3229,4 @@ export {
3161
3229
  BitfabFunction,
3162
3230
  finalizers
3163
3231
  };
3164
- //# sourceMappingURL=chunk-75LZO6JS.js.map
3232
+ //# sourceMappingURL=chunk-3YKMCCDV.js.map