bitfab 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
@@ -51,7 +51,7 @@ var __version__, __packageName__;
51
51
  var init_version_generated = __esm({
52
52
  "src/version.generated.ts"() {
53
53
  "use strict";
54
- __version__ = "0.53.0";
54
+ __version__ = "0.53.2";
55
55
  __packageName__ = "bitfab";
56
56
  }
57
57
  });
@@ -516,8 +516,8 @@ function encodePayloadBody(payload) {
516
516
  const marker = { error: `payload_serialize_failed: ${message}` };
517
517
  return { body: JSON.stringify(marker), dropped, value: marker };
518
518
  }
519
- const isRecord2 = typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized);
520
- if (dropped.length > 0 && isRecord2) {
519
+ const isRecord4 = typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized);
520
+ if (dropped.length > 0 && isRecord4) {
521
521
  const obj = sanitized;
522
522
  const existing = Array.isArray(obj.errors) ? obj.errors : [];
523
523
  obj.errors = [
@@ -534,7 +534,7 @@ function encodePayloadBody(payload) {
534
534
  return {
535
535
  body: JSON.stringify(sanitized),
536
536
  dropped,
537
- value: isRecord2 ? sanitized : void 0
537
+ value: isRecord4 ? sanitized : void 0
538
538
  };
539
539
  }
540
540
  }
@@ -546,6 +546,456 @@ var init_serializePayload = __esm({
546
546
  }
547
547
  });
548
548
 
549
+ // src/policyRefresh.ts
550
+ var PolicyRefresh;
551
+ var init_policyRefresh = __esm({
552
+ "src/policyRefresh.ts"() {
553
+ "use strict";
554
+ PolicyRefresh = class {
555
+ constructor() {
556
+ this.refreshAfter = 0;
557
+ }
558
+ due() {
559
+ return this.inFlight === void 0 && Date.now() >= this.refreshAfter;
560
+ }
561
+ hold(durationMs) {
562
+ this.refreshAfter = Date.now() + durationMs;
563
+ }
564
+ releaseHold() {
565
+ this.refreshAfter = 0;
566
+ }
567
+ run(read2, onLoaded, onFailed) {
568
+ if (!this.due()) {
569
+ return;
570
+ }
571
+ let request;
572
+ try {
573
+ request = read2().then(onLoaded).catch(onFailed).finally(() => {
574
+ this.inFlight = void 0;
575
+ });
576
+ } catch (error) {
577
+ onFailed(error);
578
+ return;
579
+ }
580
+ this.inFlight = request;
581
+ }
582
+ };
583
+ }
584
+ });
585
+
586
+ // src/spanOrigin.ts
587
+ function isFrameworkInstrumentation(value) {
588
+ return typeof value === "string" && FRAMEWORK_INSTRUMENTATION_SET.has(value);
589
+ }
590
+ function makeSpanOrigin(instrumentation) {
591
+ return {
592
+ name: SPAN_ORIGIN_NAME,
593
+ version: __version__,
594
+ instrumentation: { name: instrumentation }
595
+ };
596
+ }
597
+ function isRecord(value) {
598
+ return typeof value === "object" && value !== null && !Array.isArray(value);
599
+ }
600
+ function spanOriginOf(payload) {
601
+ const rawSpan = payload.rawSpan;
602
+ if (!isRecord(rawSpan)) {
603
+ return void 0;
604
+ }
605
+ const origin = rawSpan.span_origin;
606
+ if (!isRecord(origin) || !isRecord(origin.instrumentation)) {
607
+ return void 0;
608
+ }
609
+ const { name, version } = origin;
610
+ const instrumentation = origin.instrumentation.name;
611
+ if (typeof name !== "string" || typeof version !== "string" || typeof instrumentation !== "string" || !SPAN_INSTRUMENTATIONS.includes(instrumentation)) {
612
+ return void 0;
613
+ }
614
+ return {
615
+ name,
616
+ version,
617
+ instrumentation: { name: instrumentation }
618
+ };
619
+ }
620
+ function spanInstrumentationOf(payload) {
621
+ return spanOriginOf(payload)?.instrumentation.name;
622
+ }
623
+ function recordedByFramework(payload) {
624
+ return isFrameworkInstrumentation(spanInstrumentationOf(payload));
625
+ }
626
+ var SPAN_ORIGIN_NAME, SPAN_INSTRUMENTATIONS, FRAMEWORK_INSTRUMENTATIONS, FRAMEWORK_INSTRUMENTATION_SET;
627
+ var init_spanOrigin = __esm({
628
+ "src/spanOrigin.ts"() {
629
+ "use strict";
630
+ init_constants();
631
+ SPAN_ORIGIN_NAME = "bitfab.sdk.typescript";
632
+ SPAN_INSTRUMENTATIONS = [
633
+ "span",
634
+ "trace",
635
+ "openai-agents",
636
+ "langgraph",
637
+ "claude-agent-sdk",
638
+ "vercel-ai"
639
+ ];
640
+ FRAMEWORK_INSTRUMENTATIONS = [
641
+ "openai-agents",
642
+ "langgraph",
643
+ "claude-agent-sdk",
644
+ "vercel-ai"
645
+ ];
646
+ FRAMEWORK_INSTRUMENTATION_SET = new Set(
647
+ FRAMEWORK_INSTRUMENTATIONS
648
+ );
649
+ }
650
+ });
651
+
652
+ // src/unrefTimer.ts
653
+ function unrefTimer(timer) {
654
+ const handle = timer;
655
+ if (typeof handle.unref === "function") {
656
+ handle.unref();
657
+ }
658
+ }
659
+ var init_unrefTimer = __esm({
660
+ "src/unrefTimer.ts"() {
661
+ "use strict";
662
+ }
663
+ });
664
+
665
+ // src/simulationPlan.ts
666
+ function planWaitSliceMs(timeoutMs) {
667
+ return Math.min(SIMULATION_PLAN_READ_TIMEOUT_MS, Math.max(timeoutMs, 0) / 2);
668
+ }
669
+ function parseSimulationPlan(body) {
670
+ if (typeof body !== "object" || body === null) {
671
+ return null;
672
+ }
673
+ const nodes = body.nodes;
674
+ if (!Array.isArray(nodes)) {
675
+ return null;
676
+ }
677
+ const contentOff = /* @__PURE__ */ new Map();
678
+ for (const node of nodes) {
679
+ if (typeof node !== "object" || node === null) {
680
+ continue;
681
+ }
682
+ const { traceFunctionKey, name, captureContent } = node;
683
+ if (typeof traceFunctionKey !== "string" || typeof name !== "string" || typeof captureContent !== "boolean") {
684
+ continue;
685
+ }
686
+ if (captureContent) {
687
+ continue;
688
+ }
689
+ const names = contentOff.get(traceFunctionKey) ?? /* @__PURE__ */ new Set();
690
+ names.add(name);
691
+ contentOff.set(traceFunctionKey, names);
692
+ }
693
+ return contentOff;
694
+ }
695
+ function traceIdOf(payload) {
696
+ const traceId = payload.traceId;
697
+ if (typeof traceId === "string") {
698
+ return traceId;
699
+ }
700
+ const id = payload.id;
701
+ return typeof id === "string" ? id : void 0;
702
+ }
703
+ function isRecord2(value) {
704
+ return typeof value === "object" && value !== null;
705
+ }
706
+ function rawSpanOf(payload) {
707
+ return isRecord2(payload.rawSpan) ? payload.rawSpan : void 0;
708
+ }
709
+ function spanDataOf(payload) {
710
+ const spanData = rawSpanOf(payload)?.span_data;
711
+ return isRecord2(spanData) ? spanData : void 0;
712
+ }
713
+ function spanNameOf(payload) {
714
+ const name = spanDataOf(payload)?.name;
715
+ return typeof name === "string" ? name : void 0;
716
+ }
717
+ function stripContent(payload) {
718
+ const rawSpan = rawSpanOf(payload);
719
+ const spanData = spanDataOf(payload);
720
+ if (rawSpan === void 0 || spanData === void 0) {
721
+ return payload;
722
+ }
723
+ const kept = { ...spanData };
724
+ for (const key of CONTENT_KEYS) {
725
+ delete kept[key];
726
+ }
727
+ kept[CONTENT_OFF_KEY] = true;
728
+ return { ...payload, rawSpan: { ...rawSpan, span_data: kept } };
729
+ }
730
+ function simulationPlanEnvDisabled() {
731
+ const value = readEnv(DISABLE_SIMULATION_PLAN_ENV);
732
+ return typeof value === "string" && value.trim() !== "";
733
+ }
734
+ function missingSimulationPlanEndpoint(error) {
735
+ return error instanceof BitfabError && error.status === 404;
736
+ }
737
+ 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;
738
+ var init_simulationPlan = __esm({
739
+ "src/simulationPlan.ts"() {
740
+ "use strict";
741
+ init_errors();
742
+ init_policyRefresh();
743
+ init_readEnv();
744
+ init_spanOrigin();
745
+ init_unrefTimer();
746
+ init_warnOnce();
747
+ SIMULATION_PLAN_REFRESH_MS = 6e4;
748
+ SIMULATION_PLAN_RETRY_MS = 1e4;
749
+ SIMULATION_PLAN_READ_TIMEOUT_MS = 5e3;
750
+ SIMULATION_PLAN_MAX_HELD = 1e3;
751
+ DISABLE_SIMULATION_PLAN_ENV = "BITFAB_DISABLE_SIM_PLAN";
752
+ ROOT_TRACE_FUNCTION_KEY_FIELD = "rootTraceFunctionKey";
753
+ CONTENT_OFF_KEY = "content_off_by_simulation_plan";
754
+ CONTENT_KEYS = ["input", "input_meta", "output", "output_meta"];
755
+ SimulationPlan = class {
756
+ constructor(source, enabled = true) {
757
+ this.source = source;
758
+ this.enabled = enabled;
759
+ this.policyRefresh = new PolicyRefresh();
760
+ this.retryPending = false;
761
+ this.waitingForFirstLoad = 0;
762
+ this.held = [];
763
+ this.droppedWhileHolding = 0;
764
+ this.stopped = false;
765
+ this.loaded = new Promise((resolve) => {
766
+ this.resolveLoaded = resolve;
767
+ });
768
+ }
769
+ disabled() {
770
+ return !this.enabled || simulationPlanEnvDisabled();
771
+ }
772
+ refresh() {
773
+ if (this.stopped || this.disabled()) {
774
+ return;
775
+ }
776
+ this.policyRefresh.run(
777
+ () => this.source.getSimulationPlan(),
778
+ (body) => {
779
+ const parsed = parseSimulationPlan(body);
780
+ if (parsed === null) {
781
+ this.scheduleRetry(
782
+ "sim-plan-unreadable",
783
+ "the sim plan response was not understood"
784
+ );
785
+ return;
786
+ }
787
+ this.markLoaded(parsed);
788
+ },
789
+ (error) => {
790
+ if (missingSimulationPlanEndpoint(error)) {
791
+ this.markLoaded(/* @__PURE__ */ new Map());
792
+ return;
793
+ }
794
+ this.scheduleRetry(
795
+ "sim-plan-unavailable",
796
+ `could not read the sim plan: ${error instanceof Error ? error.message : String(error)}`
797
+ );
798
+ }
799
+ );
800
+ }
801
+ markLoaded(parsed) {
802
+ if (this.stopped) {
803
+ return;
804
+ }
805
+ this.contentOffByKey = parsed;
806
+ this.policyRefresh.hold(SIMULATION_PLAN_REFRESH_MS);
807
+ this.retryPending = false;
808
+ this.clearRetryTimer();
809
+ this.resolveLoaded?.();
810
+ this.resolveLoaded = void 0;
811
+ this.releaseHeld();
812
+ }
813
+ scheduleRetry(warnKey, reason) {
814
+ if (this.stopped) {
815
+ return;
816
+ }
817
+ this.policyRefresh.hold(SIMULATION_PLAN_RETRY_MS);
818
+ if (this.contentOffByKey !== void 0) {
819
+ return;
820
+ }
821
+ this.retryPending = true;
822
+ warnOnce(
823
+ warnKey,
824
+ `${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.`
825
+ );
826
+ this.armRetry();
827
+ }
828
+ holdsWork() {
829
+ return this.held.length > 0 || this.waitingForFirstLoad > 0;
830
+ }
831
+ armRetry() {
832
+ if (this.stopped || !this.retryPending || this.retryTimer !== void 0 || !this.holdsWork()) {
833
+ return;
834
+ }
835
+ const timer = setTimeout(() => {
836
+ this.retryTimer = void 0;
837
+ this.policyRefresh.releaseHold();
838
+ this.refresh();
839
+ }, SIMULATION_PLAN_RETRY_MS);
840
+ unrefTimer(timer);
841
+ this.retryTimer = timer;
842
+ }
843
+ clearRetryTimer() {
844
+ if (this.retryTimer !== void 0) {
845
+ clearTimeout(this.retryTimer);
846
+ this.retryTimer = void 0;
847
+ }
848
+ }
849
+ syncRetryTimer() {
850
+ if (this.holdsWork()) {
851
+ return;
852
+ }
853
+ this.clearRetryTimer();
854
+ }
855
+ firstLoad() {
856
+ if (this.contentOffByKey !== void 0 || this.stopped || this.disabled()) {
857
+ return void 0;
858
+ }
859
+ return this.loaded;
860
+ }
861
+ send(payload, traceFunctionKey, submit) {
862
+ this.refresh();
863
+ if (this.stopped || this.disabled() || traceFunctionKey === void 0 || spanNameOf(payload) === void 0 || recordedByFramework(payload)) {
864
+ submit(payload);
865
+ return;
866
+ }
867
+ if (this.contentOffByKey === void 0) {
868
+ this.hold({ payload, traceFunctionKey, submit });
869
+ return;
870
+ }
871
+ this.submitEntry({ payload, traceFunctionKey, submit });
872
+ }
873
+ sendTrace(payload, submit) {
874
+ this.refresh();
875
+ if (this.stopped || this.disabled() || this.contentOffByKey !== void 0 || !this.holdsSpanOfTrace(traceIdOf(payload))) {
876
+ submit(payload);
877
+ return;
878
+ }
879
+ this.hold({ payload, traceFunctionKey: void 0, submit });
880
+ }
881
+ holdsSpanOfTrace(traceId) {
882
+ if (traceId === void 0) {
883
+ return false;
884
+ }
885
+ return this.held.some(
886
+ (entry) => entry.traceFunctionKey !== void 0 && traceIdOf(entry.payload) === traceId
887
+ );
888
+ }
889
+ hold(entry) {
890
+ this.held.push(entry);
891
+ while (this.held.length > SIMULATION_PLAN_MAX_HELD) {
892
+ this.held.shift();
893
+ this.droppedWhileHolding += 1;
894
+ }
895
+ this.armRetry();
896
+ }
897
+ submitEntry(entry) {
898
+ try {
899
+ entry.submit(
900
+ entry.traceFunctionKey === void 0 ? entry.payload : this.apply(entry.payload, entry.traceFunctionKey)
901
+ );
902
+ } catch (error) {
903
+ warnOnce(
904
+ "sim-plan-held-span-dropped",
905
+ `a span held for the sim plan was dropped when released: ${error instanceof Error ? error.message : String(error)}`
906
+ );
907
+ }
908
+ }
909
+ releaseHeld() {
910
+ const held = this.held;
911
+ this.held = [];
912
+ this.syncRetryTimer();
913
+ this.warnDropped();
914
+ for (const entry of held) {
915
+ this.submitEntry(entry);
916
+ }
917
+ }
918
+ warnDropped() {
919
+ if (this.droppedWhileHolding === 0) {
920
+ return;
921
+ }
922
+ const dropped = this.droppedWhileHolding;
923
+ this.droppedWhileHolding = 0;
924
+ warnOnce(
925
+ "sim-plan-held-overflow",
926
+ `${dropped} record(s) were dropped while the sim plan was still loading; at most ${SIMULATION_PLAN_MAX_HELD} are held per client.`
927
+ );
928
+ }
929
+ warnNeverLoaded() {
930
+ if (this.held.length === 0) {
931
+ return;
932
+ }
933
+ warnOnce(
934
+ "sim-plan-never-loaded",
935
+ `${this.held.length} record(s) were not sent because the sim plan never loaded`
936
+ );
937
+ }
938
+ async release(timeoutMs) {
939
+ this.refresh();
940
+ if (this.holdsWork()) {
941
+ await this.waitForFirstLoad(timeoutMs);
942
+ }
943
+ this.warnNeverLoaded();
944
+ this.warnDropped();
945
+ }
946
+ waitForFirstLoad(timeoutMs) {
947
+ const firstLoad = this.firstLoad();
948
+ if (firstLoad === void 0) {
949
+ return Promise.resolve();
950
+ }
951
+ this.waitingForFirstLoad += 1;
952
+ this.armRetry();
953
+ return new Promise((resolve) => {
954
+ let settled2 = false;
955
+ const finish = () => {
956
+ if (settled2) {
957
+ return;
958
+ }
959
+ settled2 = true;
960
+ this.waitingForFirstLoad -= 1;
961
+ this.syncRetryTimer();
962
+ resolve();
963
+ };
964
+ const timer = setTimeout(finish, Math.max(timeoutMs, 0));
965
+ void firstLoad.then(() => {
966
+ clearTimeout(timer);
967
+ finish();
968
+ });
969
+ });
970
+ }
971
+ stop() {
972
+ if (this.stopped) {
973
+ return;
974
+ }
975
+ this.stopped = true;
976
+ this.retryPending = false;
977
+ this.clearRetryTimer();
978
+ this.warnNeverLoaded();
979
+ this.held = [];
980
+ this.warnDropped();
981
+ }
982
+ contentOff(traceFunctionKey, spanName) {
983
+ return this.contentOffByKey?.get(traceFunctionKey)?.has(spanName) === true;
984
+ }
985
+ apply(payload, traceFunctionKey) {
986
+ if (recordedByFramework(payload)) {
987
+ return payload;
988
+ }
989
+ const name = spanNameOf(payload);
990
+ if (name === void 0 || !this.contentOff(traceFunctionKey, name)) {
991
+ return payload;
992
+ }
993
+ return stripContent(payload);
994
+ }
995
+ };
996
+ }
997
+ });
998
+
549
999
  // src/traceMetadata.ts
550
1000
  function findRecord(traceId) {
551
1001
  return activeRecords.get(traceId) ?? completedRecords.get(traceId);
@@ -662,19 +1112,6 @@ var init_transportTypes = __esm({
662
1112
  }
663
1113
  });
664
1114
 
665
- // src/unrefTimer.ts
666
- function unrefTimer(timer) {
667
- const handle = timer;
668
- if (typeof handle.unref === "function") {
669
- handle.unref();
670
- }
671
- }
672
- var init_unrefTimer = __esm({
673
- "src/unrefTimer.ts"() {
674
- "use strict";
675
- }
676
- });
677
-
678
1115
  // src/otel.ts
679
1116
  function readBoundedIntEnv(name, max, fallback, warnKey) {
680
1117
  const raw = readEnv(name);
@@ -1325,6 +1762,17 @@ __export(http_exports, {
1325
1762
  parseRetryAfterMs: () => parseRetryAfterMs,
1326
1763
  serializePayloadBody: () => serializePayloadBody
1327
1764
  });
1765
+ async function releaseHeldExternalSpans(timeoutMs) {
1766
+ const deadline = Date.now() + Math.max(timeoutMs, 0);
1767
+ for (const client of [...liveHttpClients]) {
1768
+ await client.releaseHeldExternalSpans?.(Math.max(0, deadline - Date.now()));
1769
+ }
1770
+ }
1771
+ function stopSimulationPlans() {
1772
+ for (const client of [...liveHttpClients]) {
1773
+ client.stopSimulationPlan?.();
1774
+ }
1775
+ }
1328
1776
  function awaitOnExit(promise) {
1329
1777
  pendingTracePromises.add(promise);
1330
1778
  void promise.finally(() => {
@@ -1335,7 +1783,10 @@ function awaitOnExit(promise) {
1335
1783
  }
1336
1784
  async function flushTraces(timeoutMs = 5e3) {
1337
1785
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1338
- const requestsFlushed = await awaitPendingRequests(timeoutMs);
1786
+ await releaseHeldExternalSpans(planWaitSliceMs(timeoutMs));
1787
+ const requestsFlushed = await awaitPendingRequests(
1788
+ Math.max(0, deadline - Date.now())
1789
+ );
1339
1790
  const transportsFlushed = await flushTraceTransports(
1340
1791
  Math.max(0, deadline - Date.now())
1341
1792
  );
@@ -1454,7 +1905,22 @@ function sourceTraceIdOf(payload) {
1454
1905
  const id = rawTrace?.id;
1455
1906
  return typeof id === "string" ? id : void 0;
1456
1907
  }
1457
- 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;
1908
+ function describeError(error) {
1909
+ return error instanceof Error ? error.message : String(error);
1910
+ }
1911
+ function warnDroppedExternalSpan(error) {
1912
+ warnOnce(
1913
+ "external-span-dropped",
1914
+ `a span was dropped because the send step failed: ${describeError(error)}`
1915
+ );
1916
+ }
1917
+ function warnDroppedExternalTrace(error) {
1918
+ warnOnce(
1919
+ "external-trace-dropped",
1920
+ `a trace was dropped because the send step failed: ${describeError(error)}`
1921
+ );
1922
+ }
1923
+ 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;
1458
1924
  var init_http = __esm({
1459
1925
  "src/http.ts"() {
1460
1926
  "use strict";
@@ -1463,6 +1929,7 @@ var init_http = __esm({
1463
1929
  init_errors();
1464
1930
  init_replayContext();
1465
1931
  init_serializePayload();
1932
+ init_simulationPlan();
1466
1933
  init_traceMetadata();
1467
1934
  init_transport();
1468
1935
  init_transportTypes();
@@ -1475,6 +1942,7 @@ var init_http = __esm({
1475
1942
  EXIT_FLUSH_TIMEOUT_MS = 5e3;
1476
1943
  DEFAULT_LIFECYCLE_TIMEOUT_MS2 = 3e4;
1477
1944
  pendingTracePromises = /* @__PURE__ */ new Set();
1945
+ liveHttpClients = /* @__PURE__ */ new Set();
1478
1946
  if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
1479
1947
  let isFlushing = false;
1480
1948
  process.on("beforeExit", () => {
@@ -1482,12 +1950,27 @@ var init_http = __esm({
1482
1950
  return;
1483
1951
  }
1484
1952
  isFlushing = true;
1485
- void Promise.allSettled([
1486
- ...Array.from(pendingTracePromises).map((p) => p.catch(() => {
1487
- })),
1488
- shutdownTraceTransports(EXIT_FLUSH_TIMEOUT_MS).catch(() => false)
1489
- ]).then(() => {
1953
+ const planWaitMs = SIMULATION_PLAN_READ_TIMEOUT_MS;
1954
+ const stopFlushing = () => {
1490
1955
  isFlushing = false;
1956
+ };
1957
+ const deadline = setTimeout(
1958
+ stopFlushing,
1959
+ EXIT_FLUSH_TIMEOUT_MS + planWaitMs
1960
+ );
1961
+ unrefTimer(deadline);
1962
+ void releaseHeldExternalSpans(planWaitMs).catch(() => {
1963
+ }).then(() => {
1964
+ stopSimulationPlans();
1965
+ }).then(
1966
+ () => Promise.allSettled([
1967
+ ...Array.from(pendingTracePromises).map((p) => p.catch(() => {
1968
+ })),
1969
+ shutdownTraceTransports(EXIT_FLUSH_TIMEOUT_MS).catch(() => false)
1970
+ ])
1971
+ ).then(() => {
1972
+ clearTimeout(deadline);
1973
+ stopFlushing();
1491
1974
  });
1492
1975
  });
1493
1976
  }
@@ -1506,6 +1989,7 @@ var init_http = __esm({
1506
1989
  this.apiKey = config.apiKey;
1507
1990
  this.serviceUrl = config.serviceUrl;
1508
1991
  this.timeout = config.timeout ?? 12e4;
1992
+ liveHttpClients.add(this);
1509
1993
  }
1510
1994
  /**
1511
1995
  * Resolve the API key at the moment it is needed (request time), invoking
@@ -1703,9 +2187,14 @@ var init_http = __esm({
1703
2187
  * attributing its timeout here would fail a client whose own work succeeded.
1704
2188
  */
1705
2189
  async settleDeferredWork(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS2) {
2190
+ const deadline = Date.now() + Math.max(timeoutMs, 0);
2191
+ await this.releaseHeldExternalSpans?.(planWaitSliceMs(timeoutMs));
1706
2192
  await replayContextReady.catch(() => {
1707
2193
  });
1708
- return waitForPromises(Array.from(this.deferredWork), timeoutMs);
2194
+ return waitForPromises(
2195
+ Array.from(this.deferredWork),
2196
+ Math.max(0, deadline - Date.now())
2197
+ );
1709
2198
  }
1710
2199
  /**
1711
2200
  * Wait for spans queued by this client to be delivered, within one deadline.
@@ -1713,7 +2202,9 @@ var init_http = __esm({
1713
2202
  */
1714
2203
  async waitForPendingRequests(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS2) {
1715
2204
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1716
- const settled2 = await this.settleDeferredWork(timeoutMs);
2205
+ const settled2 = await this.settleDeferredWork(
2206
+ Math.max(0, deadline - Date.now())
2207
+ );
1717
2208
  const flushed = await this.traceTransport?.flush(Math.max(0, deadline - Date.now())) ?? true;
1718
2209
  return settled2 && flushed;
1719
2210
  }
@@ -1728,6 +2219,13 @@ var init_http = __esm({
1728
2219
  }
1729
2220
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1730
2221
  this.closing = (async () => {
2222
+ await this.releaseHeldExternalSpans?.(planWaitSliceMs(timeoutMs));
2223
+ this.stopSimulationPlan?.();
2224
+ this.externalSpanTransform = void 0;
2225
+ this.externalTraceTransform = void 0;
2226
+ this.releaseHeldExternalSpans = void 0;
2227
+ this.stopSimulationPlan = void 0;
2228
+ liveHttpClients.delete(this);
1731
2229
  const settled2 = await this.settleDeferredWork(
1732
2230
  Math.max(0, deadline - Date.now())
1733
2231
  );
@@ -1839,6 +2337,13 @@ var init_http = __esm({
1839
2337
  protocol
1840
2338
  });
1841
2339
  }
2340
+ async getSimulationPlan() {
2341
+ return this.get(
2342
+ "/api/sdk/sim-plan",
2343
+ SIMULATION_PLAN_READ_TIMEOUT_MS,
2344
+ { Connection: "close" }
2345
+ );
2346
+ }
1842
2347
  async getTraceSpan(traceId, lookup) {
1843
2348
  const searchParams = new URLSearchParams();
1844
2349
  if (lookup.id !== void 0) {
@@ -1855,14 +2360,18 @@ var init_http = __esm({
1855
2360
  * GET a JSON endpoint on the service with the client's API key. Throws a
1856
2361
  * `BitfabError` carrying the status text for any non-2xx response.
1857
2362
  */
1858
- async get(endpoint) {
2363
+ async get(endpoint, timeoutMs, extraHeaders) {
1859
2364
  const url = `${this.serviceUrl}${endpoint}`;
2365
+ const timeout = timeoutMs ?? this.timeout;
1860
2366
  const controller = new AbortController();
1861
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
2367
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
1862
2368
  try {
1863
2369
  const response = await fetch(url, {
1864
2370
  method: "GET",
1865
- headers: { Authorization: `Bearer ${this.resolveApiKey() ?? ""}` },
2371
+ headers: {
2372
+ Authorization: `Bearer ${this.resolveApiKey() ?? ""}`,
2373
+ ...extraHeaders
2374
+ },
1866
2375
  signal: controller.signal
1867
2376
  });
1868
2377
  if (!response.ok) {
@@ -1881,7 +2390,7 @@ var init_http = __esm({
1881
2390
  }
1882
2391
  if (error instanceof Error) {
1883
2392
  if (error.name === "AbortError") {
1884
- throw new BitfabError(`Request timed out after ${this.timeout}ms`);
2393
+ throw new BitfabError(`Request timed out after ${timeout}ms`);
1885
2394
  }
1886
2395
  throw new BitfabError(error.message);
1887
2396
  }
@@ -1908,16 +2417,27 @@ var init_http = __esm({
1908
2417
  carrierMeta("internal_trace", body, void 0)
1909
2418
  );
1910
2419
  }
1911
- /**
1912
- * Queue an external span (from withSpan wrapper or OpenAI tracing) onto this
1913
- * client's batching transport. Fire-and-forget: the transport owns delivery,
1914
- * so callers await `flushTraces()` or `close()` rather than a per-span
1915
- * promise.
1916
- */
1917
2420
  sendExternalSpan(payload) {
2421
+ const gate = this.externalSpanTransform;
2422
+ if (gate === void 0) {
2423
+ this.submitExternalSpan(payload);
2424
+ return;
2425
+ }
2426
+ try {
2427
+ gate(payload, (ready) => this.submitExternalSpan(ready));
2428
+ } catch (error) {
2429
+ warnDroppedExternalSpan(error);
2430
+ }
2431
+ }
2432
+ submitExternalSpan(payload) {
2433
+ const body = {
2434
+ ...payload,
2435
+ sdkVersion: __version__
2436
+ };
2437
+ delete body[ROOT_TRACE_FUNCTION_KEY_FIELD];
1918
2438
  this.getTraceTransport()?.submit(
1919
2439
  "external_span",
1920
- { ...payload, sdkVersion: __version__ },
2440
+ body,
1921
2441
  this.recordedMeta("external_span", payload, carrierRef(payload))
1922
2442
  );
1923
2443
  }
@@ -1929,6 +2449,18 @@ var init_http = __esm({
1929
2449
  */
1930
2450
  sendExternalTrace(rawPayload) {
1931
2451
  const payload = mergeCallerMetadataIntoTracePayload(rawPayload);
2452
+ const gate = this.externalTraceTransform;
2453
+ if (gate === void 0) {
2454
+ this.submitExternalTrace(payload);
2455
+ return;
2456
+ }
2457
+ try {
2458
+ gate(payload, (ready) => this.submitExternalTrace(ready));
2459
+ } catch (error) {
2460
+ warnDroppedExternalTrace(error);
2461
+ }
2462
+ }
2463
+ submitExternalTrace(payload) {
1932
2464
  this.getTraceTransport()?.submit(
1933
2465
  "external_trace",
1934
2466
  {
@@ -3642,6 +4174,7 @@ function finalizeTracePayload(payload) {
3642
4174
  // src/claudeAgentSdk.ts
3643
4175
  init_randomUuid();
3644
4176
  init_serialize();
4177
+ init_spanOrigin();
3645
4178
 
3646
4179
  // src/timestamp.ts
3647
4180
  var lastTimestampMicros = 0;
@@ -3849,6 +4382,7 @@ var BitfabClaudeAgentHandler = class {
3849
4382
  trace_id: spanInfo.traceId,
3850
4383
  started_at: spanInfo.startedAt,
3851
4384
  ended_at: spanInfo.endedAt ?? nowIso(),
4385
+ span_origin: makeSpanOrigin("claude-agent-sdk"),
3852
4386
  span_data: spanData
3853
4387
  };
3854
4388
  if (spanInfo.parentId !== null) {
@@ -5160,6 +5694,7 @@ init_constants();
5160
5694
  init_http();
5161
5695
  init_randomUuid();
5162
5696
  init_serialize();
5697
+ init_spanOrigin();
5163
5698
  var LANGSMITH_HIDDEN_TAG = "langsmith:hidden";
5164
5699
  var CHAIN_RUN_TYPES = /* @__PURE__ */ new Set(["chain", "parser", "prompt"]);
5165
5700
  var LANGGRAPH_METADATA_KEYS = [
@@ -5514,6 +6049,7 @@ var BitfabLangGraphCallbackHandler = class {
5514
6049
  trace_id: spanInfo.traceId,
5515
6050
  started_at: spanInfo.startedAt,
5516
6051
  ended_at: spanInfo.endedAt ?? nowIso2(),
6052
+ span_origin: makeSpanOrigin("langgraph"),
5517
6053
  span_data: spanData
5518
6054
  };
5519
6055
  if (spanInfo.parentId !== null) {
@@ -5775,14 +6311,14 @@ var BitfabLangGraphCallbackHandler = class {
5775
6311
  init_http();
5776
6312
  init_replayContext();
5777
6313
  var TOOL_RESULT_TAG = "__bitfabLangGraphToolResult";
5778
- function isRecord(value) {
6314
+ function isRecord3(value) {
5779
6315
  return typeof value === "object" && value !== null;
5780
6316
  }
5781
6317
  function isToolMessage(value) {
5782
- return isRecord(value) && value.lc_direct_tool_output === true && value.type === "tool" && (typeof value.content === "string" || Array.isArray(value.content));
6318
+ return isRecord3(value) && value.lc_direct_tool_output === true && value.type === "tool" && (typeof value.content === "string" || Array.isArray(value.content));
5783
6319
  }
5784
6320
  function isCommand(value) {
5785
- return isRecord(value) && value.lg_name === "Command";
6321
+ return isRecord3(value) && value.lg_name === "Command";
5786
6322
  }
5787
6323
  function encodeNested(value) {
5788
6324
  if (isToolMessage(value) || isCommand(value)) {
@@ -5791,7 +6327,7 @@ function encodeNested(value) {
5791
6327
  if (Array.isArray(value)) {
5792
6328
  return value.map(encodeNested);
5793
6329
  }
5794
- if (isRecord(value)) {
6330
+ if (isRecord3(value)) {
5795
6331
  return Object.fromEntries(
5796
6332
  Object.entries(value).map(([key, entry]) => [key, encodeNested(entry)])
5797
6333
  );
@@ -5814,17 +6350,17 @@ function encodeNativeToolResult(value) {
5814
6350
  }
5815
6351
  return {
5816
6352
  [TOOL_RESULT_TAG]: "command",
5817
- graph: isRecord(value) && typeof value.graph === "string" ? value.graph : void 0,
5818
- update: isRecord(value) ? encodeNested(value.update) : void 0,
5819
- resume: isRecord(value) ? encodeNested(value.resume) : void 0,
5820
- goto: isRecord(value) ? encodeNested(value.goto) : void 0
6353
+ graph: isRecord3(value) && typeof value.graph === "string" ? value.graph : void 0,
6354
+ update: isRecord3(value) ? encodeNested(value.update) : void 0,
6355
+ resume: isRecord3(value) ? encodeNested(value.resume) : void 0,
6356
+ goto: isRecord3(value) ? encodeNested(value.goto) : void 0
5821
6357
  };
5822
6358
  }
5823
6359
  function finalizeToolResult(value) {
5824
6360
  return isToolMessage(value) || isCommand(value) ? encodeNativeToolResult(value) : value;
5825
6361
  }
5826
6362
  function isEncodedToolResult(value) {
5827
- return isRecord(value) && typeof value[TOOL_RESULT_TAG] === "string";
6363
+ return isRecord3(value) && typeof value[TOOL_RESULT_TAG] === "string";
5828
6364
  }
5829
6365
  async function loadLangChainCore() {
5830
6366
  try {
@@ -5860,7 +6396,7 @@ async function reviveNested(value, toolCallId) {
5860
6396
  if (Array.isArray(value)) {
5861
6397
  return Promise.all(value.map((entry) => reviveNested(entry, toolCallId)));
5862
6398
  }
5863
- if (isRecord(value)) {
6399
+ if (isRecord3(value)) {
5864
6400
  const entries = await Promise.all(
5865
6401
  Object.entries(value).map(async ([key, entry]) => [
5866
6402
  key,
@@ -5885,16 +6421,16 @@ async function reviveToolResult(value, toolCallId) {
5885
6421
  ...value.artifact !== void 0 && {
5886
6422
  artifact: await reviveNested(value.artifact, toolCallId)
5887
6423
  },
5888
- ...isRecord(value.metadata) && {
6424
+ ...isRecord3(value.metadata) && {
5889
6425
  metadata: await reviveNested(value.metadata, toolCallId)
5890
6426
  },
5891
- ...isRecord(value.additionalKwargs) && {
6427
+ ...isRecord3(value.additionalKwargs) && {
5892
6428
  additional_kwargs: await reviveNested(
5893
6429
  value.additionalKwargs,
5894
6430
  toolCallId
5895
6431
  )
5896
6432
  },
5897
- ...isRecord(value.responseMetadata) && {
6433
+ ...isRecord3(value.responseMetadata) && {
5898
6434
  response_metadata: await reviveNested(
5899
6435
  value.responseMetadata,
5900
6436
  toolCallId
@@ -5964,8 +6500,8 @@ var BitfabLangGraphIntegration = class {
5964
6500
  get: (target, property) => {
5965
6501
  if (property === "invoke") {
5966
6502
  return async (input, ...rest) => {
5967
- const toolCallId = isRecord(input) && typeof input.id === "string" ? input.id : "";
5968
- const args = isRecord(input) && "args" in input ? input.args : input;
6503
+ const toolCallId = isRecord3(input) && typeof input.id === "string" ? input.id : "";
6504
+ const args = isRecord3(input) && "args" in input ? input.args : input;
5969
6505
  this.assertReplayToolResultExists(toolName, shouldMock);
5970
6506
  const execute = this.client.withSpan(
5971
6507
  this.traceFunctionKey,
@@ -5975,7 +6511,8 @@ var BitfabLangGraphIntegration = class {
5975
6511
  captureWhen: "nested",
5976
6512
  mockOnReplay: shouldMock,
5977
6513
  finalize: finalizeToolResult,
5978
- surface: "inherit"
6514
+ surface: "inherit",
6515
+ instrumentation: "langgraph"
5979
6516
  },
5980
6517
  async (_args) => await originalInvoke(input, ...rest)
5981
6518
  );
@@ -6018,7 +6555,12 @@ var BitfabLangGraphIntegration = class {
6018
6555
  wrapInvoke(fn) {
6019
6556
  return this.client.withSpan(
6020
6557
  this.traceFunctionKey,
6021
- { name: this.traceFunctionKey, type: "agent", surface: "inherit" },
6558
+ {
6559
+ name: this.traceFunctionKey,
6560
+ type: "agent",
6561
+ surface: "inherit",
6562
+ instrumentation: "langgraph"
6563
+ },
6022
6564
  fn
6023
6565
  );
6024
6566
  }
@@ -6074,7 +6616,8 @@ var BitfabOpenAIAgentHandler = class {
6074
6616
  const options_ = {
6075
6617
  type: "agent",
6076
6618
  finalize,
6077
- surface: "inherit"
6619
+ surface: "inherit",
6620
+ instrumentation: "openai-agents"
6078
6621
  };
6079
6622
  const traced = this.withSpanFn(
6080
6623
  this.traceFunctionKey,
@@ -6090,6 +6633,7 @@ var BitfabOpenAIAgentHandler = class {
6090
6633
  };
6091
6634
 
6092
6635
  // src/client.ts
6636
+ init_policyRefresh();
6093
6637
  init_randomUuid();
6094
6638
  init_replay();
6095
6639
 
@@ -6173,12 +6717,26 @@ function runWithSeedContext(ctx, fn) {
6173
6717
 
6174
6718
  // src/client.ts
6175
6719
  init_serialize();
6720
+ init_simulationPlan();
6721
+ init_spanOrigin();
6722
+
6723
+ // src/streamFinalizationError.ts
6724
+ var StreamFinalizationError = class extends Error {
6725
+ constructor(message, output) {
6726
+ super(message);
6727
+ this.output = output;
6728
+ this.name = "StreamFinalizationError";
6729
+ }
6730
+ };
6731
+
6732
+ // src/client.ts
6176
6733
  init_traceMetadata();
6177
6734
 
6178
6735
  // src/tracing.ts
6179
6736
  init_constants();
6180
6737
  init_http();
6181
6738
  init_randomUuid();
6739
+ init_spanOrigin();
6182
6740
  var BitfabOpenAITracingProcessor = class {
6183
6741
  /**
6184
6742
  * Initialize the tracing processor.
@@ -6380,6 +6938,7 @@ var BitfabOpenAITracingProcessor = class {
6380
6938
  * Build span payload for the external spans API.
6381
6939
  */
6382
6940
  buildSpanPayload(serializedSpan, errors) {
6941
+ serializedSpan.span_origin = makeSpanOrigin("openai-agents");
6383
6942
  const payload = {
6384
6943
  id: randomUuid(),
6385
6944
  type: "openai",
@@ -6444,52 +7003,110 @@ function summarizeGenerate(result, model) {
6444
7003
  }
6445
7004
  return summary;
6446
7005
  }
6447
- function accumulateStream(onComplete, model) {
7006
+ function accumulateStream(source, onComplete, model) {
7007
+ const reader = source.getReader();
6448
7008
  let text = "";
6449
7009
  const toolCalls = [];
6450
7010
  let usage;
6451
7011
  let finishReason;
6452
7012
  let completed = false;
6453
- const complete = () => {
7013
+ let streamFailure;
7014
+ const complete = (failure = streamFailure) => {
6454
7015
  if (completed) {
6455
7016
  return;
6456
7017
  }
6457
7018
  completed = true;
6458
- const summary = {
7019
+ let error;
7020
+ if (failure !== void 0) {
7021
+ try {
7022
+ error = failure.error instanceof Error ? failure.error.message : String(
7023
+ failure.error ?? (failure.cancelled === true ? "Stream cancelled" : "Stream failed")
7024
+ );
7025
+ } catch {
7026
+ error = "Stream failed";
7027
+ }
7028
+ }
7029
+ onComplete({
6459
7030
  text,
6460
7031
  toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
6461
7032
  usage,
6462
- finishReason
6463
- };
6464
- if (model) {
6465
- summary.model = model;
6466
- }
6467
- onComplete(summary);
7033
+ finishReason,
7034
+ ...model !== void 0 && { model },
7035
+ ...failure !== void 0 && { error },
7036
+ ...failure?.cancelled === true && { cancelled: true }
7037
+ });
6468
7038
  };
6469
- return new TransformStream({
6470
- transform(part, controller) {
6471
- try {
6472
- if (part?.type === "text-delta") {
6473
- text += part.delta ?? part.textDelta ?? "";
6474
- } else if (part?.type === "tool-call") {
6475
- toolCalls.push({
6476
- toolCallId: part.toolCallId,
6477
- toolName: part.toolName,
6478
- input: part.input ?? part.args
6479
- });
6480
- } else if (part?.type === "finish") {
6481
- usage = part.usage;
6482
- finishReason = part.finishReason;
6483
- complete();
7039
+ return new ReadableStream(
7040
+ {
7041
+ start(controller) {
7042
+ void reader.closed.then(
7043
+ () => {
7044
+ queueMicrotask(() => {
7045
+ if (!completed) {
7046
+ complete();
7047
+ reader.releaseLock();
7048
+ controller.close();
7049
+ }
7050
+ });
7051
+ },
7052
+ (error) => {
7053
+ if (!completed) {
7054
+ complete({ error });
7055
+ controller.error(error);
7056
+ reader.releaseLock();
7057
+ }
7058
+ }
7059
+ );
7060
+ },
7061
+ async pull(controller) {
7062
+ try {
7063
+ const { done, value: part } = await reader.read();
7064
+ if (completed) {
7065
+ return;
7066
+ }
7067
+ if (done) {
7068
+ complete();
7069
+ reader.releaseLock();
7070
+ controller.close();
7071
+ return;
7072
+ }
7073
+ try {
7074
+ if (part?.type === "text-delta") {
7075
+ text += part.delta ?? part.textDelta ?? "";
7076
+ } else if (part?.type === "tool-call") {
7077
+ toolCalls.push({
7078
+ toolCallId: part.toolCallId,
7079
+ toolName: part.toolName,
7080
+ input: part.input ?? part.args
7081
+ });
7082
+ } else if (part?.type === "error") {
7083
+ streamFailure = { error: part.error };
7084
+ } else if (part?.type === "finish") {
7085
+ usage = part.usage;
7086
+ finishReason = part.finishReason;
7087
+ }
7088
+ } catch {
7089
+ }
7090
+ controller.enqueue(part);
7091
+ } catch (error) {
7092
+ if (!completed) {
7093
+ complete({ error });
7094
+ reader.releaseLock();
7095
+ controller.error(error);
7096
+ }
7097
+ }
7098
+ },
7099
+ async cancel(reason) {
7100
+ complete({ error: reason, cancelled: true });
7101
+ try {
7102
+ await reader.cancel(reason);
7103
+ } finally {
7104
+ reader.releaseLock();
6484
7105
  }
6485
- } catch {
6486
7106
  }
6487
- controller.enqueue(part);
6488
7107
  },
6489
- flush() {
6490
- complete();
6491
- }
6492
- });
7108
+ { highWaterMark: 0 }
7109
+ );
6493
7110
  }
6494
7111
  var BitfabVercelAiHandler = class {
6495
7112
  constructor(config) {
@@ -6509,7 +7126,8 @@ var BitfabVercelAiHandler = class {
6509
7126
  {
6510
7127
  type: "llm",
6511
7128
  finalize: (result) => summarizeGenerate(result ?? {}, label),
6512
- surface: "inherit"
7129
+ surface: "inherit",
7130
+ instrumentation: "vercel-ai"
6513
7131
  },
6514
7132
  () => doGenerate()
6515
7133
  );
@@ -6527,11 +7145,24 @@ var BitfabVercelAiHandler = class {
6527
7145
  // The wrapped fn returns immediately with the live stream, so the span
6528
7146
  // output cannot be read from the return value. `finalize` instead
6529
7147
  // awaits the summary the accumulator resolves once the stream drains.
6530
- { type: "llm", finalize: () => summary, surface: "inherit" },
7148
+ {
7149
+ type: "llm",
7150
+ finalize: async () => {
7151
+ const output = await summary;
7152
+ if (output.error !== void 0) {
7153
+ throw new StreamFinalizationError(output.error, output);
7154
+ }
7155
+ return output;
7156
+ },
7157
+ surface: "inherit",
7158
+ instrumentation: "vercel-ai"
7159
+ },
6531
7160
  async () => {
6532
7161
  const result = await doStream();
6533
- const stream = result.stream.pipeThrough(
6534
- accumulateStream(resolveSummary, label)
7162
+ const stream = accumulateStream(
7163
+ result.stream,
7164
+ resolveSummary,
7165
+ label
6535
7166
  );
6536
7167
  return { ...result, stream };
6537
7168
  }
@@ -6545,6 +7176,19 @@ var BitfabVercelAiHandler = class {
6545
7176
  // src/client.ts
6546
7177
  init_warnOnce();
6547
7178
  var activeTraceStates = /* @__PURE__ */ new Map();
7179
+ function rootTraceFunctionKeyOf(payload) {
7180
+ const captured = payload[ROOT_TRACE_FUNCTION_KEY_FIELD];
7181
+ if (typeof captured === "string") {
7182
+ return captured;
7183
+ }
7184
+ const traceId = payload.traceId;
7185
+ const live = typeof traceId === "string" ? activeTraceStates.get(traceId)?.traceFunctionKey : void 0;
7186
+ if (typeof live === "string") {
7187
+ return live;
7188
+ }
7189
+ const own = payload.traceFunctionKey;
7190
+ return typeof own === "string" ? own : void 0;
7191
+ }
6548
7192
  var asyncLocalStorage = null;
6549
7193
  var SPAN_CONTEXT_STORAGE_SYMBOL = /* @__PURE__ */ Symbol.for("bitfab.spanContextStorage");
6550
7194
  var initializeAsyncContext = () => {
@@ -6949,6 +7593,14 @@ var Bitfab = class {
6949
7593
  serviceUrl: this.serviceUrl,
6950
7594
  timeout: this.timeout
6951
7595
  });
7596
+ this.simulationPlan = new SimulationPlan(
7597
+ this.httpClient,
7598
+ config.simulationPlan ?? true
7599
+ );
7600
+ this.httpClient.externalSpanTransform = (payload, submit) => this.applySimulationPlan(payload, submit);
7601
+ this.httpClient.externalTraceTransform = (payload, submit) => this.simulationPlan.sendTrace(payload, submit);
7602
+ this.httpClient.releaseHeldExternalSpans = (timeoutMs) => this.simulationPlan.release(timeoutMs);
7603
+ this.httpClient.stopSimulationPlan = () => this.simulationPlan.stop();
6952
7604
  this.datasets = new DatasetsClient(this.httpClient);
6953
7605
  this.traces = new TracesClient(this.httpClient);
6954
7606
  this.labels = new LabelsClient(this.httpClient);
@@ -7099,6 +7751,7 @@ var Bitfab = class {
7099
7751
  }
7100
7752
  createAutoTraceRoot(traceFunctionKey, name, options, fn) {
7101
7753
  const self = this;
7754
+ self.simulationPlan.refresh();
7102
7755
  const maxDepth = autoTraceLimit(
7103
7756
  options.maxDepth,
7104
7757
  DEFAULT_AUTO_TRACE_MAX_DEPTH
@@ -7114,7 +7767,8 @@ var Bitfab = class {
7114
7767
  name: options.name,
7115
7768
  nameFor: rootNameFor,
7116
7769
  type: options.type ?? "custom",
7117
- surface: "opt-out"
7770
+ surface: "opt-out",
7771
+ instrumentation: "trace"
7118
7772
  };
7119
7773
  const buildTracedRoot = (spanOptions) => this.withSpan(
7120
7774
  traceFunctionKey,
@@ -7125,6 +7779,7 @@ var Bitfab = class {
7125
7779
  traceFunctionKey
7126
7780
  );
7127
7781
  self.refreshAutoTraceCapturePolicy(traceFunctionKey);
7782
+ self.simulationPlan.refresh();
7128
7783
  let spansUsed = 0;
7129
7784
  let truncated = false;
7130
7785
  const warnTruncated = () => {
@@ -7201,6 +7856,7 @@ var Bitfab = class {
7201
7856
  type: nodeConfiguration?.type ?? "function",
7202
7857
  captureWhen: "nested",
7203
7858
  surface: "opt-out",
7859
+ instrumentation: "trace",
7204
7860
  functionId: definition.id,
7205
7861
  captureContent: nodeConfiguration !== void 0 || capturePolicy === void 0 || capturePolicy.has(definition.id),
7206
7862
  autoTraceDefinition: definition,
@@ -7232,6 +7888,7 @@ var Bitfab = class {
7232
7888
  type: "function",
7233
7889
  captureWhen: "nested",
7234
7890
  surface: "opt-out",
7891
+ instrumentation: "trace",
7235
7892
  captureContent: true,
7236
7893
  ...link !== void 0 && { nestedTrace: link }
7237
7894
  };
@@ -7297,38 +7954,36 @@ var Bitfab = class {
7297
7954
  Object.defineProperty(autoTraceRoot, "_bitfabWrappedFn", { value: fn });
7298
7955
  return autoTraceRoot;
7299
7956
  }
7957
+ applySimulationPlan(payload, submit) {
7958
+ this.simulationPlan.send(payload, rootTraceFunctionKeyOf(payload), submit);
7959
+ }
7300
7960
  refreshAutoTraceCapturePolicy(traceFunctionKey) {
7301
- const now = Date.now();
7302
- const state = this.autoTracePolicyRefreshes.get(traceFunctionKey) ?? {
7303
- refreshAfter: 0
7304
- };
7305
- if (state.inFlight || now < state.refreshAfter) {
7306
- return;
7307
- }
7308
- const request = this.httpClient.getAutoTracePolicy(
7309
- traceFunctionKey,
7310
- AUTO_TRACE_PROTOCOL
7311
- ).then((policy) => {
7312
- if (policy.protocol !== AUTO_TRACE_PROTOCOL) {
7313
- state.refreshAfter = Date.now() + AUTO_TRACE_POLICY_RETRY_MS;
7314
- return;
7315
- }
7316
- const functionIds = Array.isArray(policy.functionIds) ? policy.functionIds.filter(
7317
- (id) => typeof id === "string" && id.startsWith(`${AUTO_TRACE_PROTOCOL}:`)
7318
- ).slice(0, DEFAULT_AUTO_TRACE_MAX_SPANS) : [];
7319
- __setBitfabAutoTraceCapturePolicy(
7320
- this,
7321
- traceFunctionKey,
7322
- policy.revision === null ? void 0 : functionIds
7323
- );
7324
- state.refreshAfter = Date.now() + AUTO_TRACE_POLICY_REFRESH_MS;
7325
- }).catch(() => {
7326
- state.refreshAfter = Date.now() + AUTO_TRACE_POLICY_RETRY_MS;
7327
- }).finally(() => {
7328
- state.inFlight = void 0;
7329
- });
7330
- state.inFlight = request;
7961
+ const state = this.autoTracePolicyRefreshes.get(traceFunctionKey) ?? new PolicyRefresh();
7331
7962
  this.autoTracePolicyRefreshes.set(traceFunctionKey, state);
7963
+ state.run(
7964
+ () => this.httpClient.getAutoTracePolicy(
7965
+ traceFunctionKey,
7966
+ AUTO_TRACE_PROTOCOL
7967
+ ),
7968
+ (policy) => {
7969
+ if (policy.protocol !== AUTO_TRACE_PROTOCOL) {
7970
+ state.hold(AUTO_TRACE_POLICY_RETRY_MS);
7971
+ return;
7972
+ }
7973
+ const functionIds = Array.isArray(policy.functionIds) ? policy.functionIds.filter(
7974
+ (id) => typeof id === "string" && id.startsWith(`${AUTO_TRACE_PROTOCOL}:`)
7975
+ ).slice(0, DEFAULT_AUTO_TRACE_MAX_SPANS) : [];
7976
+ __setBitfabAutoTraceCapturePolicy(
7977
+ this,
7978
+ traceFunctionKey,
7979
+ policy.revision === null ? void 0 : functionIds
7980
+ );
7981
+ state.hold(AUTO_TRACE_POLICY_REFRESH_MS);
7982
+ },
7983
+ () => {
7984
+ state.hold(AUTO_TRACE_POLICY_RETRY_MS);
7985
+ }
7986
+ );
7332
7987
  }
7333
7988
  /**
7334
7989
  * Flush and permanently close this client's tracing resources: its pending
@@ -7812,6 +8467,7 @@ var Bitfab = class {
7812
8467
  * @returns A wrapped function with the same signature that creates spans for inputs and outputs
7813
8468
  */
7814
8469
  withSpan(traceFunctionKey, optionsOrFn, maybeFn) {
8470
+ this.simulationPlan.refresh();
7815
8471
  const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
7816
8472
  const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
7817
8473
  const self = this;
@@ -7903,8 +8559,10 @@ var Bitfab = class {
7903
8559
  const testRunId = replayCtxAtStart?.testRunId ?? options.testRunId;
7904
8560
  if (isRootSpan && !activeTraceStates.has(traceId)) {
7905
8561
  const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
8562
+ self.simulationPlan.refresh();
7906
8563
  activeTraceStates.set(traceId, {
7907
8564
  traceId,
8565
+ traceFunctionKey,
7908
8566
  startedAt,
7909
8567
  contexts: [],
7910
8568
  ...testRunId !== void 0 && { testRunId },
@@ -7919,8 +8577,10 @@ var Bitfab = class {
7919
8577
  registeredTraceId = traceId;
7920
8578
  }
7921
8579
  const functionName = fn.name !== "" ? fn.name : void 0;
8580
+ const rootTraceFunctionKey = activeTraceStates.get(traceId)?.traceFunctionKey ?? traceFunctionKey;
7922
8581
  const baseSpanParams = {
7923
8582
  traceFunctionKey,
8583
+ rootTraceFunctionKey,
7924
8584
  functionName,
7925
8585
  spanName: options.name ?? options.nameFor?.(this) ?? qualifiedSpanName(this, functionName) ?? traceFunctionKey,
7926
8586
  traceId,
@@ -7929,6 +8589,7 @@ var Bitfab = class {
7929
8589
  inputs,
7930
8590
  startedAt,
7931
8591
  spanType: options.type ?? "custom",
8592
+ instrumentation: options.instrumentation ?? "span",
7932
8593
  functionId: options.functionId,
7933
8594
  captureContent: options.captureContent ?? true,
7934
8595
  autoTraceDefinition: options.autoTraceDefinition,
@@ -7996,8 +8657,8 @@ var Bitfab = class {
7996
8657
  void self.httpClient.trackDeferred(
7997
8658
  Promise.resolve().then(() => options.finalize(result)).then((output) => sendSpan({ result: output })).catch(
7998
8659
  (error) => sendSpan({
7999
- result: void 0,
8000
- error: error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
8660
+ result: error instanceof StreamFinalizationError ? error.output : void 0,
8661
+ error: error instanceof StreamFinalizationError ? error.message : error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
8001
8662
  })
8002
8663
  )
8003
8664
  );
@@ -8344,6 +9005,7 @@ var Bitfab = class {
8344
9005
  * @returns A BitfabFunction instance for wrapping functions
8345
9006
  */
8346
9007
  getFunction(traceFunctionKey) {
9008
+ this.simulationPlan.refresh();
8347
9009
  return new BitfabFunction(this, traceFunctionKey);
8348
9010
  }
8349
9011
  /**
@@ -8431,6 +9093,7 @@ var Bitfab = class {
8431
9093
  trace_id: params.traceId,
8432
9094
  started_at: params.startedAt,
8433
9095
  ended_at: params.endedAt,
9096
+ span_origin: makeSpanOrigin(params.instrumentation),
8434
9097
  span_data: {
8435
9098
  name: params.spanName,
8436
9099
  type: params.spanType,
@@ -8491,6 +9154,9 @@ var Bitfab = class {
8491
9154
  source: "typescript-sdk-function",
8492
9155
  sourceTraceId: params.traceId,
8493
9156
  traceFunctionKey: params.traceFunctionKey,
9157
+ ...params.rootTraceFunctionKey !== void 0 && {
9158
+ [ROOT_TRACE_FUNCTION_KEY_FIELD]: params.rootTraceFunctionKey
9159
+ },
8494
9160
  rawSpan: externalSpan,
8495
9161
  ...params.testRunId && { testRunId: params.testRunId },
8496
9162
  ...params.mocked && { mocked: true },
@@ -8559,6 +9225,7 @@ var Bitfab = class {
8559
9225
  }
8560
9226
  activeTraceStates.set(traceId, {
8561
9227
  traceId,
9228
+ traceFunctionKey,
8562
9229
  startedAt,
8563
9230
  contexts: [],
8564
9231
  ingestionType: "seeded",
@@ -8568,6 +9235,7 @@ var Bitfab = class {
8568
9235
  try {
8569
9236
  this.sendWrapperSpan({
8570
9237
  traceFunctionKey,
9238
+ rootTraceFunctionKey: traceFunctionKey,
8571
9239
  spanName: options.spanName ?? traceFunctionKey,
8572
9240
  traceId,
8573
9241
  spanId: randomUuid(),
@@ -8577,6 +9245,7 @@ var Bitfab = class {
8577
9245
  startedAt,
8578
9246
  endedAt: startedAt,
8579
9247
  spanType: options.spanType ?? "agent",
9248
+ instrumentation: "span",
8580
9249
  captureContent: true
8581
9250
  });
8582
9251
  this.sendTraceCompletion({
@@ -8622,6 +9291,7 @@ var Bitfab = class {
8622
9291
  }
8623
9292
  activeTraceStates.set(traceId, {
8624
9293
  traceId,
9294
+ traceFunctionKey,
8625
9295
  startedAt: nowIsoTimestamp(),
8626
9296
  contexts: [],
8627
9297
  ingestionType: "seeded",