@bitfab/sdk 0.53.0 → 0.53.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.
package/dist/index.cjs CHANGED
@@ -42,7 +42,7 @@ var __version__, __packageName__;
42
42
  var init_version_generated = __esm({
43
43
  "src/version.generated.ts"() {
44
44
  "use strict";
45
- __version__ = "0.53.0";
45
+ __version__ = "0.53.2";
46
46
  __packageName__ = "@bitfab/sdk";
47
47
  }
48
48
  });
@@ -507,8 +507,8 @@ function encodePayloadBody(payload) {
507
507
  const marker = { error: `payload_serialize_failed: ${message}` };
508
508
  return { body: JSON.stringify(marker), dropped, value: marker };
509
509
  }
510
- const isRecord2 = typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized);
511
- if (dropped.length > 0 && isRecord2) {
510
+ const isRecord4 = typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized);
511
+ if (dropped.length > 0 && isRecord4) {
512
512
  const obj = sanitized;
513
513
  const existing = Array.isArray(obj.errors) ? obj.errors : [];
514
514
  obj.errors = [
@@ -525,7 +525,7 @@ function encodePayloadBody(payload) {
525
525
  return {
526
526
  body: JSON.stringify(sanitized),
527
527
  dropped,
528
- value: isRecord2 ? sanitized : void 0
528
+ value: isRecord4 ? sanitized : void 0
529
529
  };
530
530
  }
531
531
  }
@@ -537,6 +537,456 @@ var init_serializePayload = __esm({
537
537
  }
538
538
  });
539
539
 
540
+ // src/policyRefresh.ts
541
+ var PolicyRefresh;
542
+ var init_policyRefresh = __esm({
543
+ "src/policyRefresh.ts"() {
544
+ "use strict";
545
+ PolicyRefresh = class {
546
+ constructor() {
547
+ this.refreshAfter = 0;
548
+ }
549
+ due() {
550
+ return this.inFlight === void 0 && Date.now() >= this.refreshAfter;
551
+ }
552
+ hold(durationMs) {
553
+ this.refreshAfter = Date.now() + durationMs;
554
+ }
555
+ releaseHold() {
556
+ this.refreshAfter = 0;
557
+ }
558
+ run(read2, onLoaded, onFailed) {
559
+ if (!this.due()) {
560
+ return;
561
+ }
562
+ let request;
563
+ try {
564
+ request = read2().then(onLoaded).catch(onFailed).finally(() => {
565
+ this.inFlight = void 0;
566
+ });
567
+ } catch (error) {
568
+ onFailed(error);
569
+ return;
570
+ }
571
+ this.inFlight = request;
572
+ }
573
+ };
574
+ }
575
+ });
576
+
577
+ // src/spanOrigin.ts
578
+ function isFrameworkInstrumentation(value) {
579
+ return typeof value === "string" && FRAMEWORK_INSTRUMENTATION_SET.has(value);
580
+ }
581
+ function makeSpanOrigin(instrumentation) {
582
+ return {
583
+ name: SPAN_ORIGIN_NAME,
584
+ version: __version__,
585
+ instrumentation: { name: instrumentation }
586
+ };
587
+ }
588
+ function isRecord(value) {
589
+ return typeof value === "object" && value !== null && !Array.isArray(value);
590
+ }
591
+ function spanOriginOf(payload) {
592
+ const rawSpan = payload.rawSpan;
593
+ if (!isRecord(rawSpan)) {
594
+ return void 0;
595
+ }
596
+ const origin = rawSpan.span_origin;
597
+ if (!isRecord(origin) || !isRecord(origin.instrumentation)) {
598
+ return void 0;
599
+ }
600
+ const { name, version } = origin;
601
+ const instrumentation = origin.instrumentation.name;
602
+ if (typeof name !== "string" || typeof version !== "string" || typeof instrumentation !== "string" || !SPAN_INSTRUMENTATIONS.includes(instrumentation)) {
603
+ return void 0;
604
+ }
605
+ return {
606
+ name,
607
+ version,
608
+ instrumentation: { name: instrumentation }
609
+ };
610
+ }
611
+ function spanInstrumentationOf(payload) {
612
+ return spanOriginOf(payload)?.instrumentation.name;
613
+ }
614
+ function recordedByFramework(payload) {
615
+ return isFrameworkInstrumentation(spanInstrumentationOf(payload));
616
+ }
617
+ var SPAN_ORIGIN_NAME, SPAN_INSTRUMENTATIONS, FRAMEWORK_INSTRUMENTATIONS, FRAMEWORK_INSTRUMENTATION_SET;
618
+ var init_spanOrigin = __esm({
619
+ "src/spanOrigin.ts"() {
620
+ "use strict";
621
+ init_constants();
622
+ SPAN_ORIGIN_NAME = "bitfab.sdk.typescript";
623
+ SPAN_INSTRUMENTATIONS = [
624
+ "span",
625
+ "trace",
626
+ "openai-agents",
627
+ "langgraph",
628
+ "claude-agent-sdk",
629
+ "vercel-ai"
630
+ ];
631
+ FRAMEWORK_INSTRUMENTATIONS = [
632
+ "openai-agents",
633
+ "langgraph",
634
+ "claude-agent-sdk",
635
+ "vercel-ai"
636
+ ];
637
+ FRAMEWORK_INSTRUMENTATION_SET = new Set(
638
+ FRAMEWORK_INSTRUMENTATIONS
639
+ );
640
+ }
641
+ });
642
+
643
+ // src/unrefTimer.ts
644
+ function unrefTimer(timer) {
645
+ const handle = timer;
646
+ if (typeof handle.unref === "function") {
647
+ handle.unref();
648
+ }
649
+ }
650
+ var init_unrefTimer = __esm({
651
+ "src/unrefTimer.ts"() {
652
+ "use strict";
653
+ }
654
+ });
655
+
656
+ // src/simulationPlan.ts
657
+ function planWaitSliceMs(timeoutMs) {
658
+ return Math.min(SIMULATION_PLAN_READ_TIMEOUT_MS, Math.max(timeoutMs, 0) / 2);
659
+ }
660
+ function parseSimulationPlan(body) {
661
+ if (typeof body !== "object" || body === null) {
662
+ return null;
663
+ }
664
+ const nodes = body.nodes;
665
+ if (!Array.isArray(nodes)) {
666
+ return null;
667
+ }
668
+ const contentOff = /* @__PURE__ */ new Map();
669
+ for (const node of nodes) {
670
+ if (typeof node !== "object" || node === null) {
671
+ continue;
672
+ }
673
+ const { traceFunctionKey, name, captureContent } = node;
674
+ if (typeof traceFunctionKey !== "string" || typeof name !== "string" || typeof captureContent !== "boolean") {
675
+ continue;
676
+ }
677
+ if (captureContent) {
678
+ continue;
679
+ }
680
+ const names = contentOff.get(traceFunctionKey) ?? /* @__PURE__ */ new Set();
681
+ names.add(name);
682
+ contentOff.set(traceFunctionKey, names);
683
+ }
684
+ return contentOff;
685
+ }
686
+ function traceIdOf(payload) {
687
+ const traceId = payload.traceId;
688
+ if (typeof traceId === "string") {
689
+ return traceId;
690
+ }
691
+ const id = payload.id;
692
+ return typeof id === "string" ? id : void 0;
693
+ }
694
+ function isRecord2(value) {
695
+ return typeof value === "object" && value !== null;
696
+ }
697
+ function rawSpanOf(payload) {
698
+ return isRecord2(payload.rawSpan) ? payload.rawSpan : void 0;
699
+ }
700
+ function spanDataOf(payload) {
701
+ const spanData = rawSpanOf(payload)?.span_data;
702
+ return isRecord2(spanData) ? spanData : void 0;
703
+ }
704
+ function spanNameOf(payload) {
705
+ const name = spanDataOf(payload)?.name;
706
+ return typeof name === "string" ? name : void 0;
707
+ }
708
+ function stripContent(payload) {
709
+ const rawSpan = rawSpanOf(payload);
710
+ const spanData = spanDataOf(payload);
711
+ if (rawSpan === void 0 || spanData === void 0) {
712
+ return payload;
713
+ }
714
+ const kept = { ...spanData };
715
+ for (const key of CONTENT_KEYS) {
716
+ delete kept[key];
717
+ }
718
+ kept[CONTENT_OFF_KEY] = true;
719
+ return { ...payload, rawSpan: { ...rawSpan, span_data: kept } };
720
+ }
721
+ function simulationPlanEnvDisabled() {
722
+ const value = readEnv(DISABLE_SIMULATION_PLAN_ENV);
723
+ return typeof value === "string" && value.trim() !== "";
724
+ }
725
+ function missingSimulationPlanEndpoint(error) {
726
+ return error instanceof BitfabError && error.status === 404;
727
+ }
728
+ var SIMULATION_PLAN_REFRESH_MS, SIMULATION_PLAN_RETRY_MS, SIMULATION_PLAN_READ_TIMEOUT_MS, SIMULATION_PLAN_MAX_HELD, DISABLE_SIMULATION_PLAN_ENV, ROOT_TRACE_FUNCTION_KEY_FIELD, CONTENT_OFF_KEY, CONTENT_KEYS, SimulationPlan;
729
+ var init_simulationPlan = __esm({
730
+ "src/simulationPlan.ts"() {
731
+ "use strict";
732
+ init_errors();
733
+ init_policyRefresh();
734
+ init_readEnv();
735
+ init_spanOrigin();
736
+ init_unrefTimer();
737
+ init_warnOnce();
738
+ SIMULATION_PLAN_REFRESH_MS = 6e4;
739
+ SIMULATION_PLAN_RETRY_MS = 1e4;
740
+ SIMULATION_PLAN_READ_TIMEOUT_MS = 5e3;
741
+ SIMULATION_PLAN_MAX_HELD = 1e3;
742
+ DISABLE_SIMULATION_PLAN_ENV = "BITFAB_DISABLE_SIM_PLAN";
743
+ ROOT_TRACE_FUNCTION_KEY_FIELD = "rootTraceFunctionKey";
744
+ CONTENT_OFF_KEY = "content_off_by_simulation_plan";
745
+ CONTENT_KEYS = ["input", "input_meta", "output", "output_meta"];
746
+ SimulationPlan = class {
747
+ constructor(source, enabled = true) {
748
+ this.source = source;
749
+ this.enabled = enabled;
750
+ this.policyRefresh = new PolicyRefresh();
751
+ this.retryPending = false;
752
+ this.waitingForFirstLoad = 0;
753
+ this.held = [];
754
+ this.droppedWhileHolding = 0;
755
+ this.stopped = false;
756
+ this.loaded = new Promise((resolve) => {
757
+ this.resolveLoaded = resolve;
758
+ });
759
+ }
760
+ disabled() {
761
+ return !this.enabled || simulationPlanEnvDisabled();
762
+ }
763
+ refresh() {
764
+ if (this.stopped || this.disabled()) {
765
+ return;
766
+ }
767
+ this.policyRefresh.run(
768
+ () => this.source.getSimulationPlan(),
769
+ (body) => {
770
+ const parsed = parseSimulationPlan(body);
771
+ if (parsed === null) {
772
+ this.scheduleRetry(
773
+ "sim-plan-unreadable",
774
+ "the sim plan response was not understood"
775
+ );
776
+ return;
777
+ }
778
+ this.markLoaded(parsed);
779
+ },
780
+ (error) => {
781
+ if (missingSimulationPlanEndpoint(error)) {
782
+ this.markLoaded(/* @__PURE__ */ new Map());
783
+ return;
784
+ }
785
+ this.scheduleRetry(
786
+ "sim-plan-unavailable",
787
+ `could not read the sim plan: ${error instanceof Error ? error.message : String(error)}`
788
+ );
789
+ }
790
+ );
791
+ }
792
+ markLoaded(parsed) {
793
+ if (this.stopped) {
794
+ return;
795
+ }
796
+ this.contentOffByKey = parsed;
797
+ this.policyRefresh.hold(SIMULATION_PLAN_REFRESH_MS);
798
+ this.retryPending = false;
799
+ this.clearRetryTimer();
800
+ this.resolveLoaded?.();
801
+ this.resolveLoaded = void 0;
802
+ this.releaseHeld();
803
+ }
804
+ scheduleRetry(warnKey, reason) {
805
+ if (this.stopped) {
806
+ return;
807
+ }
808
+ this.policyRefresh.hold(SIMULATION_PLAN_RETRY_MS);
809
+ if (this.contentOffByKey !== void 0) {
810
+ return;
811
+ }
812
+ this.retryPending = true;
813
+ warnOnce(
814
+ warnKey,
815
+ `${reason}. Spans are held until the first read succeeds; while a record is held it is retried every ${SIMULATION_PLAN_RETRY_MS / 1e3}s, otherwise on the next span.`
816
+ );
817
+ this.armRetry();
818
+ }
819
+ holdsWork() {
820
+ return this.held.length > 0 || this.waitingForFirstLoad > 0;
821
+ }
822
+ armRetry() {
823
+ if (this.stopped || !this.retryPending || this.retryTimer !== void 0 || !this.holdsWork()) {
824
+ return;
825
+ }
826
+ const timer = setTimeout(() => {
827
+ this.retryTimer = void 0;
828
+ this.policyRefresh.releaseHold();
829
+ this.refresh();
830
+ }, SIMULATION_PLAN_RETRY_MS);
831
+ unrefTimer(timer);
832
+ this.retryTimer = timer;
833
+ }
834
+ clearRetryTimer() {
835
+ if (this.retryTimer !== void 0) {
836
+ clearTimeout(this.retryTimer);
837
+ this.retryTimer = void 0;
838
+ }
839
+ }
840
+ syncRetryTimer() {
841
+ if (this.holdsWork()) {
842
+ return;
843
+ }
844
+ this.clearRetryTimer();
845
+ }
846
+ firstLoad() {
847
+ if (this.contentOffByKey !== void 0 || this.stopped || this.disabled()) {
848
+ return void 0;
849
+ }
850
+ return this.loaded;
851
+ }
852
+ send(payload, traceFunctionKey, submit) {
853
+ this.refresh();
854
+ if (this.stopped || this.disabled() || traceFunctionKey === void 0 || spanNameOf(payload) === void 0 || recordedByFramework(payload)) {
855
+ submit(payload);
856
+ return;
857
+ }
858
+ if (this.contentOffByKey === void 0) {
859
+ this.hold({ payload, traceFunctionKey, submit });
860
+ return;
861
+ }
862
+ this.submitEntry({ payload, traceFunctionKey, submit });
863
+ }
864
+ sendTrace(payload, submit) {
865
+ this.refresh();
866
+ if (this.stopped || this.disabled() || this.contentOffByKey !== void 0 || !this.holdsSpanOfTrace(traceIdOf(payload))) {
867
+ submit(payload);
868
+ return;
869
+ }
870
+ this.hold({ payload, traceFunctionKey: void 0, submit });
871
+ }
872
+ holdsSpanOfTrace(traceId) {
873
+ if (traceId === void 0) {
874
+ return false;
875
+ }
876
+ return this.held.some(
877
+ (entry) => entry.traceFunctionKey !== void 0 && traceIdOf(entry.payload) === traceId
878
+ );
879
+ }
880
+ hold(entry) {
881
+ this.held.push(entry);
882
+ while (this.held.length > SIMULATION_PLAN_MAX_HELD) {
883
+ this.held.shift();
884
+ this.droppedWhileHolding += 1;
885
+ }
886
+ this.armRetry();
887
+ }
888
+ submitEntry(entry) {
889
+ try {
890
+ entry.submit(
891
+ entry.traceFunctionKey === void 0 ? entry.payload : this.apply(entry.payload, entry.traceFunctionKey)
892
+ );
893
+ } catch (error) {
894
+ warnOnce(
895
+ "sim-plan-held-span-dropped",
896
+ `a span held for the sim plan was dropped when released: ${error instanceof Error ? error.message : String(error)}`
897
+ );
898
+ }
899
+ }
900
+ releaseHeld() {
901
+ const held = this.held;
902
+ this.held = [];
903
+ this.syncRetryTimer();
904
+ this.warnDropped();
905
+ for (const entry of held) {
906
+ this.submitEntry(entry);
907
+ }
908
+ }
909
+ warnDropped() {
910
+ if (this.droppedWhileHolding === 0) {
911
+ return;
912
+ }
913
+ const dropped = this.droppedWhileHolding;
914
+ this.droppedWhileHolding = 0;
915
+ warnOnce(
916
+ "sim-plan-held-overflow",
917
+ `${dropped} record(s) were dropped while the sim plan was still loading; at most ${SIMULATION_PLAN_MAX_HELD} are held per client.`
918
+ );
919
+ }
920
+ warnNeverLoaded() {
921
+ if (this.held.length === 0) {
922
+ return;
923
+ }
924
+ warnOnce(
925
+ "sim-plan-never-loaded",
926
+ `${this.held.length} record(s) were not sent because the sim plan never loaded`
927
+ );
928
+ }
929
+ async release(timeoutMs) {
930
+ this.refresh();
931
+ if (this.holdsWork()) {
932
+ await this.waitForFirstLoad(timeoutMs);
933
+ }
934
+ this.warnNeverLoaded();
935
+ this.warnDropped();
936
+ }
937
+ waitForFirstLoad(timeoutMs) {
938
+ const firstLoad = this.firstLoad();
939
+ if (firstLoad === void 0) {
940
+ return Promise.resolve();
941
+ }
942
+ this.waitingForFirstLoad += 1;
943
+ this.armRetry();
944
+ return new Promise((resolve) => {
945
+ let settled2 = false;
946
+ const finish = () => {
947
+ if (settled2) {
948
+ return;
949
+ }
950
+ settled2 = true;
951
+ this.waitingForFirstLoad -= 1;
952
+ this.syncRetryTimer();
953
+ resolve();
954
+ };
955
+ const timer = setTimeout(finish, Math.max(timeoutMs, 0));
956
+ void firstLoad.then(() => {
957
+ clearTimeout(timer);
958
+ finish();
959
+ });
960
+ });
961
+ }
962
+ stop() {
963
+ if (this.stopped) {
964
+ return;
965
+ }
966
+ this.stopped = true;
967
+ this.retryPending = false;
968
+ this.clearRetryTimer();
969
+ this.warnNeverLoaded();
970
+ this.held = [];
971
+ this.warnDropped();
972
+ }
973
+ contentOff(traceFunctionKey, spanName) {
974
+ return this.contentOffByKey?.get(traceFunctionKey)?.has(spanName) === true;
975
+ }
976
+ apply(payload, traceFunctionKey) {
977
+ if (recordedByFramework(payload)) {
978
+ return payload;
979
+ }
980
+ const name = spanNameOf(payload);
981
+ if (name === void 0 || !this.contentOff(traceFunctionKey, name)) {
982
+ return payload;
983
+ }
984
+ return stripContent(payload);
985
+ }
986
+ };
987
+ }
988
+ });
989
+
540
990
  // src/traceMetadata.ts
541
991
  function findRecord(traceId) {
542
992
  return activeRecords.get(traceId) ?? completedRecords.get(traceId);
@@ -653,19 +1103,6 @@ var init_transportTypes = __esm({
653
1103
  }
654
1104
  });
655
1105
 
656
- // src/unrefTimer.ts
657
- function unrefTimer(timer) {
658
- const handle = timer;
659
- if (typeof handle.unref === "function") {
660
- handle.unref();
661
- }
662
- }
663
- var init_unrefTimer = __esm({
664
- "src/unrefTimer.ts"() {
665
- "use strict";
666
- }
667
- });
668
-
669
1106
  // src/otel.ts
670
1107
  function readBoundedIntEnv(name, max, fallback, warnKey) {
671
1108
  const raw = readEnv(name);
@@ -1316,6 +1753,17 @@ __export(http_exports, {
1316
1753
  parseRetryAfterMs: () => parseRetryAfterMs,
1317
1754
  serializePayloadBody: () => serializePayloadBody
1318
1755
  });
1756
+ async function releaseHeldExternalSpans(timeoutMs) {
1757
+ const deadline = Date.now() + Math.max(timeoutMs, 0);
1758
+ for (const client of [...liveHttpClients]) {
1759
+ await client.releaseHeldExternalSpans?.(Math.max(0, deadline - Date.now()));
1760
+ }
1761
+ }
1762
+ function stopSimulationPlans() {
1763
+ for (const client of [...liveHttpClients]) {
1764
+ client.stopSimulationPlan?.();
1765
+ }
1766
+ }
1319
1767
  function awaitOnExit(promise) {
1320
1768
  pendingTracePromises.add(promise);
1321
1769
  void promise.finally(() => {
@@ -1326,7 +1774,10 @@ function awaitOnExit(promise) {
1326
1774
  }
1327
1775
  async function flushTraces(timeoutMs = 5e3) {
1328
1776
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1329
- const requestsFlushed = await awaitPendingRequests(timeoutMs);
1777
+ await releaseHeldExternalSpans(planWaitSliceMs(timeoutMs));
1778
+ const requestsFlushed = await awaitPendingRequests(
1779
+ Math.max(0, deadline - Date.now())
1780
+ );
1330
1781
  const transportsFlushed = await flushTraceTransports(
1331
1782
  Math.max(0, deadline - Date.now())
1332
1783
  );
@@ -1445,7 +1896,22 @@ function sourceTraceIdOf(payload) {
1445
1896
  const id = rawTrace?.id;
1446
1897
  return typeof id === "string" ? id : void 0;
1447
1898
  }
1448
- var REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS, REPLAY_COMPLETE_REQUEST_TIMEOUT_MS, OTLP_TRACES_ENDPOINT, RETRYABLE_STATUSES, EXIT_FLUSH_TIMEOUT_MS, DEFAULT_LIFECYCLE_TIMEOUT_MS2, pendingTracePromises, carrierSeq, HttpClient;
1899
+ function describeError(error) {
1900
+ return error instanceof Error ? error.message : String(error);
1901
+ }
1902
+ function warnDroppedExternalSpan(error) {
1903
+ warnOnce(
1904
+ "external-span-dropped",
1905
+ `a span was dropped because the send step failed: ${describeError(error)}`
1906
+ );
1907
+ }
1908
+ function warnDroppedExternalTrace(error) {
1909
+ warnOnce(
1910
+ "external-trace-dropped",
1911
+ `a trace was dropped because the send step failed: ${describeError(error)}`
1912
+ );
1913
+ }
1914
+ var REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS, REPLAY_COMPLETE_REQUEST_TIMEOUT_MS, OTLP_TRACES_ENDPOINT, RETRYABLE_STATUSES, EXIT_FLUSH_TIMEOUT_MS, DEFAULT_LIFECYCLE_TIMEOUT_MS2, pendingTracePromises, liveHttpClients, carrierSeq, HttpClient;
1449
1915
  var init_http = __esm({
1450
1916
  "src/http.ts"() {
1451
1917
  "use strict";
@@ -1454,6 +1920,7 @@ var init_http = __esm({
1454
1920
  init_errors();
1455
1921
  init_replayContext();
1456
1922
  init_serializePayload();
1923
+ init_simulationPlan();
1457
1924
  init_traceMetadata();
1458
1925
  init_transport();
1459
1926
  init_transportTypes();
@@ -1466,6 +1933,7 @@ var init_http = __esm({
1466
1933
  EXIT_FLUSH_TIMEOUT_MS = 5e3;
1467
1934
  DEFAULT_LIFECYCLE_TIMEOUT_MS2 = 3e4;
1468
1935
  pendingTracePromises = /* @__PURE__ */ new Set();
1936
+ liveHttpClients = /* @__PURE__ */ new Set();
1469
1937
  if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
1470
1938
  let isFlushing = false;
1471
1939
  process.on("beforeExit", () => {
@@ -1473,12 +1941,27 @@ var init_http = __esm({
1473
1941
  return;
1474
1942
  }
1475
1943
  isFlushing = true;
1476
- void Promise.allSettled([
1477
- ...Array.from(pendingTracePromises).map((p) => p.catch(() => {
1478
- })),
1479
- shutdownTraceTransports(EXIT_FLUSH_TIMEOUT_MS).catch(() => false)
1480
- ]).then(() => {
1944
+ const planWaitMs = SIMULATION_PLAN_READ_TIMEOUT_MS;
1945
+ const stopFlushing = () => {
1481
1946
  isFlushing = false;
1947
+ };
1948
+ const deadline = setTimeout(
1949
+ stopFlushing,
1950
+ EXIT_FLUSH_TIMEOUT_MS + planWaitMs
1951
+ );
1952
+ unrefTimer(deadline);
1953
+ void releaseHeldExternalSpans(planWaitMs).catch(() => {
1954
+ }).then(() => {
1955
+ stopSimulationPlans();
1956
+ }).then(
1957
+ () => Promise.allSettled([
1958
+ ...Array.from(pendingTracePromises).map((p) => p.catch(() => {
1959
+ })),
1960
+ shutdownTraceTransports(EXIT_FLUSH_TIMEOUT_MS).catch(() => false)
1961
+ ])
1962
+ ).then(() => {
1963
+ clearTimeout(deadline);
1964
+ stopFlushing();
1482
1965
  });
1483
1966
  });
1484
1967
  }
@@ -1497,6 +1980,7 @@ var init_http = __esm({
1497
1980
  this.apiKey = config.apiKey;
1498
1981
  this.serviceUrl = config.serviceUrl;
1499
1982
  this.timeout = config.timeout ?? 12e4;
1983
+ liveHttpClients.add(this);
1500
1984
  }
1501
1985
  /**
1502
1986
  * Resolve the API key at the moment it is needed (request time), invoking
@@ -1694,9 +2178,14 @@ var init_http = __esm({
1694
2178
  * attributing its timeout here would fail a client whose own work succeeded.
1695
2179
  */
1696
2180
  async settleDeferredWork(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS2) {
2181
+ const deadline = Date.now() + Math.max(timeoutMs, 0);
2182
+ await this.releaseHeldExternalSpans?.(planWaitSliceMs(timeoutMs));
1697
2183
  await replayContextReady.catch(() => {
1698
2184
  });
1699
- return waitForPromises(Array.from(this.deferredWork), timeoutMs);
2185
+ return waitForPromises(
2186
+ Array.from(this.deferredWork),
2187
+ Math.max(0, deadline - Date.now())
2188
+ );
1700
2189
  }
1701
2190
  /**
1702
2191
  * Wait for spans queued by this client to be delivered, within one deadline.
@@ -1704,7 +2193,9 @@ var init_http = __esm({
1704
2193
  */
1705
2194
  async waitForPendingRequests(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS2) {
1706
2195
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1707
- const settled2 = await this.settleDeferredWork(timeoutMs);
2196
+ const settled2 = await this.settleDeferredWork(
2197
+ Math.max(0, deadline - Date.now())
2198
+ );
1708
2199
  const flushed = await this.traceTransport?.flush(Math.max(0, deadline - Date.now())) ?? true;
1709
2200
  return settled2 && flushed;
1710
2201
  }
@@ -1719,6 +2210,13 @@ var init_http = __esm({
1719
2210
  }
1720
2211
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1721
2212
  this.closing = (async () => {
2213
+ await this.releaseHeldExternalSpans?.(planWaitSliceMs(timeoutMs));
2214
+ this.stopSimulationPlan?.();
2215
+ this.externalSpanTransform = void 0;
2216
+ this.externalTraceTransform = void 0;
2217
+ this.releaseHeldExternalSpans = void 0;
2218
+ this.stopSimulationPlan = void 0;
2219
+ liveHttpClients.delete(this);
1722
2220
  const settled2 = await this.settleDeferredWork(
1723
2221
  Math.max(0, deadline - Date.now())
1724
2222
  );
@@ -1830,6 +2328,13 @@ var init_http = __esm({
1830
2328
  protocol
1831
2329
  });
1832
2330
  }
2331
+ async getSimulationPlan() {
2332
+ return this.get(
2333
+ "/api/sdk/sim-plan",
2334
+ SIMULATION_PLAN_READ_TIMEOUT_MS,
2335
+ { Connection: "close" }
2336
+ );
2337
+ }
1833
2338
  async getTraceSpan(traceId, lookup) {
1834
2339
  const searchParams = new URLSearchParams();
1835
2340
  if (lookup.id !== void 0) {
@@ -1846,14 +2351,18 @@ var init_http = __esm({
1846
2351
  * GET a JSON endpoint on the service with the client's API key. Throws a
1847
2352
  * `BitfabError` carrying the status text for any non-2xx response.
1848
2353
  */
1849
- async get(endpoint) {
2354
+ async get(endpoint, timeoutMs, extraHeaders) {
1850
2355
  const url = `${this.serviceUrl}${endpoint}`;
2356
+ const timeout = timeoutMs ?? this.timeout;
1851
2357
  const controller = new AbortController();
1852
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
2358
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
1853
2359
  try {
1854
2360
  const response = await fetch(url, {
1855
2361
  method: "GET",
1856
- headers: { Authorization: `Bearer ${this.resolveApiKey() ?? ""}` },
2362
+ headers: {
2363
+ Authorization: `Bearer ${this.resolveApiKey() ?? ""}`,
2364
+ ...extraHeaders
2365
+ },
1857
2366
  signal: controller.signal
1858
2367
  });
1859
2368
  if (!response.ok) {
@@ -1872,7 +2381,7 @@ var init_http = __esm({
1872
2381
  }
1873
2382
  if (error instanceof Error) {
1874
2383
  if (error.name === "AbortError") {
1875
- throw new BitfabError(`Request timed out after ${this.timeout}ms`);
2384
+ throw new BitfabError(`Request timed out after ${timeout}ms`);
1876
2385
  }
1877
2386
  throw new BitfabError(error.message);
1878
2387
  }
@@ -1899,16 +2408,27 @@ var init_http = __esm({
1899
2408
  carrierMeta("internal_trace", body, void 0)
1900
2409
  );
1901
2410
  }
1902
- /**
1903
- * Queue an external span (from withSpan wrapper or OpenAI tracing) onto this
1904
- * client's batching transport. Fire-and-forget: the transport owns delivery,
1905
- * so callers await `flushTraces()` or `close()` rather than a per-span
1906
- * promise.
1907
- */
1908
2411
  sendExternalSpan(payload) {
2412
+ const gate = this.externalSpanTransform;
2413
+ if (gate === void 0) {
2414
+ this.submitExternalSpan(payload);
2415
+ return;
2416
+ }
2417
+ try {
2418
+ gate(payload, (ready) => this.submitExternalSpan(ready));
2419
+ } catch (error) {
2420
+ warnDroppedExternalSpan(error);
2421
+ }
2422
+ }
2423
+ submitExternalSpan(payload) {
2424
+ const body = {
2425
+ ...payload,
2426
+ sdkVersion: __version__
2427
+ };
2428
+ delete body[ROOT_TRACE_FUNCTION_KEY_FIELD];
1909
2429
  this.getTraceTransport()?.submit(
1910
2430
  "external_span",
1911
- { ...payload, sdkVersion: __version__ },
2431
+ body,
1912
2432
  this.recordedMeta("external_span", payload, carrierRef(payload))
1913
2433
  );
1914
2434
  }
@@ -1920,6 +2440,18 @@ var init_http = __esm({
1920
2440
  */
1921
2441
  sendExternalTrace(rawPayload) {
1922
2442
  const payload = mergeCallerMetadataIntoTracePayload(rawPayload);
2443
+ const gate = this.externalTraceTransform;
2444
+ if (gate === void 0) {
2445
+ this.submitExternalTrace(payload);
2446
+ return;
2447
+ }
2448
+ try {
2449
+ gate(payload, (ready) => this.submitExternalTrace(ready));
2450
+ } catch (error) {
2451
+ warnDroppedExternalTrace(error);
2452
+ }
2453
+ }
2454
+ submitExternalTrace(payload) {
1923
2455
  this.getTraceTransport()?.submit(
1924
2456
  "external_trace",
1925
2457
  {
@@ -3633,6 +4165,7 @@ function finalizeTracePayload(payload) {
3633
4165
  // src/claudeAgentSdk.ts
3634
4166
  init_randomUuid();
3635
4167
  init_serialize();
4168
+ init_spanOrigin();
3636
4169
 
3637
4170
  // src/timestamp.ts
3638
4171
  var lastTimestampMicros = 0;
@@ -3840,6 +4373,7 @@ var BitfabClaudeAgentHandler = class {
3840
4373
  trace_id: spanInfo.traceId,
3841
4374
  started_at: spanInfo.startedAt,
3842
4375
  ended_at: spanInfo.endedAt ?? nowIso(),
4376
+ span_origin: makeSpanOrigin("claude-agent-sdk"),
3843
4377
  span_data: spanData
3844
4378
  };
3845
4379
  if (spanInfo.parentId !== null) {
@@ -5151,6 +5685,7 @@ init_constants();
5151
5685
  init_http();
5152
5686
  init_randomUuid();
5153
5687
  init_serialize();
5688
+ init_spanOrigin();
5154
5689
  var LANGSMITH_HIDDEN_TAG = "langsmith:hidden";
5155
5690
  var CHAIN_RUN_TYPES = /* @__PURE__ */ new Set(["chain", "parser", "prompt"]);
5156
5691
  var LANGGRAPH_METADATA_KEYS = [
@@ -5505,6 +6040,7 @@ var BitfabLangGraphCallbackHandler = class {
5505
6040
  trace_id: spanInfo.traceId,
5506
6041
  started_at: spanInfo.startedAt,
5507
6042
  ended_at: spanInfo.endedAt ?? nowIso2(),
6043
+ span_origin: makeSpanOrigin("langgraph"),
5508
6044
  span_data: spanData
5509
6045
  };
5510
6046
  if (spanInfo.parentId !== null) {
@@ -5766,14 +6302,14 @@ var BitfabLangGraphCallbackHandler = class {
5766
6302
  init_http();
5767
6303
  init_replayContext();
5768
6304
  var TOOL_RESULT_TAG = "__bitfabLangGraphToolResult";
5769
- function isRecord(value) {
6305
+ function isRecord3(value) {
5770
6306
  return typeof value === "object" && value !== null;
5771
6307
  }
5772
6308
  function isToolMessage(value) {
5773
- return isRecord(value) && value.lc_direct_tool_output === true && value.type === "tool" && (typeof value.content === "string" || Array.isArray(value.content));
6309
+ return isRecord3(value) && value.lc_direct_tool_output === true && value.type === "tool" && (typeof value.content === "string" || Array.isArray(value.content));
5774
6310
  }
5775
6311
  function isCommand(value) {
5776
- return isRecord(value) && value.lg_name === "Command";
6312
+ return isRecord3(value) && value.lg_name === "Command";
5777
6313
  }
5778
6314
  function encodeNested(value) {
5779
6315
  if (isToolMessage(value) || isCommand(value)) {
@@ -5782,7 +6318,7 @@ function encodeNested(value) {
5782
6318
  if (Array.isArray(value)) {
5783
6319
  return value.map(encodeNested);
5784
6320
  }
5785
- if (isRecord(value)) {
6321
+ if (isRecord3(value)) {
5786
6322
  return Object.fromEntries(
5787
6323
  Object.entries(value).map(([key, entry]) => [key, encodeNested(entry)])
5788
6324
  );
@@ -5805,17 +6341,17 @@ function encodeNativeToolResult(value) {
5805
6341
  }
5806
6342
  return {
5807
6343
  [TOOL_RESULT_TAG]: "command",
5808
- graph: isRecord(value) && typeof value.graph === "string" ? value.graph : void 0,
5809
- update: isRecord(value) ? encodeNested(value.update) : void 0,
5810
- resume: isRecord(value) ? encodeNested(value.resume) : void 0,
5811
- goto: isRecord(value) ? encodeNested(value.goto) : void 0
6344
+ graph: isRecord3(value) && typeof value.graph === "string" ? value.graph : void 0,
6345
+ update: isRecord3(value) ? encodeNested(value.update) : void 0,
6346
+ resume: isRecord3(value) ? encodeNested(value.resume) : void 0,
6347
+ goto: isRecord3(value) ? encodeNested(value.goto) : void 0
5812
6348
  };
5813
6349
  }
5814
6350
  function finalizeToolResult(value) {
5815
6351
  return isToolMessage(value) || isCommand(value) ? encodeNativeToolResult(value) : value;
5816
6352
  }
5817
6353
  function isEncodedToolResult(value) {
5818
- return isRecord(value) && typeof value[TOOL_RESULT_TAG] === "string";
6354
+ return isRecord3(value) && typeof value[TOOL_RESULT_TAG] === "string";
5819
6355
  }
5820
6356
  async function loadLangChainCore() {
5821
6357
  try {
@@ -5851,7 +6387,7 @@ async function reviveNested(value, toolCallId) {
5851
6387
  if (Array.isArray(value)) {
5852
6388
  return Promise.all(value.map((entry) => reviveNested(entry, toolCallId)));
5853
6389
  }
5854
- if (isRecord(value)) {
6390
+ if (isRecord3(value)) {
5855
6391
  const entries = await Promise.all(
5856
6392
  Object.entries(value).map(async ([key, entry]) => [
5857
6393
  key,
@@ -5876,16 +6412,16 @@ async function reviveToolResult(value, toolCallId) {
5876
6412
  ...value.artifact !== void 0 && {
5877
6413
  artifact: await reviveNested(value.artifact, toolCallId)
5878
6414
  },
5879
- ...isRecord(value.metadata) && {
6415
+ ...isRecord3(value.metadata) && {
5880
6416
  metadata: await reviveNested(value.metadata, toolCallId)
5881
6417
  },
5882
- ...isRecord(value.additionalKwargs) && {
6418
+ ...isRecord3(value.additionalKwargs) && {
5883
6419
  additional_kwargs: await reviveNested(
5884
6420
  value.additionalKwargs,
5885
6421
  toolCallId
5886
6422
  )
5887
6423
  },
5888
- ...isRecord(value.responseMetadata) && {
6424
+ ...isRecord3(value.responseMetadata) && {
5889
6425
  response_metadata: await reviveNested(
5890
6426
  value.responseMetadata,
5891
6427
  toolCallId
@@ -5955,8 +6491,8 @@ var BitfabLangGraphIntegration = class {
5955
6491
  get: (target, property) => {
5956
6492
  if (property === "invoke") {
5957
6493
  return async (input, ...rest) => {
5958
- const toolCallId = isRecord(input) && typeof input.id === "string" ? input.id : "";
5959
- const args = isRecord(input) && "args" in input ? input.args : input;
6494
+ const toolCallId = isRecord3(input) && typeof input.id === "string" ? input.id : "";
6495
+ const args = isRecord3(input) && "args" in input ? input.args : input;
5960
6496
  this.assertReplayToolResultExists(toolName, shouldMock);
5961
6497
  const execute = this.client.withSpan(
5962
6498
  this.traceFunctionKey,
@@ -5966,7 +6502,8 @@ var BitfabLangGraphIntegration = class {
5966
6502
  captureWhen: "nested",
5967
6503
  mockOnReplay: shouldMock,
5968
6504
  finalize: finalizeToolResult,
5969
- surface: "inherit"
6505
+ surface: "inherit",
6506
+ instrumentation: "langgraph"
5970
6507
  },
5971
6508
  async (_args) => await originalInvoke(input, ...rest)
5972
6509
  );
@@ -6009,7 +6546,12 @@ var BitfabLangGraphIntegration = class {
6009
6546
  wrapInvoke(fn) {
6010
6547
  return this.client.withSpan(
6011
6548
  this.traceFunctionKey,
6012
- { name: this.traceFunctionKey, type: "agent", surface: "inherit" },
6549
+ {
6550
+ name: this.traceFunctionKey,
6551
+ type: "agent",
6552
+ surface: "inherit",
6553
+ instrumentation: "langgraph"
6554
+ },
6013
6555
  fn
6014
6556
  );
6015
6557
  }
@@ -6065,7 +6607,8 @@ var BitfabOpenAIAgentHandler = class {
6065
6607
  const options_ = {
6066
6608
  type: "agent",
6067
6609
  finalize,
6068
- surface: "inherit"
6610
+ surface: "inherit",
6611
+ instrumentation: "openai-agents"
6069
6612
  };
6070
6613
  const traced = this.withSpanFn(
6071
6614
  this.traceFunctionKey,
@@ -6081,6 +6624,7 @@ var BitfabOpenAIAgentHandler = class {
6081
6624
  };
6082
6625
 
6083
6626
  // src/client.ts
6627
+ init_policyRefresh();
6084
6628
  init_randomUuid();
6085
6629
  init_replay();
6086
6630
 
@@ -6164,12 +6708,26 @@ function runWithSeedContext(ctx, fn) {
6164
6708
 
6165
6709
  // src/client.ts
6166
6710
  init_serialize();
6711
+ init_simulationPlan();
6712
+ init_spanOrigin();
6713
+
6714
+ // src/streamFinalizationError.ts
6715
+ var StreamFinalizationError = class extends Error {
6716
+ constructor(message, output) {
6717
+ super(message);
6718
+ this.output = output;
6719
+ this.name = "StreamFinalizationError";
6720
+ }
6721
+ };
6722
+
6723
+ // src/client.ts
6167
6724
  init_traceMetadata();
6168
6725
 
6169
6726
  // src/tracing.ts
6170
6727
  init_constants();
6171
6728
  init_http();
6172
6729
  init_randomUuid();
6730
+ init_spanOrigin();
6173
6731
  var BitfabOpenAITracingProcessor = class {
6174
6732
  /**
6175
6733
  * Initialize the tracing processor.
@@ -6371,6 +6929,7 @@ var BitfabOpenAITracingProcessor = class {
6371
6929
  * Build span payload for the external spans API.
6372
6930
  */
6373
6931
  buildSpanPayload(serializedSpan, errors) {
6932
+ serializedSpan.span_origin = makeSpanOrigin("openai-agents");
6374
6933
  const payload = {
6375
6934
  id: randomUuid(),
6376
6935
  type: "openai",
@@ -6435,52 +6994,110 @@ function summarizeGenerate(result, model) {
6435
6994
  }
6436
6995
  return summary;
6437
6996
  }
6438
- function accumulateStream(onComplete, model) {
6997
+ function accumulateStream(source, onComplete, model) {
6998
+ const reader = source.getReader();
6439
6999
  let text = "";
6440
7000
  const toolCalls = [];
6441
7001
  let usage;
6442
7002
  let finishReason;
6443
7003
  let completed = false;
6444
- const complete = () => {
7004
+ let streamFailure;
7005
+ const complete = (failure = streamFailure) => {
6445
7006
  if (completed) {
6446
7007
  return;
6447
7008
  }
6448
7009
  completed = true;
6449
- const summary = {
7010
+ let error;
7011
+ if (failure !== void 0) {
7012
+ try {
7013
+ error = failure.error instanceof Error ? failure.error.message : String(
7014
+ failure.error ?? (failure.cancelled === true ? "Stream cancelled" : "Stream failed")
7015
+ );
7016
+ } catch {
7017
+ error = "Stream failed";
7018
+ }
7019
+ }
7020
+ onComplete({
6450
7021
  text,
6451
7022
  toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
6452
7023
  usage,
6453
- finishReason
6454
- };
6455
- if (model) {
6456
- summary.model = model;
6457
- }
6458
- onComplete(summary);
7024
+ finishReason,
7025
+ ...model !== void 0 && { model },
7026
+ ...failure !== void 0 && { error },
7027
+ ...failure?.cancelled === true && { cancelled: true }
7028
+ });
6459
7029
  };
6460
- return new TransformStream({
6461
- transform(part, controller) {
6462
- try {
6463
- if (part?.type === "text-delta") {
6464
- text += part.delta ?? part.textDelta ?? "";
6465
- } else if (part?.type === "tool-call") {
6466
- toolCalls.push({
6467
- toolCallId: part.toolCallId,
6468
- toolName: part.toolName,
6469
- input: part.input ?? part.args
6470
- });
6471
- } else if (part?.type === "finish") {
6472
- usage = part.usage;
6473
- finishReason = part.finishReason;
6474
- complete();
7030
+ return new ReadableStream(
7031
+ {
7032
+ start(controller) {
7033
+ void reader.closed.then(
7034
+ () => {
7035
+ queueMicrotask(() => {
7036
+ if (!completed) {
7037
+ complete();
7038
+ reader.releaseLock();
7039
+ controller.close();
7040
+ }
7041
+ });
7042
+ },
7043
+ (error) => {
7044
+ if (!completed) {
7045
+ complete({ error });
7046
+ controller.error(error);
7047
+ reader.releaseLock();
7048
+ }
7049
+ }
7050
+ );
7051
+ },
7052
+ async pull(controller) {
7053
+ try {
7054
+ const { done, value: part } = await reader.read();
7055
+ if (completed) {
7056
+ return;
7057
+ }
7058
+ if (done) {
7059
+ complete();
7060
+ reader.releaseLock();
7061
+ controller.close();
7062
+ return;
7063
+ }
7064
+ try {
7065
+ if (part?.type === "text-delta") {
7066
+ text += part.delta ?? part.textDelta ?? "";
7067
+ } else if (part?.type === "tool-call") {
7068
+ toolCalls.push({
7069
+ toolCallId: part.toolCallId,
7070
+ toolName: part.toolName,
7071
+ input: part.input ?? part.args
7072
+ });
7073
+ } else if (part?.type === "error") {
7074
+ streamFailure = { error: part.error };
7075
+ } else if (part?.type === "finish") {
7076
+ usage = part.usage;
7077
+ finishReason = part.finishReason;
7078
+ }
7079
+ } catch {
7080
+ }
7081
+ controller.enqueue(part);
7082
+ } catch (error) {
7083
+ if (!completed) {
7084
+ complete({ error });
7085
+ reader.releaseLock();
7086
+ controller.error(error);
7087
+ }
7088
+ }
7089
+ },
7090
+ async cancel(reason) {
7091
+ complete({ error: reason, cancelled: true });
7092
+ try {
7093
+ await reader.cancel(reason);
7094
+ } finally {
7095
+ reader.releaseLock();
6475
7096
  }
6476
- } catch {
6477
7097
  }
6478
- controller.enqueue(part);
6479
7098
  },
6480
- flush() {
6481
- complete();
6482
- }
6483
- });
7099
+ { highWaterMark: 0 }
7100
+ );
6484
7101
  }
6485
7102
  var BitfabVercelAiHandler = class {
6486
7103
  constructor(config) {
@@ -6500,7 +7117,8 @@ var BitfabVercelAiHandler = class {
6500
7117
  {
6501
7118
  type: "llm",
6502
7119
  finalize: (result) => summarizeGenerate(result ?? {}, label),
6503
- surface: "inherit"
7120
+ surface: "inherit",
7121
+ instrumentation: "vercel-ai"
6504
7122
  },
6505
7123
  () => doGenerate()
6506
7124
  );
@@ -6518,11 +7136,24 @@ var BitfabVercelAiHandler = class {
6518
7136
  // The wrapped fn returns immediately with the live stream, so the span
6519
7137
  // output cannot be read from the return value. `finalize` instead
6520
7138
  // awaits the summary the accumulator resolves once the stream drains.
6521
- { type: "llm", finalize: () => summary, surface: "inherit" },
7139
+ {
7140
+ type: "llm",
7141
+ finalize: async () => {
7142
+ const output = await summary;
7143
+ if (output.error !== void 0) {
7144
+ throw new StreamFinalizationError(output.error, output);
7145
+ }
7146
+ return output;
7147
+ },
7148
+ surface: "inherit",
7149
+ instrumentation: "vercel-ai"
7150
+ },
6522
7151
  async () => {
6523
7152
  const result = await doStream();
6524
- const stream = result.stream.pipeThrough(
6525
- accumulateStream(resolveSummary, label)
7153
+ const stream = accumulateStream(
7154
+ result.stream,
7155
+ resolveSummary,
7156
+ label
6526
7157
  );
6527
7158
  return { ...result, stream };
6528
7159
  }
@@ -6536,6 +7167,19 @@ var BitfabVercelAiHandler = class {
6536
7167
  // src/client.ts
6537
7168
  init_warnOnce();
6538
7169
  var activeTraceStates = /* @__PURE__ */ new Map();
7170
+ function rootTraceFunctionKeyOf(payload) {
7171
+ const captured = payload[ROOT_TRACE_FUNCTION_KEY_FIELD];
7172
+ if (typeof captured === "string") {
7173
+ return captured;
7174
+ }
7175
+ const traceId = payload.traceId;
7176
+ const live = typeof traceId === "string" ? activeTraceStates.get(traceId)?.traceFunctionKey : void 0;
7177
+ if (typeof live === "string") {
7178
+ return live;
7179
+ }
7180
+ const own = payload.traceFunctionKey;
7181
+ return typeof own === "string" ? own : void 0;
7182
+ }
6539
7183
  var asyncLocalStorage = null;
6540
7184
  var SPAN_CONTEXT_STORAGE_SYMBOL = /* @__PURE__ */ Symbol.for("bitfab.spanContextStorage");
6541
7185
  var initializeAsyncContext = () => {
@@ -6940,6 +7584,14 @@ var Bitfab = class {
6940
7584
  serviceUrl: this.serviceUrl,
6941
7585
  timeout: this.timeout
6942
7586
  });
7587
+ this.simulationPlan = new SimulationPlan(
7588
+ this.httpClient,
7589
+ config.simulationPlan ?? true
7590
+ );
7591
+ this.httpClient.externalSpanTransform = (payload, submit) => this.applySimulationPlan(payload, submit);
7592
+ this.httpClient.externalTraceTransform = (payload, submit) => this.simulationPlan.sendTrace(payload, submit);
7593
+ this.httpClient.releaseHeldExternalSpans = (timeoutMs) => this.simulationPlan.release(timeoutMs);
7594
+ this.httpClient.stopSimulationPlan = () => this.simulationPlan.stop();
6943
7595
  this.datasets = new DatasetsClient(this.httpClient);
6944
7596
  this.traces = new TracesClient(this.httpClient);
6945
7597
  this.labels = new LabelsClient(this.httpClient);
@@ -7090,6 +7742,7 @@ var Bitfab = class {
7090
7742
  }
7091
7743
  createAutoTraceRoot(traceFunctionKey, name, options, fn) {
7092
7744
  const self = this;
7745
+ self.simulationPlan.refresh();
7093
7746
  const maxDepth = autoTraceLimit(
7094
7747
  options.maxDepth,
7095
7748
  DEFAULT_AUTO_TRACE_MAX_DEPTH
@@ -7105,7 +7758,8 @@ var Bitfab = class {
7105
7758
  name: options.name,
7106
7759
  nameFor: rootNameFor,
7107
7760
  type: options.type ?? "custom",
7108
- surface: "opt-out"
7761
+ surface: "opt-out",
7762
+ instrumentation: "trace"
7109
7763
  };
7110
7764
  const buildTracedRoot = (spanOptions) => this.withSpan(
7111
7765
  traceFunctionKey,
@@ -7116,6 +7770,7 @@ var Bitfab = class {
7116
7770
  traceFunctionKey
7117
7771
  );
7118
7772
  self.refreshAutoTraceCapturePolicy(traceFunctionKey);
7773
+ self.simulationPlan.refresh();
7119
7774
  let spansUsed = 0;
7120
7775
  let truncated = false;
7121
7776
  const warnTruncated = () => {
@@ -7192,6 +7847,7 @@ var Bitfab = class {
7192
7847
  type: nodeConfiguration?.type ?? "function",
7193
7848
  captureWhen: "nested",
7194
7849
  surface: "opt-out",
7850
+ instrumentation: "trace",
7195
7851
  functionId: definition.id,
7196
7852
  captureContent: nodeConfiguration !== void 0 || capturePolicy === void 0 || capturePolicy.has(definition.id),
7197
7853
  autoTraceDefinition: definition,
@@ -7223,6 +7879,7 @@ var Bitfab = class {
7223
7879
  type: "function",
7224
7880
  captureWhen: "nested",
7225
7881
  surface: "opt-out",
7882
+ instrumentation: "trace",
7226
7883
  captureContent: true,
7227
7884
  ...link !== void 0 && { nestedTrace: link }
7228
7885
  };
@@ -7288,38 +7945,36 @@ var Bitfab = class {
7288
7945
  Object.defineProperty(autoTraceRoot, "_bitfabWrappedFn", { value: fn });
7289
7946
  return autoTraceRoot;
7290
7947
  }
7948
+ applySimulationPlan(payload, submit) {
7949
+ this.simulationPlan.send(payload, rootTraceFunctionKeyOf(payload), submit);
7950
+ }
7291
7951
  refreshAutoTraceCapturePolicy(traceFunctionKey) {
7292
- const now = Date.now();
7293
- const state = this.autoTracePolicyRefreshes.get(traceFunctionKey) ?? {
7294
- refreshAfter: 0
7295
- };
7296
- if (state.inFlight || now < state.refreshAfter) {
7297
- return;
7298
- }
7299
- const request = this.httpClient.getAutoTracePolicy(
7300
- traceFunctionKey,
7301
- AUTO_TRACE_PROTOCOL
7302
- ).then((policy) => {
7303
- if (policy.protocol !== AUTO_TRACE_PROTOCOL) {
7304
- state.refreshAfter = Date.now() + AUTO_TRACE_POLICY_RETRY_MS;
7305
- return;
7306
- }
7307
- const functionIds = Array.isArray(policy.functionIds) ? policy.functionIds.filter(
7308
- (id) => typeof id === "string" && id.startsWith(`${AUTO_TRACE_PROTOCOL}:`)
7309
- ).slice(0, DEFAULT_AUTO_TRACE_MAX_SPANS) : [];
7310
- __setBitfabAutoTraceCapturePolicy(
7311
- this,
7312
- traceFunctionKey,
7313
- policy.revision === null ? void 0 : functionIds
7314
- );
7315
- state.refreshAfter = Date.now() + AUTO_TRACE_POLICY_REFRESH_MS;
7316
- }).catch(() => {
7317
- state.refreshAfter = Date.now() + AUTO_TRACE_POLICY_RETRY_MS;
7318
- }).finally(() => {
7319
- state.inFlight = void 0;
7320
- });
7321
- state.inFlight = request;
7952
+ const state = this.autoTracePolicyRefreshes.get(traceFunctionKey) ?? new PolicyRefresh();
7322
7953
  this.autoTracePolicyRefreshes.set(traceFunctionKey, state);
7954
+ state.run(
7955
+ () => this.httpClient.getAutoTracePolicy(
7956
+ traceFunctionKey,
7957
+ AUTO_TRACE_PROTOCOL
7958
+ ),
7959
+ (policy) => {
7960
+ if (policy.protocol !== AUTO_TRACE_PROTOCOL) {
7961
+ state.hold(AUTO_TRACE_POLICY_RETRY_MS);
7962
+ return;
7963
+ }
7964
+ const functionIds = Array.isArray(policy.functionIds) ? policy.functionIds.filter(
7965
+ (id) => typeof id === "string" && id.startsWith(`${AUTO_TRACE_PROTOCOL}:`)
7966
+ ).slice(0, DEFAULT_AUTO_TRACE_MAX_SPANS) : [];
7967
+ __setBitfabAutoTraceCapturePolicy(
7968
+ this,
7969
+ traceFunctionKey,
7970
+ policy.revision === null ? void 0 : functionIds
7971
+ );
7972
+ state.hold(AUTO_TRACE_POLICY_REFRESH_MS);
7973
+ },
7974
+ () => {
7975
+ state.hold(AUTO_TRACE_POLICY_RETRY_MS);
7976
+ }
7977
+ );
7323
7978
  }
7324
7979
  /**
7325
7980
  * Flush and permanently close this client's tracing resources: its pending
@@ -7803,6 +8458,7 @@ var Bitfab = class {
7803
8458
  * @returns A wrapped function with the same signature that creates spans for inputs and outputs
7804
8459
  */
7805
8460
  withSpan(traceFunctionKey, optionsOrFn, maybeFn) {
8461
+ this.simulationPlan.refresh();
7806
8462
  const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
7807
8463
  const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
7808
8464
  const self = this;
@@ -7894,8 +8550,10 @@ var Bitfab = class {
7894
8550
  const testRunId = replayCtxAtStart?.testRunId ?? options.testRunId;
7895
8551
  if (isRootSpan && !activeTraceStates.has(traceId)) {
7896
8552
  const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
8553
+ self.simulationPlan.refresh();
7897
8554
  activeTraceStates.set(traceId, {
7898
8555
  traceId,
8556
+ traceFunctionKey,
7899
8557
  startedAt,
7900
8558
  contexts: [],
7901
8559
  ...testRunId !== void 0 && { testRunId },
@@ -7910,8 +8568,10 @@ var Bitfab = class {
7910
8568
  registeredTraceId = traceId;
7911
8569
  }
7912
8570
  const functionName = fn.name !== "" ? fn.name : void 0;
8571
+ const rootTraceFunctionKey = activeTraceStates.get(traceId)?.traceFunctionKey ?? traceFunctionKey;
7913
8572
  const baseSpanParams = {
7914
8573
  traceFunctionKey,
8574
+ rootTraceFunctionKey,
7915
8575
  functionName,
7916
8576
  spanName: options.name ?? options.nameFor?.(this) ?? qualifiedSpanName(this, functionName) ?? traceFunctionKey,
7917
8577
  traceId,
@@ -7920,6 +8580,7 @@ var Bitfab = class {
7920
8580
  inputs,
7921
8581
  startedAt,
7922
8582
  spanType: options.type ?? "custom",
8583
+ instrumentation: options.instrumentation ?? "span",
7923
8584
  functionId: options.functionId,
7924
8585
  captureContent: options.captureContent ?? true,
7925
8586
  autoTraceDefinition: options.autoTraceDefinition,
@@ -7987,8 +8648,8 @@ var Bitfab = class {
7987
8648
  void self.httpClient.trackDeferred(
7988
8649
  Promise.resolve().then(() => options.finalize(result)).then((output) => sendSpan({ result: output })).catch(
7989
8650
  (error) => sendSpan({
7990
- result: void 0,
7991
- error: error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
8651
+ result: error instanceof StreamFinalizationError ? error.output : void 0,
8652
+ error: error instanceof StreamFinalizationError ? error.message : error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
7992
8653
  })
7993
8654
  )
7994
8655
  );
@@ -8335,6 +8996,7 @@ var Bitfab = class {
8335
8996
  * @returns A BitfabFunction instance for wrapping functions
8336
8997
  */
8337
8998
  getFunction(traceFunctionKey) {
8999
+ this.simulationPlan.refresh();
8338
9000
  return new BitfabFunction(this, traceFunctionKey);
8339
9001
  }
8340
9002
  /**
@@ -8422,6 +9084,7 @@ var Bitfab = class {
8422
9084
  trace_id: params.traceId,
8423
9085
  started_at: params.startedAt,
8424
9086
  ended_at: params.endedAt,
9087
+ span_origin: makeSpanOrigin(params.instrumentation),
8425
9088
  span_data: {
8426
9089
  name: params.spanName,
8427
9090
  type: params.spanType,
@@ -8482,6 +9145,9 @@ var Bitfab = class {
8482
9145
  source: "typescript-sdk-function",
8483
9146
  sourceTraceId: params.traceId,
8484
9147
  traceFunctionKey: params.traceFunctionKey,
9148
+ ...params.rootTraceFunctionKey !== void 0 && {
9149
+ [ROOT_TRACE_FUNCTION_KEY_FIELD]: params.rootTraceFunctionKey
9150
+ },
8485
9151
  rawSpan: externalSpan,
8486
9152
  ...params.testRunId && { testRunId: params.testRunId },
8487
9153
  ...params.mocked && { mocked: true },
@@ -8550,6 +9216,7 @@ var Bitfab = class {
8550
9216
  }
8551
9217
  activeTraceStates.set(traceId, {
8552
9218
  traceId,
9219
+ traceFunctionKey,
8553
9220
  startedAt,
8554
9221
  contexts: [],
8555
9222
  ingestionType: "seeded",
@@ -8559,6 +9226,7 @@ var Bitfab = class {
8559
9226
  try {
8560
9227
  this.sendWrapperSpan({
8561
9228
  traceFunctionKey,
9229
+ rootTraceFunctionKey: traceFunctionKey,
8562
9230
  spanName: options.spanName ?? traceFunctionKey,
8563
9231
  traceId,
8564
9232
  spanId: randomUuid(),
@@ -8568,6 +9236,7 @@ var Bitfab = class {
8568
9236
  startedAt,
8569
9237
  endedAt: startedAt,
8570
9238
  spanType: options.spanType ?? "agent",
9239
+ instrumentation: "span",
8571
9240
  captureContent: true
8572
9241
  });
8573
9242
  this.sendTraceCompletion({
@@ -8613,6 +9282,7 @@ var Bitfab = class {
8613
9282
  }
8614
9283
  activeTraceStates.set(traceId, {
8615
9284
  traceId,
9285
+ traceFunctionKey,
8616
9286
  startedAt: nowIsoTimestamp(),
8617
9287
  contexts: [],
8618
9288
  ingestionType: "seeded",