bitfab 0.52.6 → 0.53.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/node.cjs CHANGED
@@ -97,7 +97,7 @@ var __version__, __packageName__;
97
97
  var init_version_generated = __esm({
98
98
  "src/version.generated.ts"() {
99
99
  "use strict";
100
- __version__ = "0.52.6";
100
+ __version__ = "0.53.1";
101
101
  __packageName__ = "bitfab";
102
102
  }
103
103
  });
@@ -523,8 +523,8 @@ function encodePayloadBody(payload) {
523
523
  const marker = { error: `payload_serialize_failed: ${message}` };
524
524
  return { body: JSON.stringify(marker), dropped, value: marker };
525
525
  }
526
- const isRecord2 = typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized);
527
- if (dropped.length > 0 && isRecord2) {
526
+ const isRecord4 = typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized);
527
+ if (dropped.length > 0 && isRecord4) {
528
528
  const obj = sanitized;
529
529
  const existing = Array.isArray(obj.errors) ? obj.errors : [];
530
530
  obj.errors = [
@@ -541,7 +541,7 @@ function encodePayloadBody(payload) {
541
541
  return {
542
542
  body: JSON.stringify(sanitized),
543
543
  dropped,
544
- value: isRecord2 ? sanitized : void 0
544
+ value: isRecord4 ? sanitized : void 0
545
545
  };
546
546
  }
547
547
  }
@@ -553,6 +553,456 @@ var init_serializePayload = __esm({
553
553
  }
554
554
  });
555
555
 
556
+ // src/policyRefresh.ts
557
+ var PolicyRefresh;
558
+ var init_policyRefresh = __esm({
559
+ "src/policyRefresh.ts"() {
560
+ "use strict";
561
+ PolicyRefresh = class {
562
+ constructor() {
563
+ this.refreshAfter = 0;
564
+ }
565
+ due() {
566
+ return this.inFlight === void 0 && Date.now() >= this.refreshAfter;
567
+ }
568
+ hold(durationMs) {
569
+ this.refreshAfter = Date.now() + durationMs;
570
+ }
571
+ releaseHold() {
572
+ this.refreshAfter = 0;
573
+ }
574
+ run(read2, onLoaded, onFailed) {
575
+ if (!this.due()) {
576
+ return;
577
+ }
578
+ let request;
579
+ try {
580
+ request = read2().then(onLoaded).catch(onFailed).finally(() => {
581
+ this.inFlight = void 0;
582
+ });
583
+ } catch (error) {
584
+ onFailed(error);
585
+ return;
586
+ }
587
+ this.inFlight = request;
588
+ }
589
+ };
590
+ }
591
+ });
592
+
593
+ // src/spanOrigin.ts
594
+ function isFrameworkInstrumentation(value) {
595
+ return typeof value === "string" && FRAMEWORK_INSTRUMENTATION_SET.has(value);
596
+ }
597
+ function makeSpanOrigin(instrumentation) {
598
+ return {
599
+ name: SPAN_ORIGIN_NAME,
600
+ version: __version__,
601
+ instrumentation: { name: instrumentation }
602
+ };
603
+ }
604
+ function isRecord(value) {
605
+ return typeof value === "object" && value !== null && !Array.isArray(value);
606
+ }
607
+ function spanOriginOf(payload) {
608
+ const rawSpan = payload.rawSpan;
609
+ if (!isRecord(rawSpan)) {
610
+ return void 0;
611
+ }
612
+ const origin = rawSpan.span_origin;
613
+ if (!isRecord(origin) || !isRecord(origin.instrumentation)) {
614
+ return void 0;
615
+ }
616
+ const { name, version } = origin;
617
+ const instrumentation = origin.instrumentation.name;
618
+ if (typeof name !== "string" || typeof version !== "string" || typeof instrumentation !== "string" || !SPAN_INSTRUMENTATIONS.includes(instrumentation)) {
619
+ return void 0;
620
+ }
621
+ return {
622
+ name,
623
+ version,
624
+ instrumentation: { name: instrumentation }
625
+ };
626
+ }
627
+ function spanInstrumentationOf(payload) {
628
+ return spanOriginOf(payload)?.instrumentation.name;
629
+ }
630
+ function recordedByFramework(payload) {
631
+ return isFrameworkInstrumentation(spanInstrumentationOf(payload));
632
+ }
633
+ var SPAN_ORIGIN_NAME, SPAN_INSTRUMENTATIONS, FRAMEWORK_INSTRUMENTATIONS, FRAMEWORK_INSTRUMENTATION_SET;
634
+ var init_spanOrigin = __esm({
635
+ "src/spanOrigin.ts"() {
636
+ "use strict";
637
+ init_constants();
638
+ SPAN_ORIGIN_NAME = "bitfab.sdk.typescript";
639
+ SPAN_INSTRUMENTATIONS = [
640
+ "span",
641
+ "trace",
642
+ "openai-agents",
643
+ "langgraph",
644
+ "claude-agent-sdk",
645
+ "vercel-ai"
646
+ ];
647
+ FRAMEWORK_INSTRUMENTATIONS = [
648
+ "openai-agents",
649
+ "langgraph",
650
+ "claude-agent-sdk",
651
+ "vercel-ai"
652
+ ];
653
+ FRAMEWORK_INSTRUMENTATION_SET = new Set(
654
+ FRAMEWORK_INSTRUMENTATIONS
655
+ );
656
+ }
657
+ });
658
+
659
+ // src/unrefTimer.ts
660
+ function unrefTimer(timer) {
661
+ const handle = timer;
662
+ if (typeof handle.unref === "function") {
663
+ handle.unref();
664
+ }
665
+ }
666
+ var init_unrefTimer = __esm({
667
+ "src/unrefTimer.ts"() {
668
+ "use strict";
669
+ }
670
+ });
671
+
672
+ // src/simulationPlan.ts
673
+ function planWaitSliceMs(timeoutMs) {
674
+ return Math.min(SIMULATION_PLAN_READ_TIMEOUT_MS, Math.max(timeoutMs, 0) / 2);
675
+ }
676
+ function parseSimulationPlan(body) {
677
+ if (typeof body !== "object" || body === null) {
678
+ return null;
679
+ }
680
+ const nodes = body.nodes;
681
+ if (!Array.isArray(nodes)) {
682
+ return null;
683
+ }
684
+ const contentOff = /* @__PURE__ */ new Map();
685
+ for (const node of nodes) {
686
+ if (typeof node !== "object" || node === null) {
687
+ continue;
688
+ }
689
+ const { traceFunctionKey, name, captureContent } = node;
690
+ if (typeof traceFunctionKey !== "string" || typeof name !== "string" || typeof captureContent !== "boolean") {
691
+ continue;
692
+ }
693
+ if (captureContent) {
694
+ continue;
695
+ }
696
+ const names = contentOff.get(traceFunctionKey) ?? /* @__PURE__ */ new Set();
697
+ names.add(name);
698
+ contentOff.set(traceFunctionKey, names);
699
+ }
700
+ return contentOff;
701
+ }
702
+ function traceIdOf(payload) {
703
+ const traceId = payload.traceId;
704
+ if (typeof traceId === "string") {
705
+ return traceId;
706
+ }
707
+ const id = payload.id;
708
+ return typeof id === "string" ? id : void 0;
709
+ }
710
+ function isRecord2(value) {
711
+ return typeof value === "object" && value !== null;
712
+ }
713
+ function rawSpanOf(payload) {
714
+ return isRecord2(payload.rawSpan) ? payload.rawSpan : void 0;
715
+ }
716
+ function spanDataOf(payload) {
717
+ const spanData = rawSpanOf(payload)?.span_data;
718
+ return isRecord2(spanData) ? spanData : void 0;
719
+ }
720
+ function spanNameOf(payload) {
721
+ const name = spanDataOf(payload)?.name;
722
+ return typeof name === "string" ? name : void 0;
723
+ }
724
+ function stripContent(payload) {
725
+ const rawSpan = rawSpanOf(payload);
726
+ const spanData = spanDataOf(payload);
727
+ if (rawSpan === void 0 || spanData === void 0) {
728
+ return payload;
729
+ }
730
+ const kept = { ...spanData };
731
+ for (const key of CONTENT_KEYS) {
732
+ delete kept[key];
733
+ }
734
+ kept[CONTENT_OFF_KEY] = true;
735
+ return { ...payload, rawSpan: { ...rawSpan, span_data: kept } };
736
+ }
737
+ function simulationPlanEnvDisabled() {
738
+ const value = readEnv(DISABLE_SIMULATION_PLAN_ENV);
739
+ return typeof value === "string" && value.trim() !== "";
740
+ }
741
+ function missingSimulationPlanEndpoint(error) {
742
+ return error instanceof BitfabError && error.status === 404;
743
+ }
744
+ 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;
745
+ var init_simulationPlan = __esm({
746
+ "src/simulationPlan.ts"() {
747
+ "use strict";
748
+ init_errors();
749
+ init_policyRefresh();
750
+ init_readEnv();
751
+ init_spanOrigin();
752
+ init_unrefTimer();
753
+ init_warnOnce();
754
+ SIMULATION_PLAN_REFRESH_MS = 6e4;
755
+ SIMULATION_PLAN_RETRY_MS = 1e4;
756
+ SIMULATION_PLAN_READ_TIMEOUT_MS = 5e3;
757
+ SIMULATION_PLAN_MAX_HELD = 1e3;
758
+ DISABLE_SIMULATION_PLAN_ENV = "BITFAB_DISABLE_SIM_PLAN";
759
+ ROOT_TRACE_FUNCTION_KEY_FIELD = "rootTraceFunctionKey";
760
+ CONTENT_OFF_KEY = "content_off_by_simulation_plan";
761
+ CONTENT_KEYS = ["input", "input_meta", "output", "output_meta"];
762
+ SimulationPlan = class {
763
+ constructor(source, enabled = true) {
764
+ this.source = source;
765
+ this.enabled = enabled;
766
+ this.policyRefresh = new PolicyRefresh();
767
+ this.retryPending = false;
768
+ this.waitingForFirstLoad = 0;
769
+ this.held = [];
770
+ this.droppedWhileHolding = 0;
771
+ this.stopped = false;
772
+ this.loaded = new Promise((resolve) => {
773
+ this.resolveLoaded = resolve;
774
+ });
775
+ }
776
+ disabled() {
777
+ return !this.enabled || simulationPlanEnvDisabled();
778
+ }
779
+ refresh() {
780
+ if (this.stopped || this.disabled()) {
781
+ return;
782
+ }
783
+ this.policyRefresh.run(
784
+ () => this.source.getSimulationPlan(),
785
+ (body) => {
786
+ const parsed = parseSimulationPlan(body);
787
+ if (parsed === null) {
788
+ this.scheduleRetry(
789
+ "sim-plan-unreadable",
790
+ "the sim plan response was not understood"
791
+ );
792
+ return;
793
+ }
794
+ this.markLoaded(parsed);
795
+ },
796
+ (error) => {
797
+ if (missingSimulationPlanEndpoint(error)) {
798
+ this.markLoaded(/* @__PURE__ */ new Map());
799
+ return;
800
+ }
801
+ this.scheduleRetry(
802
+ "sim-plan-unavailable",
803
+ `could not read the sim plan: ${error instanceof Error ? error.message : String(error)}`
804
+ );
805
+ }
806
+ );
807
+ }
808
+ markLoaded(parsed) {
809
+ if (this.stopped) {
810
+ return;
811
+ }
812
+ this.contentOffByKey = parsed;
813
+ this.policyRefresh.hold(SIMULATION_PLAN_REFRESH_MS);
814
+ this.retryPending = false;
815
+ this.clearRetryTimer();
816
+ this.resolveLoaded?.();
817
+ this.resolveLoaded = void 0;
818
+ this.releaseHeld();
819
+ }
820
+ scheduleRetry(warnKey, reason) {
821
+ if (this.stopped) {
822
+ return;
823
+ }
824
+ this.policyRefresh.hold(SIMULATION_PLAN_RETRY_MS);
825
+ if (this.contentOffByKey !== void 0) {
826
+ return;
827
+ }
828
+ this.retryPending = true;
829
+ warnOnce(
830
+ warnKey,
831
+ `${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.`
832
+ );
833
+ this.armRetry();
834
+ }
835
+ holdsWork() {
836
+ return this.held.length > 0 || this.waitingForFirstLoad > 0;
837
+ }
838
+ armRetry() {
839
+ if (this.stopped || !this.retryPending || this.retryTimer !== void 0 || !this.holdsWork()) {
840
+ return;
841
+ }
842
+ const timer = setTimeout(() => {
843
+ this.retryTimer = void 0;
844
+ this.policyRefresh.releaseHold();
845
+ this.refresh();
846
+ }, SIMULATION_PLAN_RETRY_MS);
847
+ unrefTimer(timer);
848
+ this.retryTimer = timer;
849
+ }
850
+ clearRetryTimer() {
851
+ if (this.retryTimer !== void 0) {
852
+ clearTimeout(this.retryTimer);
853
+ this.retryTimer = void 0;
854
+ }
855
+ }
856
+ syncRetryTimer() {
857
+ if (this.holdsWork()) {
858
+ return;
859
+ }
860
+ this.clearRetryTimer();
861
+ }
862
+ firstLoad() {
863
+ if (this.contentOffByKey !== void 0 || this.stopped || this.disabled()) {
864
+ return void 0;
865
+ }
866
+ return this.loaded;
867
+ }
868
+ send(payload, traceFunctionKey, submit) {
869
+ this.refresh();
870
+ if (this.stopped || this.disabled() || traceFunctionKey === void 0 || spanNameOf(payload) === void 0 || recordedByFramework(payload)) {
871
+ submit(payload);
872
+ return;
873
+ }
874
+ if (this.contentOffByKey === void 0) {
875
+ this.hold({ payload, traceFunctionKey, submit });
876
+ return;
877
+ }
878
+ this.submitEntry({ payload, traceFunctionKey, submit });
879
+ }
880
+ sendTrace(payload, submit) {
881
+ this.refresh();
882
+ if (this.stopped || this.disabled() || this.contentOffByKey !== void 0 || !this.holdsSpanOfTrace(traceIdOf(payload))) {
883
+ submit(payload);
884
+ return;
885
+ }
886
+ this.hold({ payload, traceFunctionKey: void 0, submit });
887
+ }
888
+ holdsSpanOfTrace(traceId) {
889
+ if (traceId === void 0) {
890
+ return false;
891
+ }
892
+ return this.held.some(
893
+ (entry) => entry.traceFunctionKey !== void 0 && traceIdOf(entry.payload) === traceId
894
+ );
895
+ }
896
+ hold(entry) {
897
+ this.held.push(entry);
898
+ while (this.held.length > SIMULATION_PLAN_MAX_HELD) {
899
+ this.held.shift();
900
+ this.droppedWhileHolding += 1;
901
+ }
902
+ this.armRetry();
903
+ }
904
+ submitEntry(entry) {
905
+ try {
906
+ entry.submit(
907
+ entry.traceFunctionKey === void 0 ? entry.payload : this.apply(entry.payload, entry.traceFunctionKey)
908
+ );
909
+ } catch (error) {
910
+ warnOnce(
911
+ "sim-plan-held-span-dropped",
912
+ `a span held for the sim plan was dropped when released: ${error instanceof Error ? error.message : String(error)}`
913
+ );
914
+ }
915
+ }
916
+ releaseHeld() {
917
+ const held = this.held;
918
+ this.held = [];
919
+ this.syncRetryTimer();
920
+ this.warnDropped();
921
+ for (const entry of held) {
922
+ this.submitEntry(entry);
923
+ }
924
+ }
925
+ warnDropped() {
926
+ if (this.droppedWhileHolding === 0) {
927
+ return;
928
+ }
929
+ const dropped = this.droppedWhileHolding;
930
+ this.droppedWhileHolding = 0;
931
+ warnOnce(
932
+ "sim-plan-held-overflow",
933
+ `${dropped} record(s) were dropped while the sim plan was still loading; at most ${SIMULATION_PLAN_MAX_HELD} are held per client.`
934
+ );
935
+ }
936
+ warnNeverLoaded() {
937
+ if (this.held.length === 0) {
938
+ return;
939
+ }
940
+ warnOnce(
941
+ "sim-plan-never-loaded",
942
+ `${this.held.length} record(s) were not sent because the sim plan never loaded`
943
+ );
944
+ }
945
+ async release(timeoutMs) {
946
+ this.refresh();
947
+ if (this.holdsWork()) {
948
+ await this.waitForFirstLoad(timeoutMs);
949
+ }
950
+ this.warnNeverLoaded();
951
+ this.warnDropped();
952
+ }
953
+ waitForFirstLoad(timeoutMs) {
954
+ const firstLoad = this.firstLoad();
955
+ if (firstLoad === void 0) {
956
+ return Promise.resolve();
957
+ }
958
+ this.waitingForFirstLoad += 1;
959
+ this.armRetry();
960
+ return new Promise((resolve) => {
961
+ let settled2 = false;
962
+ const finish = () => {
963
+ if (settled2) {
964
+ return;
965
+ }
966
+ settled2 = true;
967
+ this.waitingForFirstLoad -= 1;
968
+ this.syncRetryTimer();
969
+ resolve();
970
+ };
971
+ const timer = setTimeout(finish, Math.max(timeoutMs, 0));
972
+ void firstLoad.then(() => {
973
+ clearTimeout(timer);
974
+ finish();
975
+ });
976
+ });
977
+ }
978
+ stop() {
979
+ if (this.stopped) {
980
+ return;
981
+ }
982
+ this.stopped = true;
983
+ this.retryPending = false;
984
+ this.clearRetryTimer();
985
+ this.warnNeverLoaded();
986
+ this.held = [];
987
+ this.warnDropped();
988
+ }
989
+ contentOff(traceFunctionKey, spanName) {
990
+ return this.contentOffByKey?.get(traceFunctionKey)?.has(spanName) === true;
991
+ }
992
+ apply(payload, traceFunctionKey) {
993
+ if (recordedByFramework(payload)) {
994
+ return payload;
995
+ }
996
+ const name = spanNameOf(payload);
997
+ if (name === void 0 || !this.contentOff(traceFunctionKey, name)) {
998
+ return payload;
999
+ }
1000
+ return stripContent(payload);
1001
+ }
1002
+ };
1003
+ }
1004
+ });
1005
+
556
1006
  // src/traceMetadata.ts
557
1007
  function findRecord(traceId) {
558
1008
  return activeRecords.get(traceId) ?? completedRecords.get(traceId);
@@ -669,19 +1119,6 @@ var init_transportTypes = __esm({
669
1119
  }
670
1120
  });
671
1121
 
672
- // src/unrefTimer.ts
673
- function unrefTimer(timer) {
674
- const handle = timer;
675
- if (typeof handle.unref === "function") {
676
- handle.unref();
677
- }
678
- }
679
- var init_unrefTimer = __esm({
680
- "src/unrefTimer.ts"() {
681
- "use strict";
682
- }
683
- });
684
-
685
1122
  // src/otel.ts
686
1123
  function readBoundedIntEnv(name, max, fallback, warnKey) {
687
1124
  const raw = readEnv(name);
@@ -1332,6 +1769,17 @@ __export(http_exports, {
1332
1769
  parseRetryAfterMs: () => parseRetryAfterMs,
1333
1770
  serializePayloadBody: () => serializePayloadBody
1334
1771
  });
1772
+ async function releaseHeldExternalSpans(timeoutMs) {
1773
+ const deadline = Date.now() + Math.max(timeoutMs, 0);
1774
+ for (const client of [...liveHttpClients]) {
1775
+ await client.releaseHeldExternalSpans?.(Math.max(0, deadline - Date.now()));
1776
+ }
1777
+ }
1778
+ function stopSimulationPlans() {
1779
+ for (const client of [...liveHttpClients]) {
1780
+ client.stopSimulationPlan?.();
1781
+ }
1782
+ }
1335
1783
  function awaitOnExit(promise) {
1336
1784
  pendingTracePromises.add(promise);
1337
1785
  void promise.finally(() => {
@@ -1342,7 +1790,10 @@ function awaitOnExit(promise) {
1342
1790
  }
1343
1791
  async function flushTraces(timeoutMs = 5e3) {
1344
1792
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1345
- const requestsFlushed = await awaitPendingRequests(timeoutMs);
1793
+ await releaseHeldExternalSpans(planWaitSliceMs(timeoutMs));
1794
+ const requestsFlushed = await awaitPendingRequests(
1795
+ Math.max(0, deadline - Date.now())
1796
+ );
1346
1797
  const transportsFlushed = await flushTraceTransports(
1347
1798
  Math.max(0, deadline - Date.now())
1348
1799
  );
@@ -1461,7 +1912,22 @@ function sourceTraceIdOf(payload) {
1461
1912
  const id = rawTrace?.id;
1462
1913
  return typeof id === "string" ? id : void 0;
1463
1914
  }
1464
- 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;
1915
+ function describeError(error) {
1916
+ return error instanceof Error ? error.message : String(error);
1917
+ }
1918
+ function warnDroppedExternalSpan(error) {
1919
+ warnOnce(
1920
+ "external-span-dropped",
1921
+ `a span was dropped because the send step failed: ${describeError(error)}`
1922
+ );
1923
+ }
1924
+ function warnDroppedExternalTrace(error) {
1925
+ warnOnce(
1926
+ "external-trace-dropped",
1927
+ `a trace was dropped because the send step failed: ${describeError(error)}`
1928
+ );
1929
+ }
1930
+ 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;
1465
1931
  var init_http = __esm({
1466
1932
  "src/http.ts"() {
1467
1933
  "use strict";
@@ -1470,6 +1936,7 @@ var init_http = __esm({
1470
1936
  init_errors();
1471
1937
  init_replayContext();
1472
1938
  init_serializePayload();
1939
+ init_simulationPlan();
1473
1940
  init_traceMetadata();
1474
1941
  init_transport();
1475
1942
  init_transportTypes();
@@ -1482,6 +1949,7 @@ var init_http = __esm({
1482
1949
  EXIT_FLUSH_TIMEOUT_MS = 5e3;
1483
1950
  DEFAULT_LIFECYCLE_TIMEOUT_MS2 = 3e4;
1484
1951
  pendingTracePromises = /* @__PURE__ */ new Set();
1952
+ liveHttpClients = /* @__PURE__ */ new Set();
1485
1953
  if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
1486
1954
  let isFlushing = false;
1487
1955
  process.on("beforeExit", () => {
@@ -1489,12 +1957,27 @@ var init_http = __esm({
1489
1957
  return;
1490
1958
  }
1491
1959
  isFlushing = true;
1492
- void Promise.allSettled([
1493
- ...Array.from(pendingTracePromises).map((p) => p.catch(() => {
1494
- })),
1495
- shutdownTraceTransports(EXIT_FLUSH_TIMEOUT_MS).catch(() => false)
1496
- ]).then(() => {
1960
+ const planWaitMs = SIMULATION_PLAN_READ_TIMEOUT_MS;
1961
+ const stopFlushing = () => {
1497
1962
  isFlushing = false;
1963
+ };
1964
+ const deadline = setTimeout(
1965
+ stopFlushing,
1966
+ EXIT_FLUSH_TIMEOUT_MS + planWaitMs
1967
+ );
1968
+ unrefTimer(deadline);
1969
+ void releaseHeldExternalSpans(planWaitMs).catch(() => {
1970
+ }).then(() => {
1971
+ stopSimulationPlans();
1972
+ }).then(
1973
+ () => Promise.allSettled([
1974
+ ...Array.from(pendingTracePromises).map((p) => p.catch(() => {
1975
+ })),
1976
+ shutdownTraceTransports(EXIT_FLUSH_TIMEOUT_MS).catch(() => false)
1977
+ ])
1978
+ ).then(() => {
1979
+ clearTimeout(deadline);
1980
+ stopFlushing();
1498
1981
  });
1499
1982
  });
1500
1983
  }
@@ -1513,6 +1996,7 @@ var init_http = __esm({
1513
1996
  this.apiKey = config.apiKey;
1514
1997
  this.serviceUrl = config.serviceUrl;
1515
1998
  this.timeout = config.timeout ?? 12e4;
1999
+ liveHttpClients.add(this);
1516
2000
  }
1517
2001
  /**
1518
2002
  * Resolve the API key at the moment it is needed (request time), invoking
@@ -1710,9 +2194,14 @@ var init_http = __esm({
1710
2194
  * attributing its timeout here would fail a client whose own work succeeded.
1711
2195
  */
1712
2196
  async settleDeferredWork(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS2) {
2197
+ const deadline = Date.now() + Math.max(timeoutMs, 0);
2198
+ await this.releaseHeldExternalSpans?.(planWaitSliceMs(timeoutMs));
1713
2199
  await replayContextReady.catch(() => {
1714
2200
  });
1715
- return waitForPromises(Array.from(this.deferredWork), timeoutMs);
2201
+ return waitForPromises(
2202
+ Array.from(this.deferredWork),
2203
+ Math.max(0, deadline - Date.now())
2204
+ );
1716
2205
  }
1717
2206
  /**
1718
2207
  * Wait for spans queued by this client to be delivered, within one deadline.
@@ -1720,7 +2209,9 @@ var init_http = __esm({
1720
2209
  */
1721
2210
  async waitForPendingRequests(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS2) {
1722
2211
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1723
- const settled2 = await this.settleDeferredWork(timeoutMs);
2212
+ const settled2 = await this.settleDeferredWork(
2213
+ Math.max(0, deadline - Date.now())
2214
+ );
1724
2215
  const flushed = await this.traceTransport?.flush(Math.max(0, deadline - Date.now())) ?? true;
1725
2216
  return settled2 && flushed;
1726
2217
  }
@@ -1735,6 +2226,13 @@ var init_http = __esm({
1735
2226
  }
1736
2227
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1737
2228
  this.closing = (async () => {
2229
+ await this.releaseHeldExternalSpans?.(planWaitSliceMs(timeoutMs));
2230
+ this.stopSimulationPlan?.();
2231
+ this.externalSpanTransform = void 0;
2232
+ this.externalTraceTransform = void 0;
2233
+ this.releaseHeldExternalSpans = void 0;
2234
+ this.stopSimulationPlan = void 0;
2235
+ liveHttpClients.delete(this);
1738
2236
  const settled2 = await this.settleDeferredWork(
1739
2237
  Math.max(0, deadline - Date.now())
1740
2238
  );
@@ -1846,6 +2344,13 @@ var init_http = __esm({
1846
2344
  protocol
1847
2345
  });
1848
2346
  }
2347
+ async getSimulationPlan() {
2348
+ return this.get(
2349
+ "/api/sdk/sim-plan",
2350
+ SIMULATION_PLAN_READ_TIMEOUT_MS,
2351
+ { Connection: "close" }
2352
+ );
2353
+ }
1849
2354
  async getTraceSpan(traceId, lookup) {
1850
2355
  const searchParams = new URLSearchParams();
1851
2356
  if (lookup.id !== void 0) {
@@ -1862,14 +2367,18 @@ var init_http = __esm({
1862
2367
  * GET a JSON endpoint on the service with the client's API key. Throws a
1863
2368
  * `BitfabError` carrying the status text for any non-2xx response.
1864
2369
  */
1865
- async get(endpoint) {
2370
+ async get(endpoint, timeoutMs, extraHeaders) {
1866
2371
  const url = `${this.serviceUrl}${endpoint}`;
2372
+ const timeout = timeoutMs ?? this.timeout;
1867
2373
  const controller = new AbortController();
1868
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
2374
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
1869
2375
  try {
1870
2376
  const response = await fetch(url, {
1871
2377
  method: "GET",
1872
- headers: { Authorization: `Bearer ${this.resolveApiKey() ?? ""}` },
2378
+ headers: {
2379
+ Authorization: `Bearer ${this.resolveApiKey() ?? ""}`,
2380
+ ...extraHeaders
2381
+ },
1873
2382
  signal: controller.signal
1874
2383
  });
1875
2384
  if (!response.ok) {
@@ -1888,7 +2397,7 @@ var init_http = __esm({
1888
2397
  }
1889
2398
  if (error instanceof Error) {
1890
2399
  if (error.name === "AbortError") {
1891
- throw new BitfabError(`Request timed out after ${this.timeout}ms`);
2400
+ throw new BitfabError(`Request timed out after ${timeout}ms`);
1892
2401
  }
1893
2402
  throw new BitfabError(error.message);
1894
2403
  }
@@ -1915,16 +2424,27 @@ var init_http = __esm({
1915
2424
  carrierMeta("internal_trace", body, void 0)
1916
2425
  );
1917
2426
  }
1918
- /**
1919
- * Queue an external span (from withSpan wrapper or OpenAI tracing) onto this
1920
- * client's batching transport. Fire-and-forget: the transport owns delivery,
1921
- * so callers await `flushTraces()` or `close()` rather than a per-span
1922
- * promise.
1923
- */
1924
2427
  sendExternalSpan(payload) {
2428
+ const gate = this.externalSpanTransform;
2429
+ if (gate === void 0) {
2430
+ this.submitExternalSpan(payload);
2431
+ return;
2432
+ }
2433
+ try {
2434
+ gate(payload, (ready) => this.submitExternalSpan(ready));
2435
+ } catch (error) {
2436
+ warnDroppedExternalSpan(error);
2437
+ }
2438
+ }
2439
+ submitExternalSpan(payload) {
2440
+ const body = {
2441
+ ...payload,
2442
+ sdkVersion: __version__
2443
+ };
2444
+ delete body[ROOT_TRACE_FUNCTION_KEY_FIELD];
1925
2445
  this.getTraceTransport()?.submit(
1926
2446
  "external_span",
1927
- { ...payload, sdkVersion: __version__ },
2447
+ body,
1928
2448
  this.recordedMeta("external_span", payload, carrierRef(payload))
1929
2449
  );
1930
2450
  }
@@ -1936,6 +2456,18 @@ var init_http = __esm({
1936
2456
  */
1937
2457
  sendExternalTrace(rawPayload) {
1938
2458
  const payload = mergeCallerMetadataIntoTracePayload(rawPayload);
2459
+ const gate = this.externalTraceTransform;
2460
+ if (gate === void 0) {
2461
+ this.submitExternalTrace(payload);
2462
+ return;
2463
+ }
2464
+ try {
2465
+ gate(payload, (ready) => this.submitExternalTrace(ready));
2466
+ } catch (error) {
2467
+ warnDroppedExternalTrace(error);
2468
+ }
2469
+ }
2470
+ submitExternalTrace(payload) {
1939
2471
  this.getTraceTransport()?.submit(
1940
2472
  "external_trace",
1941
2473
  {
@@ -3656,6 +4188,7 @@ function finalizeTracePayload(payload) {
3656
4188
  // src/claudeAgentSdk.ts
3657
4189
  init_randomUuid();
3658
4190
  init_serialize();
4191
+ init_spanOrigin();
3659
4192
 
3660
4193
  // src/timestamp.ts
3661
4194
  var lastTimestampMicros = 0;
@@ -3863,6 +4396,7 @@ var BitfabClaudeAgentHandler = class {
3863
4396
  trace_id: spanInfo.traceId,
3864
4397
  started_at: spanInfo.startedAt,
3865
4398
  ended_at: spanInfo.endedAt ?? nowIso(),
4399
+ span_origin: makeSpanOrigin("claude-agent-sdk"),
3866
4400
  span_data: spanData
3867
4401
  };
3868
4402
  if (spanInfo.parentId !== null) {
@@ -5174,6 +5708,7 @@ init_constants();
5174
5708
  init_http();
5175
5709
  init_randomUuid();
5176
5710
  init_serialize();
5711
+ init_spanOrigin();
5177
5712
  var LANGSMITH_HIDDEN_TAG = "langsmith:hidden";
5178
5713
  var CHAIN_RUN_TYPES = /* @__PURE__ */ new Set(["chain", "parser", "prompt"]);
5179
5714
  var LANGGRAPH_METADATA_KEYS = [
@@ -5528,6 +6063,7 @@ var BitfabLangGraphCallbackHandler = class {
5528
6063
  trace_id: spanInfo.traceId,
5529
6064
  started_at: spanInfo.startedAt,
5530
6065
  ended_at: spanInfo.endedAt ?? nowIso2(),
6066
+ span_origin: makeSpanOrigin("langgraph"),
5531
6067
  span_data: spanData
5532
6068
  };
5533
6069
  if (spanInfo.parentId !== null) {
@@ -5789,14 +6325,14 @@ var BitfabLangGraphCallbackHandler = class {
5789
6325
  init_http();
5790
6326
  init_replayContext();
5791
6327
  var TOOL_RESULT_TAG = "__bitfabLangGraphToolResult";
5792
- function isRecord(value) {
6328
+ function isRecord3(value) {
5793
6329
  return typeof value === "object" && value !== null;
5794
6330
  }
5795
6331
  function isToolMessage(value) {
5796
- return isRecord(value) && value.lc_direct_tool_output === true && value.type === "tool" && (typeof value.content === "string" || Array.isArray(value.content));
6332
+ return isRecord3(value) && value.lc_direct_tool_output === true && value.type === "tool" && (typeof value.content === "string" || Array.isArray(value.content));
5797
6333
  }
5798
6334
  function isCommand(value) {
5799
- return isRecord(value) && value.lg_name === "Command";
6335
+ return isRecord3(value) && value.lg_name === "Command";
5800
6336
  }
5801
6337
  function encodeNested(value) {
5802
6338
  if (isToolMessage(value) || isCommand(value)) {
@@ -5805,7 +6341,7 @@ function encodeNested(value) {
5805
6341
  if (Array.isArray(value)) {
5806
6342
  return value.map(encodeNested);
5807
6343
  }
5808
- if (isRecord(value)) {
6344
+ if (isRecord3(value)) {
5809
6345
  return Object.fromEntries(
5810
6346
  Object.entries(value).map(([key, entry]) => [key, encodeNested(entry)])
5811
6347
  );
@@ -5828,17 +6364,17 @@ function encodeNativeToolResult(value) {
5828
6364
  }
5829
6365
  return {
5830
6366
  [TOOL_RESULT_TAG]: "command",
5831
- graph: isRecord(value) && typeof value.graph === "string" ? value.graph : void 0,
5832
- update: isRecord(value) ? encodeNested(value.update) : void 0,
5833
- resume: isRecord(value) ? encodeNested(value.resume) : void 0,
5834
- goto: isRecord(value) ? encodeNested(value.goto) : void 0
6367
+ graph: isRecord3(value) && typeof value.graph === "string" ? value.graph : void 0,
6368
+ update: isRecord3(value) ? encodeNested(value.update) : void 0,
6369
+ resume: isRecord3(value) ? encodeNested(value.resume) : void 0,
6370
+ goto: isRecord3(value) ? encodeNested(value.goto) : void 0
5835
6371
  };
5836
6372
  }
5837
6373
  function finalizeToolResult(value) {
5838
6374
  return isToolMessage(value) || isCommand(value) ? encodeNativeToolResult(value) : value;
5839
6375
  }
5840
6376
  function isEncodedToolResult(value) {
5841
- return isRecord(value) && typeof value[TOOL_RESULT_TAG] === "string";
6377
+ return isRecord3(value) && typeof value[TOOL_RESULT_TAG] === "string";
5842
6378
  }
5843
6379
  async function loadLangChainCore() {
5844
6380
  try {
@@ -5874,7 +6410,7 @@ async function reviveNested(value, toolCallId) {
5874
6410
  if (Array.isArray(value)) {
5875
6411
  return Promise.all(value.map((entry) => reviveNested(entry, toolCallId)));
5876
6412
  }
5877
- if (isRecord(value)) {
6413
+ if (isRecord3(value)) {
5878
6414
  const entries = await Promise.all(
5879
6415
  Object.entries(value).map(async ([key, entry]) => [
5880
6416
  key,
@@ -5899,16 +6435,16 @@ async function reviveToolResult(value, toolCallId) {
5899
6435
  ...value.artifact !== void 0 && {
5900
6436
  artifact: await reviveNested(value.artifact, toolCallId)
5901
6437
  },
5902
- ...isRecord(value.metadata) && {
6438
+ ...isRecord3(value.metadata) && {
5903
6439
  metadata: await reviveNested(value.metadata, toolCallId)
5904
6440
  },
5905
- ...isRecord(value.additionalKwargs) && {
6441
+ ...isRecord3(value.additionalKwargs) && {
5906
6442
  additional_kwargs: await reviveNested(
5907
6443
  value.additionalKwargs,
5908
6444
  toolCallId
5909
6445
  )
5910
6446
  },
5911
- ...isRecord(value.responseMetadata) && {
6447
+ ...isRecord3(value.responseMetadata) && {
5912
6448
  response_metadata: await reviveNested(
5913
6449
  value.responseMetadata,
5914
6450
  toolCallId
@@ -5978,8 +6514,8 @@ var BitfabLangGraphIntegration = class {
5978
6514
  get: (target, property) => {
5979
6515
  if (property === "invoke") {
5980
6516
  return async (input, ...rest) => {
5981
- const toolCallId = isRecord(input) && typeof input.id === "string" ? input.id : "";
5982
- const args = isRecord(input) && "args" in input ? input.args : input;
6517
+ const toolCallId = isRecord3(input) && typeof input.id === "string" ? input.id : "";
6518
+ const args = isRecord3(input) && "args" in input ? input.args : input;
5983
6519
  this.assertReplayToolResultExists(toolName, shouldMock);
5984
6520
  const execute = this.client.withSpan(
5985
6521
  this.traceFunctionKey,
@@ -5989,7 +6525,8 @@ var BitfabLangGraphIntegration = class {
5989
6525
  captureWhen: "nested",
5990
6526
  mockOnReplay: shouldMock,
5991
6527
  finalize: finalizeToolResult,
5992
- surface: "inherit"
6528
+ surface: "inherit",
6529
+ instrumentation: "langgraph"
5993
6530
  },
5994
6531
  async (_args) => await originalInvoke(input, ...rest)
5995
6532
  );
@@ -6032,7 +6569,12 @@ var BitfabLangGraphIntegration = class {
6032
6569
  wrapInvoke(fn) {
6033
6570
  return this.client.withSpan(
6034
6571
  this.traceFunctionKey,
6035
- { name: this.traceFunctionKey, type: "agent", surface: "inherit" },
6572
+ {
6573
+ name: this.traceFunctionKey,
6574
+ type: "agent",
6575
+ surface: "inherit",
6576
+ instrumentation: "langgraph"
6577
+ },
6036
6578
  fn
6037
6579
  );
6038
6580
  }
@@ -6088,7 +6630,8 @@ var BitfabOpenAIAgentHandler = class {
6088
6630
  const options_ = {
6089
6631
  type: "agent",
6090
6632
  finalize,
6091
- surface: "inherit"
6633
+ surface: "inherit",
6634
+ instrumentation: "openai-agents"
6092
6635
  };
6093
6636
  const traced = this.withSpanFn(
6094
6637
  this.traceFunctionKey,
@@ -6104,6 +6647,7 @@ var BitfabOpenAIAgentHandler = class {
6104
6647
  };
6105
6648
 
6106
6649
  // src/client.ts
6650
+ init_policyRefresh();
6107
6651
  init_randomUuid();
6108
6652
  init_replay();
6109
6653
 
@@ -6187,12 +6731,15 @@ function runWithSeedContext(ctx, fn) {
6187
6731
 
6188
6732
  // src/client.ts
6189
6733
  init_serialize();
6734
+ init_simulationPlan();
6735
+ init_spanOrigin();
6190
6736
  init_traceMetadata();
6191
6737
 
6192
6738
  // src/tracing.ts
6193
6739
  init_constants();
6194
6740
  init_http();
6195
6741
  init_randomUuid();
6742
+ init_spanOrigin();
6196
6743
  var BitfabOpenAITracingProcessor = class {
6197
6744
  /**
6198
6745
  * Initialize the tracing processor.
@@ -6394,6 +6941,7 @@ var BitfabOpenAITracingProcessor = class {
6394
6941
  * Build span payload for the external spans API.
6395
6942
  */
6396
6943
  buildSpanPayload(serializedSpan, errors) {
6944
+ serializedSpan.span_origin = makeSpanOrigin("openai-agents");
6397
6945
  const payload = {
6398
6946
  id: randomUuid(),
6399
6947
  type: "openai",
@@ -6523,7 +7071,8 @@ var BitfabVercelAiHandler = class {
6523
7071
  {
6524
7072
  type: "llm",
6525
7073
  finalize: (result) => summarizeGenerate(result ?? {}, label),
6526
- surface: "inherit"
7074
+ surface: "inherit",
7075
+ instrumentation: "vercel-ai"
6527
7076
  },
6528
7077
  () => doGenerate()
6529
7078
  );
@@ -6541,7 +7090,12 @@ var BitfabVercelAiHandler = class {
6541
7090
  // The wrapped fn returns immediately with the live stream, so the span
6542
7091
  // output cannot be read from the return value. `finalize` instead
6543
7092
  // awaits the summary the accumulator resolves once the stream drains.
6544
- { type: "llm", finalize: () => summary, surface: "inherit" },
7093
+ {
7094
+ type: "llm",
7095
+ finalize: () => summary,
7096
+ surface: "inherit",
7097
+ instrumentation: "vercel-ai"
7098
+ },
6545
7099
  async () => {
6546
7100
  const result = await doStream();
6547
7101
  const stream = result.stream.pipeThrough(
@@ -6559,6 +7113,19 @@ var BitfabVercelAiHandler = class {
6559
7113
  // src/client.ts
6560
7114
  init_warnOnce();
6561
7115
  var activeTraceStates = /* @__PURE__ */ new Map();
7116
+ function rootTraceFunctionKeyOf(payload) {
7117
+ const captured = payload[ROOT_TRACE_FUNCTION_KEY_FIELD];
7118
+ if (typeof captured === "string") {
7119
+ return captured;
7120
+ }
7121
+ const traceId = payload.traceId;
7122
+ const live = typeof traceId === "string" ? activeTraceStates.get(traceId)?.traceFunctionKey : void 0;
7123
+ if (typeof live === "string") {
7124
+ return live;
7125
+ }
7126
+ const own = payload.traceFunctionKey;
7127
+ return typeof own === "string" ? own : void 0;
7128
+ }
6562
7129
  var asyncLocalStorage = null;
6563
7130
  var SPAN_CONTEXT_STORAGE_SYMBOL = /* @__PURE__ */ Symbol.for("bitfab.spanContextStorage");
6564
7131
  var initializeAsyncContext = () => {
@@ -6901,6 +7468,26 @@ var DEFAULT_AUTO_TRACE_MAX_SPANS = 500;
6901
7468
  var AUTO_TRACE_PROTOCOL = "ts-auto-v1";
6902
7469
  var AUTO_TRACE_POLICY_REFRESH_MS = 6e4;
6903
7470
  var AUTO_TRACE_POLICY_RETRY_MS = 1e4;
7471
+ function spanOwnerName(receiver) {
7472
+ if (typeof receiver === "function") {
7473
+ return receiver.name !== "" ? receiver.name : void 0;
7474
+ }
7475
+ if (typeof receiver !== "object" || receiver === null) {
7476
+ return void 0;
7477
+ }
7478
+ const owner = receiver.constructor;
7479
+ if (typeof owner !== "function" || owner === Object || owner.name === "") {
7480
+ return void 0;
7481
+ }
7482
+ return owner.name;
7483
+ }
7484
+ function qualifiedSpanName(receiver, functionName) {
7485
+ if (functionName === void 0) {
7486
+ return void 0;
7487
+ }
7488
+ const owner = spanOwnerName(receiver);
7489
+ return owner === void 0 ? functionName : `${owner}.${functionName}`;
7490
+ }
6904
7491
  function autoTraceLimit(value, fallback) {
6905
7492
  return value !== void 0 && Number.isFinite(value) && value >= 0 ? Math.floor(value) : fallback;
6906
7493
  }
@@ -6943,6 +7530,14 @@ var Bitfab = class {
6943
7530
  serviceUrl: this.serviceUrl,
6944
7531
  timeout: this.timeout
6945
7532
  });
7533
+ this.simulationPlan = new SimulationPlan(
7534
+ this.httpClient,
7535
+ config.simulationPlan ?? true
7536
+ );
7537
+ this.httpClient.externalSpanTransform = (payload, submit) => this.applySimulationPlan(payload, submit);
7538
+ this.httpClient.externalTraceTransform = (payload, submit) => this.simulationPlan.sendTrace(payload, submit);
7539
+ this.httpClient.releaseHeldExternalSpans = (timeoutMs) => this.simulationPlan.release(timeoutMs);
7540
+ this.httpClient.stopSimulationPlan = () => this.simulationPlan.stop();
6946
7541
  this.datasets = new DatasetsClient(this.httpClient);
6947
7542
  this.traces = new TracesClient(this.httpClient);
6948
7543
  this.labels = new LabelsClient(this.httpClient);
@@ -7093,6 +7688,7 @@ var Bitfab = class {
7093
7688
  }
7094
7689
  createAutoTraceRoot(traceFunctionKey, name, options, fn) {
7095
7690
  const self = this;
7691
+ self.simulationPlan.refresh();
7096
7692
  const maxDepth = autoTraceLimit(
7097
7693
  options.maxDepth,
7098
7694
  DEFAULT_AUTO_TRACE_MAX_DEPTH
@@ -7103,12 +7699,14 @@ var Bitfab = class {
7103
7699
  );
7104
7700
  const excluded = new Set(options.exclude ?? []);
7105
7701
  const includeWrappers = options.includeWrappers ?? false;
7702
+ const rootNameFor = (receiver) => options.name ?? (fn.name !== "" ? qualifiedSpanName(receiver, name) ?? name : name);
7106
7703
  const rootOptions = {
7107
- name: options.name ?? name,
7704
+ name: options.name,
7705
+ nameFor: rootNameFor,
7108
7706
  type: options.type ?? "custom",
7109
- surface: "opt-out"
7707
+ surface: "opt-out",
7708
+ instrumentation: "trace"
7110
7709
  };
7111
- const rootName = options.name ?? name;
7112
7710
  const buildTracedRoot = (spanOptions) => this.withSpan(
7113
7711
  traceFunctionKey,
7114
7712
  spanOptions,
@@ -7118,6 +7716,7 @@ var Bitfab = class {
7118
7716
  traceFunctionKey
7119
7717
  );
7120
7718
  self.refreshAutoTraceCapturePolicy(traceFunctionKey);
7719
+ self.simulationPlan.refresh();
7121
7720
  let spansUsed = 0;
7122
7721
  let truncated = false;
7123
7722
  const warnTruncated = () => {
@@ -7194,6 +7793,7 @@ var Bitfab = class {
7194
7793
  type: nodeConfiguration?.type ?? "function",
7195
7794
  captureWhen: "nested",
7196
7795
  surface: "opt-out",
7796
+ instrumentation: "trace",
7197
7797
  functionId: definition.id,
7198
7798
  captureContent: nodeConfiguration !== void 0 || capturePolicy === void 0 || capturePolicy.has(definition.id),
7199
7799
  autoTraceDefinition: definition,
@@ -7225,6 +7825,7 @@ var Bitfab = class {
7225
7825
  type: "function",
7226
7826
  captureWhen: "nested",
7227
7827
  surface: "opt-out",
7828
+ instrumentation: "trace",
7228
7829
  captureContent: true,
7229
7830
  ...link !== void 0 && { nestedTrace: link }
7230
7831
  };
@@ -7248,6 +7849,7 @@ var Bitfab = class {
7248
7849
  return tracedRoot.apply(this, args);
7249
7850
  }
7250
7851
  const link = getReplayContext() || inSeedScope() ? void 0 : { traceFunctionKey };
7852
+ const rootName = rootNameFor(this);
7251
7853
  return invokeThroughAutoTraceEntries(
7252
7854
  enclosing,
7253
7855
  (entry, next) => entry.context.invokeNestedRoot(
@@ -7289,38 +7891,36 @@ var Bitfab = class {
7289
7891
  Object.defineProperty(autoTraceRoot, "_bitfabWrappedFn", { value: fn });
7290
7892
  return autoTraceRoot;
7291
7893
  }
7894
+ applySimulationPlan(payload, submit) {
7895
+ this.simulationPlan.send(payload, rootTraceFunctionKeyOf(payload), submit);
7896
+ }
7292
7897
  refreshAutoTraceCapturePolicy(traceFunctionKey) {
7293
- const now = Date.now();
7294
- const state = this.autoTracePolicyRefreshes.get(traceFunctionKey) ?? {
7295
- refreshAfter: 0
7296
- };
7297
- if (state.inFlight || now < state.refreshAfter) {
7298
- return;
7299
- }
7300
- const request = this.httpClient.getAutoTracePolicy(
7301
- traceFunctionKey,
7302
- AUTO_TRACE_PROTOCOL
7303
- ).then((policy) => {
7304
- if (policy.protocol !== AUTO_TRACE_PROTOCOL) {
7305
- state.refreshAfter = Date.now() + AUTO_TRACE_POLICY_RETRY_MS;
7306
- return;
7307
- }
7308
- const functionIds = Array.isArray(policy.functionIds) ? policy.functionIds.filter(
7309
- (id) => typeof id === "string" && id.startsWith(`${AUTO_TRACE_PROTOCOL}:`)
7310
- ).slice(0, DEFAULT_AUTO_TRACE_MAX_SPANS) : [];
7311
- __setBitfabAutoTraceCapturePolicy(
7312
- this,
7313
- traceFunctionKey,
7314
- policy.revision === null ? void 0 : functionIds
7315
- );
7316
- state.refreshAfter = Date.now() + AUTO_TRACE_POLICY_REFRESH_MS;
7317
- }).catch(() => {
7318
- state.refreshAfter = Date.now() + AUTO_TRACE_POLICY_RETRY_MS;
7319
- }).finally(() => {
7320
- state.inFlight = void 0;
7321
- });
7322
- state.inFlight = request;
7898
+ const state = this.autoTracePolicyRefreshes.get(traceFunctionKey) ?? new PolicyRefresh();
7323
7899
  this.autoTracePolicyRefreshes.set(traceFunctionKey, state);
7900
+ state.run(
7901
+ () => this.httpClient.getAutoTracePolicy(
7902
+ traceFunctionKey,
7903
+ AUTO_TRACE_PROTOCOL
7904
+ ),
7905
+ (policy) => {
7906
+ if (policy.protocol !== AUTO_TRACE_PROTOCOL) {
7907
+ state.hold(AUTO_TRACE_POLICY_RETRY_MS);
7908
+ return;
7909
+ }
7910
+ const functionIds = Array.isArray(policy.functionIds) ? policy.functionIds.filter(
7911
+ (id) => typeof id === "string" && id.startsWith(`${AUTO_TRACE_PROTOCOL}:`)
7912
+ ).slice(0, DEFAULT_AUTO_TRACE_MAX_SPANS) : [];
7913
+ __setBitfabAutoTraceCapturePolicy(
7914
+ this,
7915
+ traceFunctionKey,
7916
+ policy.revision === null ? void 0 : functionIds
7917
+ );
7918
+ state.hold(AUTO_TRACE_POLICY_REFRESH_MS);
7919
+ },
7920
+ () => {
7921
+ state.hold(AUTO_TRACE_POLICY_RETRY_MS);
7922
+ }
7923
+ );
7324
7924
  }
7325
7925
  /**
7326
7926
  * Flush and permanently close this client's tracing resources: its pending
@@ -7804,6 +8404,7 @@ var Bitfab = class {
7804
8404
  * @returns A wrapped function with the same signature that creates spans for inputs and outputs
7805
8405
  */
7806
8406
  withSpan(traceFunctionKey, optionsOrFn, maybeFn) {
8407
+ this.simulationPlan.refresh();
7807
8408
  const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
7808
8409
  const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
7809
8410
  const self = this;
@@ -7895,8 +8496,10 @@ var Bitfab = class {
7895
8496
  const testRunId = replayCtxAtStart?.testRunId ?? options.testRunId;
7896
8497
  if (isRootSpan && !activeTraceStates.has(traceId)) {
7897
8498
  const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
8499
+ self.simulationPlan.refresh();
7898
8500
  activeTraceStates.set(traceId, {
7899
8501
  traceId,
8502
+ traceFunctionKey,
7900
8503
  startedAt,
7901
8504
  contexts: [],
7902
8505
  ...testRunId !== void 0 && { testRunId },
@@ -7911,16 +8514,19 @@ var Bitfab = class {
7911
8514
  registeredTraceId = traceId;
7912
8515
  }
7913
8516
  const functionName = fn.name !== "" ? fn.name : void 0;
8517
+ const rootTraceFunctionKey = activeTraceStates.get(traceId)?.traceFunctionKey ?? traceFunctionKey;
7914
8518
  const baseSpanParams = {
7915
8519
  traceFunctionKey,
8520
+ rootTraceFunctionKey,
7916
8521
  functionName,
7917
- spanName: options.name ?? functionName ?? traceFunctionKey,
8522
+ spanName: options.name ?? options.nameFor?.(this) ?? qualifiedSpanName(this, functionName) ?? traceFunctionKey,
7918
8523
  traceId,
7919
8524
  spanId,
7920
8525
  parentSpanId,
7921
8526
  inputs,
7922
8527
  startedAt,
7923
8528
  spanType: options.type ?? "custom",
8529
+ instrumentation: options.instrumentation ?? "span",
7924
8530
  functionId: options.functionId,
7925
8531
  captureContent: options.captureContent ?? true,
7926
8532
  autoTraceDefinition: options.autoTraceDefinition,
@@ -8336,6 +8942,7 @@ var Bitfab = class {
8336
8942
  * @returns A BitfabFunction instance for wrapping functions
8337
8943
  */
8338
8944
  getFunction(traceFunctionKey) {
8945
+ this.simulationPlan.refresh();
8339
8946
  return new BitfabFunction(this, traceFunctionKey);
8340
8947
  }
8341
8948
  /**
@@ -8423,6 +9030,7 @@ var Bitfab = class {
8423
9030
  trace_id: params.traceId,
8424
9031
  started_at: params.startedAt,
8425
9032
  ended_at: params.endedAt,
9033
+ span_origin: makeSpanOrigin(params.instrumentation),
8426
9034
  span_data: {
8427
9035
  name: params.spanName,
8428
9036
  type: params.spanType,
@@ -8483,6 +9091,9 @@ var Bitfab = class {
8483
9091
  source: "typescript-sdk-function",
8484
9092
  sourceTraceId: params.traceId,
8485
9093
  traceFunctionKey: params.traceFunctionKey,
9094
+ ...params.rootTraceFunctionKey !== void 0 && {
9095
+ [ROOT_TRACE_FUNCTION_KEY_FIELD]: params.rootTraceFunctionKey
9096
+ },
8486
9097
  rawSpan: externalSpan,
8487
9098
  ...params.testRunId && { testRunId: params.testRunId },
8488
9099
  ...params.mocked && { mocked: true },
@@ -8551,6 +9162,7 @@ var Bitfab = class {
8551
9162
  }
8552
9163
  activeTraceStates.set(traceId, {
8553
9164
  traceId,
9165
+ traceFunctionKey,
8554
9166
  startedAt,
8555
9167
  contexts: [],
8556
9168
  ingestionType: "seeded",
@@ -8560,6 +9172,7 @@ var Bitfab = class {
8560
9172
  try {
8561
9173
  this.sendWrapperSpan({
8562
9174
  traceFunctionKey,
9175
+ rootTraceFunctionKey: traceFunctionKey,
8563
9176
  spanName: options.spanName ?? traceFunctionKey,
8564
9177
  traceId,
8565
9178
  spanId: randomUuid(),
@@ -8569,6 +9182,7 @@ var Bitfab = class {
8569
9182
  startedAt,
8570
9183
  endedAt: startedAt,
8571
9184
  spanType: options.spanType ?? "agent",
9185
+ instrumentation: "span",
8572
9186
  captureContent: true
8573
9187
  });
8574
9188
  this.sendTraceCompletion({
@@ -8614,6 +9228,7 @@ var Bitfab = class {
8614
9228
  }
8615
9229
  activeTraceStates.set(traceId, {
8616
9230
  traceId,
9231
+ traceFunctionKey,
8617
9232
  startedAt: nowIsoTimestamp(),
8618
9233
  contexts: [],
8619
9234
  ingestionType: "seeded",