@bitfab/sdk 0.53.0 → 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/index.cjs CHANGED
@@ -42,7 +42,7 @@ var __version__, __packageName__;
42
42
  var init_version_generated = __esm({
43
43
  "src/version.generated.ts"() {
44
44
  "use strict";
45
- __version__ = "0.53.0";
45
+ __version__ = "0.53.1";
46
46
  __packageName__ = "@bitfab/sdk";
47
47
  }
48
48
  });
@@ -507,8 +507,8 @@ function encodePayloadBody(payload) {
507
507
  const marker = { error: `payload_serialize_failed: ${message}` };
508
508
  return { body: JSON.stringify(marker), dropped, value: marker };
509
509
  }
510
- const isRecord2 = typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized);
511
- if (dropped.length > 0 && isRecord2) {
510
+ const isRecord4 = typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized);
511
+ if (dropped.length > 0 && isRecord4) {
512
512
  const obj = sanitized;
513
513
  const existing = Array.isArray(obj.errors) ? obj.errors : [];
514
514
  obj.errors = [
@@ -525,7 +525,7 @@ function encodePayloadBody(payload) {
525
525
  return {
526
526
  body: JSON.stringify(sanitized),
527
527
  dropped,
528
- value: isRecord2 ? sanitized : void 0
528
+ value: isRecord4 ? sanitized : void 0
529
529
  };
530
530
  }
531
531
  }
@@ -537,6 +537,456 @@ var init_serializePayload = __esm({
537
537
  }
538
538
  });
539
539
 
540
+ // src/policyRefresh.ts
541
+ var PolicyRefresh;
542
+ var init_policyRefresh = __esm({
543
+ "src/policyRefresh.ts"() {
544
+ "use strict";
545
+ PolicyRefresh = class {
546
+ constructor() {
547
+ this.refreshAfter = 0;
548
+ }
549
+ due() {
550
+ return this.inFlight === void 0 && Date.now() >= this.refreshAfter;
551
+ }
552
+ hold(durationMs) {
553
+ this.refreshAfter = Date.now() + durationMs;
554
+ }
555
+ releaseHold() {
556
+ this.refreshAfter = 0;
557
+ }
558
+ run(read2, onLoaded, onFailed) {
559
+ if (!this.due()) {
560
+ return;
561
+ }
562
+ let request;
563
+ try {
564
+ request = read2().then(onLoaded).catch(onFailed).finally(() => {
565
+ this.inFlight = void 0;
566
+ });
567
+ } catch (error) {
568
+ onFailed(error);
569
+ return;
570
+ }
571
+ this.inFlight = request;
572
+ }
573
+ };
574
+ }
575
+ });
576
+
577
+ // src/spanOrigin.ts
578
+ function isFrameworkInstrumentation(value) {
579
+ return typeof value === "string" && FRAMEWORK_INSTRUMENTATION_SET.has(value);
580
+ }
581
+ function makeSpanOrigin(instrumentation) {
582
+ return {
583
+ name: SPAN_ORIGIN_NAME,
584
+ version: __version__,
585
+ instrumentation: { name: instrumentation }
586
+ };
587
+ }
588
+ function isRecord(value) {
589
+ return typeof value === "object" && value !== null && !Array.isArray(value);
590
+ }
591
+ function spanOriginOf(payload) {
592
+ const rawSpan = payload.rawSpan;
593
+ if (!isRecord(rawSpan)) {
594
+ return void 0;
595
+ }
596
+ const origin = rawSpan.span_origin;
597
+ if (!isRecord(origin) || !isRecord(origin.instrumentation)) {
598
+ return void 0;
599
+ }
600
+ const { name, version } = origin;
601
+ const instrumentation = origin.instrumentation.name;
602
+ if (typeof name !== "string" || typeof version !== "string" || typeof instrumentation !== "string" || !SPAN_INSTRUMENTATIONS.includes(instrumentation)) {
603
+ return void 0;
604
+ }
605
+ return {
606
+ name,
607
+ version,
608
+ instrumentation: { name: instrumentation }
609
+ };
610
+ }
611
+ function spanInstrumentationOf(payload) {
612
+ return spanOriginOf(payload)?.instrumentation.name;
613
+ }
614
+ function recordedByFramework(payload) {
615
+ return isFrameworkInstrumentation(spanInstrumentationOf(payload));
616
+ }
617
+ var SPAN_ORIGIN_NAME, SPAN_INSTRUMENTATIONS, FRAMEWORK_INSTRUMENTATIONS, FRAMEWORK_INSTRUMENTATION_SET;
618
+ var init_spanOrigin = __esm({
619
+ "src/spanOrigin.ts"() {
620
+ "use strict";
621
+ init_constants();
622
+ SPAN_ORIGIN_NAME = "bitfab.sdk.typescript";
623
+ SPAN_INSTRUMENTATIONS = [
624
+ "span",
625
+ "trace",
626
+ "openai-agents",
627
+ "langgraph",
628
+ "claude-agent-sdk",
629
+ "vercel-ai"
630
+ ];
631
+ FRAMEWORK_INSTRUMENTATIONS = [
632
+ "openai-agents",
633
+ "langgraph",
634
+ "claude-agent-sdk",
635
+ "vercel-ai"
636
+ ];
637
+ FRAMEWORK_INSTRUMENTATION_SET = new Set(
638
+ FRAMEWORK_INSTRUMENTATIONS
639
+ );
640
+ }
641
+ });
642
+
643
+ // src/unrefTimer.ts
644
+ function unrefTimer(timer) {
645
+ const handle = timer;
646
+ if (typeof handle.unref === "function") {
647
+ handle.unref();
648
+ }
649
+ }
650
+ var init_unrefTimer = __esm({
651
+ "src/unrefTimer.ts"() {
652
+ "use strict";
653
+ }
654
+ });
655
+
656
+ // src/simulationPlan.ts
657
+ function planWaitSliceMs(timeoutMs) {
658
+ return Math.min(SIMULATION_PLAN_READ_TIMEOUT_MS, Math.max(timeoutMs, 0) / 2);
659
+ }
660
+ function parseSimulationPlan(body) {
661
+ if (typeof body !== "object" || body === null) {
662
+ return null;
663
+ }
664
+ const nodes = body.nodes;
665
+ if (!Array.isArray(nodes)) {
666
+ return null;
667
+ }
668
+ const contentOff = /* @__PURE__ */ new Map();
669
+ for (const node of nodes) {
670
+ if (typeof node !== "object" || node === null) {
671
+ continue;
672
+ }
673
+ const { traceFunctionKey, name, captureContent } = node;
674
+ if (typeof traceFunctionKey !== "string" || typeof name !== "string" || typeof captureContent !== "boolean") {
675
+ continue;
676
+ }
677
+ if (captureContent) {
678
+ continue;
679
+ }
680
+ const names = contentOff.get(traceFunctionKey) ?? /* @__PURE__ */ new Set();
681
+ names.add(name);
682
+ contentOff.set(traceFunctionKey, names);
683
+ }
684
+ return contentOff;
685
+ }
686
+ function traceIdOf(payload) {
687
+ const traceId = payload.traceId;
688
+ if (typeof traceId === "string") {
689
+ return traceId;
690
+ }
691
+ const id = payload.id;
692
+ return typeof id === "string" ? id : void 0;
693
+ }
694
+ function isRecord2(value) {
695
+ return typeof value === "object" && value !== null;
696
+ }
697
+ function rawSpanOf(payload) {
698
+ return isRecord2(payload.rawSpan) ? payload.rawSpan : void 0;
699
+ }
700
+ function spanDataOf(payload) {
701
+ const spanData = rawSpanOf(payload)?.span_data;
702
+ return isRecord2(spanData) ? spanData : void 0;
703
+ }
704
+ function spanNameOf(payload) {
705
+ const name = spanDataOf(payload)?.name;
706
+ return typeof name === "string" ? name : void 0;
707
+ }
708
+ function stripContent(payload) {
709
+ const rawSpan = rawSpanOf(payload);
710
+ const spanData = spanDataOf(payload);
711
+ if (rawSpan === void 0 || spanData === void 0) {
712
+ return payload;
713
+ }
714
+ const kept = { ...spanData };
715
+ for (const key of CONTENT_KEYS) {
716
+ delete kept[key];
717
+ }
718
+ kept[CONTENT_OFF_KEY] = true;
719
+ return { ...payload, rawSpan: { ...rawSpan, span_data: kept } };
720
+ }
721
+ function simulationPlanEnvDisabled() {
722
+ const value = readEnv(DISABLE_SIMULATION_PLAN_ENV);
723
+ return typeof value === "string" && value.trim() !== "";
724
+ }
725
+ function missingSimulationPlanEndpoint(error) {
726
+ return error instanceof BitfabError && error.status === 404;
727
+ }
728
+ var SIMULATION_PLAN_REFRESH_MS, SIMULATION_PLAN_RETRY_MS, SIMULATION_PLAN_READ_TIMEOUT_MS, SIMULATION_PLAN_MAX_HELD, DISABLE_SIMULATION_PLAN_ENV, ROOT_TRACE_FUNCTION_KEY_FIELD, CONTENT_OFF_KEY, CONTENT_KEYS, SimulationPlan;
729
+ var init_simulationPlan = __esm({
730
+ "src/simulationPlan.ts"() {
731
+ "use strict";
732
+ init_errors();
733
+ init_policyRefresh();
734
+ init_readEnv();
735
+ init_spanOrigin();
736
+ init_unrefTimer();
737
+ init_warnOnce();
738
+ SIMULATION_PLAN_REFRESH_MS = 6e4;
739
+ SIMULATION_PLAN_RETRY_MS = 1e4;
740
+ SIMULATION_PLAN_READ_TIMEOUT_MS = 5e3;
741
+ SIMULATION_PLAN_MAX_HELD = 1e3;
742
+ DISABLE_SIMULATION_PLAN_ENV = "BITFAB_DISABLE_SIM_PLAN";
743
+ ROOT_TRACE_FUNCTION_KEY_FIELD = "rootTraceFunctionKey";
744
+ CONTENT_OFF_KEY = "content_off_by_simulation_plan";
745
+ CONTENT_KEYS = ["input", "input_meta", "output", "output_meta"];
746
+ SimulationPlan = class {
747
+ constructor(source, enabled = true) {
748
+ this.source = source;
749
+ this.enabled = enabled;
750
+ this.policyRefresh = new PolicyRefresh();
751
+ this.retryPending = false;
752
+ this.waitingForFirstLoad = 0;
753
+ this.held = [];
754
+ this.droppedWhileHolding = 0;
755
+ this.stopped = false;
756
+ this.loaded = new Promise((resolve) => {
757
+ this.resolveLoaded = resolve;
758
+ });
759
+ }
760
+ disabled() {
761
+ return !this.enabled || simulationPlanEnvDisabled();
762
+ }
763
+ refresh() {
764
+ if (this.stopped || this.disabled()) {
765
+ return;
766
+ }
767
+ this.policyRefresh.run(
768
+ () => this.source.getSimulationPlan(),
769
+ (body) => {
770
+ const parsed = parseSimulationPlan(body);
771
+ if (parsed === null) {
772
+ this.scheduleRetry(
773
+ "sim-plan-unreadable",
774
+ "the sim plan response was not understood"
775
+ );
776
+ return;
777
+ }
778
+ this.markLoaded(parsed);
779
+ },
780
+ (error) => {
781
+ if (missingSimulationPlanEndpoint(error)) {
782
+ this.markLoaded(/* @__PURE__ */ new Map());
783
+ return;
784
+ }
785
+ this.scheduleRetry(
786
+ "sim-plan-unavailable",
787
+ `could not read the sim plan: ${error instanceof Error ? error.message : String(error)}`
788
+ );
789
+ }
790
+ );
791
+ }
792
+ markLoaded(parsed) {
793
+ if (this.stopped) {
794
+ return;
795
+ }
796
+ this.contentOffByKey = parsed;
797
+ this.policyRefresh.hold(SIMULATION_PLAN_REFRESH_MS);
798
+ this.retryPending = false;
799
+ this.clearRetryTimer();
800
+ this.resolveLoaded?.();
801
+ this.resolveLoaded = void 0;
802
+ this.releaseHeld();
803
+ }
804
+ scheduleRetry(warnKey, reason) {
805
+ if (this.stopped) {
806
+ return;
807
+ }
808
+ this.policyRefresh.hold(SIMULATION_PLAN_RETRY_MS);
809
+ if (this.contentOffByKey !== void 0) {
810
+ return;
811
+ }
812
+ this.retryPending = true;
813
+ warnOnce(
814
+ warnKey,
815
+ `${reason}. Spans are held until the first read succeeds; while a record is held it is retried every ${SIMULATION_PLAN_RETRY_MS / 1e3}s, otherwise on the next span.`
816
+ );
817
+ this.armRetry();
818
+ }
819
+ holdsWork() {
820
+ return this.held.length > 0 || this.waitingForFirstLoad > 0;
821
+ }
822
+ armRetry() {
823
+ if (this.stopped || !this.retryPending || this.retryTimer !== void 0 || !this.holdsWork()) {
824
+ return;
825
+ }
826
+ const timer = setTimeout(() => {
827
+ this.retryTimer = void 0;
828
+ this.policyRefresh.releaseHold();
829
+ this.refresh();
830
+ }, SIMULATION_PLAN_RETRY_MS);
831
+ unrefTimer(timer);
832
+ this.retryTimer = timer;
833
+ }
834
+ clearRetryTimer() {
835
+ if (this.retryTimer !== void 0) {
836
+ clearTimeout(this.retryTimer);
837
+ this.retryTimer = void 0;
838
+ }
839
+ }
840
+ syncRetryTimer() {
841
+ if (this.holdsWork()) {
842
+ return;
843
+ }
844
+ this.clearRetryTimer();
845
+ }
846
+ firstLoad() {
847
+ if (this.contentOffByKey !== void 0 || this.stopped || this.disabled()) {
848
+ return void 0;
849
+ }
850
+ return this.loaded;
851
+ }
852
+ send(payload, traceFunctionKey, submit) {
853
+ this.refresh();
854
+ if (this.stopped || this.disabled() || traceFunctionKey === void 0 || spanNameOf(payload) === void 0 || recordedByFramework(payload)) {
855
+ submit(payload);
856
+ return;
857
+ }
858
+ if (this.contentOffByKey === void 0) {
859
+ this.hold({ payload, traceFunctionKey, submit });
860
+ return;
861
+ }
862
+ this.submitEntry({ payload, traceFunctionKey, submit });
863
+ }
864
+ sendTrace(payload, submit) {
865
+ this.refresh();
866
+ if (this.stopped || this.disabled() || this.contentOffByKey !== void 0 || !this.holdsSpanOfTrace(traceIdOf(payload))) {
867
+ submit(payload);
868
+ return;
869
+ }
870
+ this.hold({ payload, traceFunctionKey: void 0, submit });
871
+ }
872
+ holdsSpanOfTrace(traceId) {
873
+ if (traceId === void 0) {
874
+ return false;
875
+ }
876
+ return this.held.some(
877
+ (entry) => entry.traceFunctionKey !== void 0 && traceIdOf(entry.payload) === traceId
878
+ );
879
+ }
880
+ hold(entry) {
881
+ this.held.push(entry);
882
+ while (this.held.length > SIMULATION_PLAN_MAX_HELD) {
883
+ this.held.shift();
884
+ this.droppedWhileHolding += 1;
885
+ }
886
+ this.armRetry();
887
+ }
888
+ submitEntry(entry) {
889
+ try {
890
+ entry.submit(
891
+ entry.traceFunctionKey === void 0 ? entry.payload : this.apply(entry.payload, entry.traceFunctionKey)
892
+ );
893
+ } catch (error) {
894
+ warnOnce(
895
+ "sim-plan-held-span-dropped",
896
+ `a span held for the sim plan was dropped when released: ${error instanceof Error ? error.message : String(error)}`
897
+ );
898
+ }
899
+ }
900
+ releaseHeld() {
901
+ const held = this.held;
902
+ this.held = [];
903
+ this.syncRetryTimer();
904
+ this.warnDropped();
905
+ for (const entry of held) {
906
+ this.submitEntry(entry);
907
+ }
908
+ }
909
+ warnDropped() {
910
+ if (this.droppedWhileHolding === 0) {
911
+ return;
912
+ }
913
+ const dropped = this.droppedWhileHolding;
914
+ this.droppedWhileHolding = 0;
915
+ warnOnce(
916
+ "sim-plan-held-overflow",
917
+ `${dropped} record(s) were dropped while the sim plan was still loading; at most ${SIMULATION_PLAN_MAX_HELD} are held per client.`
918
+ );
919
+ }
920
+ warnNeverLoaded() {
921
+ if (this.held.length === 0) {
922
+ return;
923
+ }
924
+ warnOnce(
925
+ "sim-plan-never-loaded",
926
+ `${this.held.length} record(s) were not sent because the sim plan never loaded`
927
+ );
928
+ }
929
+ async release(timeoutMs) {
930
+ this.refresh();
931
+ if (this.holdsWork()) {
932
+ await this.waitForFirstLoad(timeoutMs);
933
+ }
934
+ this.warnNeverLoaded();
935
+ this.warnDropped();
936
+ }
937
+ waitForFirstLoad(timeoutMs) {
938
+ const firstLoad = this.firstLoad();
939
+ if (firstLoad === void 0) {
940
+ return Promise.resolve();
941
+ }
942
+ this.waitingForFirstLoad += 1;
943
+ this.armRetry();
944
+ return new Promise((resolve) => {
945
+ let settled2 = false;
946
+ const finish = () => {
947
+ if (settled2) {
948
+ return;
949
+ }
950
+ settled2 = true;
951
+ this.waitingForFirstLoad -= 1;
952
+ this.syncRetryTimer();
953
+ resolve();
954
+ };
955
+ const timer = setTimeout(finish, Math.max(timeoutMs, 0));
956
+ void firstLoad.then(() => {
957
+ clearTimeout(timer);
958
+ finish();
959
+ });
960
+ });
961
+ }
962
+ stop() {
963
+ if (this.stopped) {
964
+ return;
965
+ }
966
+ this.stopped = true;
967
+ this.retryPending = false;
968
+ this.clearRetryTimer();
969
+ this.warnNeverLoaded();
970
+ this.held = [];
971
+ this.warnDropped();
972
+ }
973
+ contentOff(traceFunctionKey, spanName) {
974
+ return this.contentOffByKey?.get(traceFunctionKey)?.has(spanName) === true;
975
+ }
976
+ apply(payload, traceFunctionKey) {
977
+ if (recordedByFramework(payload)) {
978
+ return payload;
979
+ }
980
+ const name = spanNameOf(payload);
981
+ if (name === void 0 || !this.contentOff(traceFunctionKey, name)) {
982
+ return payload;
983
+ }
984
+ return stripContent(payload);
985
+ }
986
+ };
987
+ }
988
+ });
989
+
540
990
  // src/traceMetadata.ts
541
991
  function findRecord(traceId) {
542
992
  return activeRecords.get(traceId) ?? completedRecords.get(traceId);
@@ -653,19 +1103,6 @@ var init_transportTypes = __esm({
653
1103
  }
654
1104
  });
655
1105
 
656
- // src/unrefTimer.ts
657
- function unrefTimer(timer) {
658
- const handle = timer;
659
- if (typeof handle.unref === "function") {
660
- handle.unref();
661
- }
662
- }
663
- var init_unrefTimer = __esm({
664
- "src/unrefTimer.ts"() {
665
- "use strict";
666
- }
667
- });
668
-
669
1106
  // src/otel.ts
670
1107
  function readBoundedIntEnv(name, max, fallback, warnKey) {
671
1108
  const raw = readEnv(name);
@@ -1316,6 +1753,17 @@ __export(http_exports, {
1316
1753
  parseRetryAfterMs: () => parseRetryAfterMs,
1317
1754
  serializePayloadBody: () => serializePayloadBody
1318
1755
  });
1756
+ async function releaseHeldExternalSpans(timeoutMs) {
1757
+ const deadline = Date.now() + Math.max(timeoutMs, 0);
1758
+ for (const client of [...liveHttpClients]) {
1759
+ await client.releaseHeldExternalSpans?.(Math.max(0, deadline - Date.now()));
1760
+ }
1761
+ }
1762
+ function stopSimulationPlans() {
1763
+ for (const client of [...liveHttpClients]) {
1764
+ client.stopSimulationPlan?.();
1765
+ }
1766
+ }
1319
1767
  function awaitOnExit(promise) {
1320
1768
  pendingTracePromises.add(promise);
1321
1769
  void promise.finally(() => {
@@ -1326,7 +1774,10 @@ function awaitOnExit(promise) {
1326
1774
  }
1327
1775
  async function flushTraces(timeoutMs = 5e3) {
1328
1776
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1329
- const requestsFlushed = await awaitPendingRequests(timeoutMs);
1777
+ await releaseHeldExternalSpans(planWaitSliceMs(timeoutMs));
1778
+ const requestsFlushed = await awaitPendingRequests(
1779
+ Math.max(0, deadline - Date.now())
1780
+ );
1330
1781
  const transportsFlushed = await flushTraceTransports(
1331
1782
  Math.max(0, deadline - Date.now())
1332
1783
  );
@@ -1445,7 +1896,22 @@ function sourceTraceIdOf(payload) {
1445
1896
  const id = rawTrace?.id;
1446
1897
  return typeof id === "string" ? id : void 0;
1447
1898
  }
1448
- var REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS, REPLAY_COMPLETE_REQUEST_TIMEOUT_MS, OTLP_TRACES_ENDPOINT, RETRYABLE_STATUSES, EXIT_FLUSH_TIMEOUT_MS, DEFAULT_LIFECYCLE_TIMEOUT_MS2, pendingTracePromises, carrierSeq, HttpClient;
1899
+ function describeError(error) {
1900
+ return error instanceof Error ? error.message : String(error);
1901
+ }
1902
+ function warnDroppedExternalSpan(error) {
1903
+ warnOnce(
1904
+ "external-span-dropped",
1905
+ `a span was dropped because the send step failed: ${describeError(error)}`
1906
+ );
1907
+ }
1908
+ function warnDroppedExternalTrace(error) {
1909
+ warnOnce(
1910
+ "external-trace-dropped",
1911
+ `a trace was dropped because the send step failed: ${describeError(error)}`
1912
+ );
1913
+ }
1914
+ var REPLAY_DB_BRANCH_REQUEST_TIMEOUT_MS, REPLAY_COMPLETE_REQUEST_TIMEOUT_MS, OTLP_TRACES_ENDPOINT, RETRYABLE_STATUSES, EXIT_FLUSH_TIMEOUT_MS, DEFAULT_LIFECYCLE_TIMEOUT_MS2, pendingTracePromises, liveHttpClients, carrierSeq, HttpClient;
1449
1915
  var init_http = __esm({
1450
1916
  "src/http.ts"() {
1451
1917
  "use strict";
@@ -1454,6 +1920,7 @@ var init_http = __esm({
1454
1920
  init_errors();
1455
1921
  init_replayContext();
1456
1922
  init_serializePayload();
1923
+ init_simulationPlan();
1457
1924
  init_traceMetadata();
1458
1925
  init_transport();
1459
1926
  init_transportTypes();
@@ -1466,6 +1933,7 @@ var init_http = __esm({
1466
1933
  EXIT_FLUSH_TIMEOUT_MS = 5e3;
1467
1934
  DEFAULT_LIFECYCLE_TIMEOUT_MS2 = 3e4;
1468
1935
  pendingTracePromises = /* @__PURE__ */ new Set();
1936
+ liveHttpClients = /* @__PURE__ */ new Set();
1469
1937
  if (typeof process !== "undefined" && process.versions != null && process.versions.node != null) {
1470
1938
  let isFlushing = false;
1471
1939
  process.on("beforeExit", () => {
@@ -1473,12 +1941,27 @@ var init_http = __esm({
1473
1941
  return;
1474
1942
  }
1475
1943
  isFlushing = true;
1476
- void Promise.allSettled([
1477
- ...Array.from(pendingTracePromises).map((p) => p.catch(() => {
1478
- })),
1479
- shutdownTraceTransports(EXIT_FLUSH_TIMEOUT_MS).catch(() => false)
1480
- ]).then(() => {
1944
+ const planWaitMs = SIMULATION_PLAN_READ_TIMEOUT_MS;
1945
+ const stopFlushing = () => {
1481
1946
  isFlushing = false;
1947
+ };
1948
+ const deadline = setTimeout(
1949
+ stopFlushing,
1950
+ EXIT_FLUSH_TIMEOUT_MS + planWaitMs
1951
+ );
1952
+ unrefTimer(deadline);
1953
+ void releaseHeldExternalSpans(planWaitMs).catch(() => {
1954
+ }).then(() => {
1955
+ stopSimulationPlans();
1956
+ }).then(
1957
+ () => Promise.allSettled([
1958
+ ...Array.from(pendingTracePromises).map((p) => p.catch(() => {
1959
+ })),
1960
+ shutdownTraceTransports(EXIT_FLUSH_TIMEOUT_MS).catch(() => false)
1961
+ ])
1962
+ ).then(() => {
1963
+ clearTimeout(deadline);
1964
+ stopFlushing();
1482
1965
  });
1483
1966
  });
1484
1967
  }
@@ -1497,6 +1980,7 @@ var init_http = __esm({
1497
1980
  this.apiKey = config.apiKey;
1498
1981
  this.serviceUrl = config.serviceUrl;
1499
1982
  this.timeout = config.timeout ?? 12e4;
1983
+ liveHttpClients.add(this);
1500
1984
  }
1501
1985
  /**
1502
1986
  * Resolve the API key at the moment it is needed (request time), invoking
@@ -1694,9 +2178,14 @@ var init_http = __esm({
1694
2178
  * attributing its timeout here would fail a client whose own work succeeded.
1695
2179
  */
1696
2180
  async settleDeferredWork(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS2) {
2181
+ const deadline = Date.now() + Math.max(timeoutMs, 0);
2182
+ await this.releaseHeldExternalSpans?.(planWaitSliceMs(timeoutMs));
1697
2183
  await replayContextReady.catch(() => {
1698
2184
  });
1699
- return waitForPromises(Array.from(this.deferredWork), timeoutMs);
2185
+ return waitForPromises(
2186
+ Array.from(this.deferredWork),
2187
+ Math.max(0, deadline - Date.now())
2188
+ );
1700
2189
  }
1701
2190
  /**
1702
2191
  * Wait for spans queued by this client to be delivered, within one deadline.
@@ -1704,7 +2193,9 @@ var init_http = __esm({
1704
2193
  */
1705
2194
  async waitForPendingRequests(timeoutMs = DEFAULT_LIFECYCLE_TIMEOUT_MS2) {
1706
2195
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1707
- const settled2 = await this.settleDeferredWork(timeoutMs);
2196
+ const settled2 = await this.settleDeferredWork(
2197
+ Math.max(0, deadline - Date.now())
2198
+ );
1708
2199
  const flushed = await this.traceTransport?.flush(Math.max(0, deadline - Date.now())) ?? true;
1709
2200
  return settled2 && flushed;
1710
2201
  }
@@ -1719,6 +2210,13 @@ var init_http = __esm({
1719
2210
  }
1720
2211
  const deadline = Date.now() + Math.max(timeoutMs, 0);
1721
2212
  this.closing = (async () => {
2213
+ await this.releaseHeldExternalSpans?.(planWaitSliceMs(timeoutMs));
2214
+ this.stopSimulationPlan?.();
2215
+ this.externalSpanTransform = void 0;
2216
+ this.externalTraceTransform = void 0;
2217
+ this.releaseHeldExternalSpans = void 0;
2218
+ this.stopSimulationPlan = void 0;
2219
+ liveHttpClients.delete(this);
1722
2220
  const settled2 = await this.settleDeferredWork(
1723
2221
  Math.max(0, deadline - Date.now())
1724
2222
  );
@@ -1830,6 +2328,13 @@ var init_http = __esm({
1830
2328
  protocol
1831
2329
  });
1832
2330
  }
2331
+ async getSimulationPlan() {
2332
+ return this.get(
2333
+ "/api/sdk/sim-plan",
2334
+ SIMULATION_PLAN_READ_TIMEOUT_MS,
2335
+ { Connection: "close" }
2336
+ );
2337
+ }
1833
2338
  async getTraceSpan(traceId, lookup) {
1834
2339
  const searchParams = new URLSearchParams();
1835
2340
  if (lookup.id !== void 0) {
@@ -1846,14 +2351,18 @@ var init_http = __esm({
1846
2351
  * GET a JSON endpoint on the service with the client's API key. Throws a
1847
2352
  * `BitfabError` carrying the status text for any non-2xx response.
1848
2353
  */
1849
- async get(endpoint) {
2354
+ async get(endpoint, timeoutMs, extraHeaders) {
1850
2355
  const url = `${this.serviceUrl}${endpoint}`;
2356
+ const timeout = timeoutMs ?? this.timeout;
1851
2357
  const controller = new AbortController();
1852
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
2358
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
1853
2359
  try {
1854
2360
  const response = await fetch(url, {
1855
2361
  method: "GET",
1856
- headers: { Authorization: `Bearer ${this.resolveApiKey() ?? ""}` },
2362
+ headers: {
2363
+ Authorization: `Bearer ${this.resolveApiKey() ?? ""}`,
2364
+ ...extraHeaders
2365
+ },
1857
2366
  signal: controller.signal
1858
2367
  });
1859
2368
  if (!response.ok) {
@@ -1872,7 +2381,7 @@ var init_http = __esm({
1872
2381
  }
1873
2382
  if (error instanceof Error) {
1874
2383
  if (error.name === "AbortError") {
1875
- throw new BitfabError(`Request timed out after ${this.timeout}ms`);
2384
+ throw new BitfabError(`Request timed out after ${timeout}ms`);
1876
2385
  }
1877
2386
  throw new BitfabError(error.message);
1878
2387
  }
@@ -1899,16 +2408,27 @@ var init_http = __esm({
1899
2408
  carrierMeta("internal_trace", body, void 0)
1900
2409
  );
1901
2410
  }
1902
- /**
1903
- * Queue an external span (from withSpan wrapper or OpenAI tracing) onto this
1904
- * client's batching transport. Fire-and-forget: the transport owns delivery,
1905
- * so callers await `flushTraces()` or `close()` rather than a per-span
1906
- * promise.
1907
- */
1908
2411
  sendExternalSpan(payload) {
2412
+ const gate = this.externalSpanTransform;
2413
+ if (gate === void 0) {
2414
+ this.submitExternalSpan(payload);
2415
+ return;
2416
+ }
2417
+ try {
2418
+ gate(payload, (ready) => this.submitExternalSpan(ready));
2419
+ } catch (error) {
2420
+ warnDroppedExternalSpan(error);
2421
+ }
2422
+ }
2423
+ submitExternalSpan(payload) {
2424
+ const body = {
2425
+ ...payload,
2426
+ sdkVersion: __version__
2427
+ };
2428
+ delete body[ROOT_TRACE_FUNCTION_KEY_FIELD];
1909
2429
  this.getTraceTransport()?.submit(
1910
2430
  "external_span",
1911
- { ...payload, sdkVersion: __version__ },
2431
+ body,
1912
2432
  this.recordedMeta("external_span", payload, carrierRef(payload))
1913
2433
  );
1914
2434
  }
@@ -1920,6 +2440,18 @@ var init_http = __esm({
1920
2440
  */
1921
2441
  sendExternalTrace(rawPayload) {
1922
2442
  const payload = mergeCallerMetadataIntoTracePayload(rawPayload);
2443
+ const gate = this.externalTraceTransform;
2444
+ if (gate === void 0) {
2445
+ this.submitExternalTrace(payload);
2446
+ return;
2447
+ }
2448
+ try {
2449
+ gate(payload, (ready) => this.submitExternalTrace(ready));
2450
+ } catch (error) {
2451
+ warnDroppedExternalTrace(error);
2452
+ }
2453
+ }
2454
+ submitExternalTrace(payload) {
1923
2455
  this.getTraceTransport()?.submit(
1924
2456
  "external_trace",
1925
2457
  {
@@ -3633,6 +4165,7 @@ function finalizeTracePayload(payload) {
3633
4165
  // src/claudeAgentSdk.ts
3634
4166
  init_randomUuid();
3635
4167
  init_serialize();
4168
+ init_spanOrigin();
3636
4169
 
3637
4170
  // src/timestamp.ts
3638
4171
  var lastTimestampMicros = 0;
@@ -3840,6 +4373,7 @@ var BitfabClaudeAgentHandler = class {
3840
4373
  trace_id: spanInfo.traceId,
3841
4374
  started_at: spanInfo.startedAt,
3842
4375
  ended_at: spanInfo.endedAt ?? nowIso(),
4376
+ span_origin: makeSpanOrigin("claude-agent-sdk"),
3843
4377
  span_data: spanData
3844
4378
  };
3845
4379
  if (spanInfo.parentId !== null) {
@@ -5151,6 +5685,7 @@ init_constants();
5151
5685
  init_http();
5152
5686
  init_randomUuid();
5153
5687
  init_serialize();
5688
+ init_spanOrigin();
5154
5689
  var LANGSMITH_HIDDEN_TAG = "langsmith:hidden";
5155
5690
  var CHAIN_RUN_TYPES = /* @__PURE__ */ new Set(["chain", "parser", "prompt"]);
5156
5691
  var LANGGRAPH_METADATA_KEYS = [
@@ -5505,6 +6040,7 @@ var BitfabLangGraphCallbackHandler = class {
5505
6040
  trace_id: spanInfo.traceId,
5506
6041
  started_at: spanInfo.startedAt,
5507
6042
  ended_at: spanInfo.endedAt ?? nowIso2(),
6043
+ span_origin: makeSpanOrigin("langgraph"),
5508
6044
  span_data: spanData
5509
6045
  };
5510
6046
  if (spanInfo.parentId !== null) {
@@ -5766,14 +6302,14 @@ var BitfabLangGraphCallbackHandler = class {
5766
6302
  init_http();
5767
6303
  init_replayContext();
5768
6304
  var TOOL_RESULT_TAG = "__bitfabLangGraphToolResult";
5769
- function isRecord(value) {
6305
+ function isRecord3(value) {
5770
6306
  return typeof value === "object" && value !== null;
5771
6307
  }
5772
6308
  function isToolMessage(value) {
5773
- return isRecord(value) && value.lc_direct_tool_output === true && value.type === "tool" && (typeof value.content === "string" || Array.isArray(value.content));
6309
+ return isRecord3(value) && value.lc_direct_tool_output === true && value.type === "tool" && (typeof value.content === "string" || Array.isArray(value.content));
5774
6310
  }
5775
6311
  function isCommand(value) {
5776
- return isRecord(value) && value.lg_name === "Command";
6312
+ return isRecord3(value) && value.lg_name === "Command";
5777
6313
  }
5778
6314
  function encodeNested(value) {
5779
6315
  if (isToolMessage(value) || isCommand(value)) {
@@ -5782,7 +6318,7 @@ function encodeNested(value) {
5782
6318
  if (Array.isArray(value)) {
5783
6319
  return value.map(encodeNested);
5784
6320
  }
5785
- if (isRecord(value)) {
6321
+ if (isRecord3(value)) {
5786
6322
  return Object.fromEntries(
5787
6323
  Object.entries(value).map(([key, entry]) => [key, encodeNested(entry)])
5788
6324
  );
@@ -5805,17 +6341,17 @@ function encodeNativeToolResult(value) {
5805
6341
  }
5806
6342
  return {
5807
6343
  [TOOL_RESULT_TAG]: "command",
5808
- graph: isRecord(value) && typeof value.graph === "string" ? value.graph : void 0,
5809
- update: isRecord(value) ? encodeNested(value.update) : void 0,
5810
- resume: isRecord(value) ? encodeNested(value.resume) : void 0,
5811
- goto: isRecord(value) ? encodeNested(value.goto) : void 0
6344
+ graph: isRecord3(value) && typeof value.graph === "string" ? value.graph : void 0,
6345
+ update: isRecord3(value) ? encodeNested(value.update) : void 0,
6346
+ resume: isRecord3(value) ? encodeNested(value.resume) : void 0,
6347
+ goto: isRecord3(value) ? encodeNested(value.goto) : void 0
5812
6348
  };
5813
6349
  }
5814
6350
  function finalizeToolResult(value) {
5815
6351
  return isToolMessage(value) || isCommand(value) ? encodeNativeToolResult(value) : value;
5816
6352
  }
5817
6353
  function isEncodedToolResult(value) {
5818
- return isRecord(value) && typeof value[TOOL_RESULT_TAG] === "string";
6354
+ return isRecord3(value) && typeof value[TOOL_RESULT_TAG] === "string";
5819
6355
  }
5820
6356
  async function loadLangChainCore() {
5821
6357
  try {
@@ -5851,7 +6387,7 @@ async function reviveNested(value, toolCallId) {
5851
6387
  if (Array.isArray(value)) {
5852
6388
  return Promise.all(value.map((entry) => reviveNested(entry, toolCallId)));
5853
6389
  }
5854
- if (isRecord(value)) {
6390
+ if (isRecord3(value)) {
5855
6391
  const entries = await Promise.all(
5856
6392
  Object.entries(value).map(async ([key, entry]) => [
5857
6393
  key,
@@ -5876,16 +6412,16 @@ async function reviveToolResult(value, toolCallId) {
5876
6412
  ...value.artifact !== void 0 && {
5877
6413
  artifact: await reviveNested(value.artifact, toolCallId)
5878
6414
  },
5879
- ...isRecord(value.metadata) && {
6415
+ ...isRecord3(value.metadata) && {
5880
6416
  metadata: await reviveNested(value.metadata, toolCallId)
5881
6417
  },
5882
- ...isRecord(value.additionalKwargs) && {
6418
+ ...isRecord3(value.additionalKwargs) && {
5883
6419
  additional_kwargs: await reviveNested(
5884
6420
  value.additionalKwargs,
5885
6421
  toolCallId
5886
6422
  )
5887
6423
  },
5888
- ...isRecord(value.responseMetadata) && {
6424
+ ...isRecord3(value.responseMetadata) && {
5889
6425
  response_metadata: await reviveNested(
5890
6426
  value.responseMetadata,
5891
6427
  toolCallId
@@ -5955,8 +6491,8 @@ var BitfabLangGraphIntegration = class {
5955
6491
  get: (target, property) => {
5956
6492
  if (property === "invoke") {
5957
6493
  return async (input, ...rest) => {
5958
- const toolCallId = isRecord(input) && typeof input.id === "string" ? input.id : "";
5959
- const args = isRecord(input) && "args" in input ? input.args : input;
6494
+ const toolCallId = isRecord3(input) && typeof input.id === "string" ? input.id : "";
6495
+ const args = isRecord3(input) && "args" in input ? input.args : input;
5960
6496
  this.assertReplayToolResultExists(toolName, shouldMock);
5961
6497
  const execute = this.client.withSpan(
5962
6498
  this.traceFunctionKey,
@@ -5966,7 +6502,8 @@ var BitfabLangGraphIntegration = class {
5966
6502
  captureWhen: "nested",
5967
6503
  mockOnReplay: shouldMock,
5968
6504
  finalize: finalizeToolResult,
5969
- surface: "inherit"
6505
+ surface: "inherit",
6506
+ instrumentation: "langgraph"
5970
6507
  },
5971
6508
  async (_args) => await originalInvoke(input, ...rest)
5972
6509
  );
@@ -6009,7 +6546,12 @@ var BitfabLangGraphIntegration = class {
6009
6546
  wrapInvoke(fn) {
6010
6547
  return this.client.withSpan(
6011
6548
  this.traceFunctionKey,
6012
- { name: this.traceFunctionKey, type: "agent", surface: "inherit" },
6549
+ {
6550
+ name: this.traceFunctionKey,
6551
+ type: "agent",
6552
+ surface: "inherit",
6553
+ instrumentation: "langgraph"
6554
+ },
6013
6555
  fn
6014
6556
  );
6015
6557
  }
@@ -6065,7 +6607,8 @@ var BitfabOpenAIAgentHandler = class {
6065
6607
  const options_ = {
6066
6608
  type: "agent",
6067
6609
  finalize,
6068
- surface: "inherit"
6610
+ surface: "inherit",
6611
+ instrumentation: "openai-agents"
6069
6612
  };
6070
6613
  const traced = this.withSpanFn(
6071
6614
  this.traceFunctionKey,
@@ -6081,6 +6624,7 @@ var BitfabOpenAIAgentHandler = class {
6081
6624
  };
6082
6625
 
6083
6626
  // src/client.ts
6627
+ init_policyRefresh();
6084
6628
  init_randomUuid();
6085
6629
  init_replay();
6086
6630
 
@@ -6164,12 +6708,15 @@ function runWithSeedContext(ctx, fn) {
6164
6708
 
6165
6709
  // src/client.ts
6166
6710
  init_serialize();
6711
+ init_simulationPlan();
6712
+ init_spanOrigin();
6167
6713
  init_traceMetadata();
6168
6714
 
6169
6715
  // src/tracing.ts
6170
6716
  init_constants();
6171
6717
  init_http();
6172
6718
  init_randomUuid();
6719
+ init_spanOrigin();
6173
6720
  var BitfabOpenAITracingProcessor = class {
6174
6721
  /**
6175
6722
  * Initialize the tracing processor.
@@ -6371,6 +6918,7 @@ var BitfabOpenAITracingProcessor = class {
6371
6918
  * Build span payload for the external spans API.
6372
6919
  */
6373
6920
  buildSpanPayload(serializedSpan, errors) {
6921
+ serializedSpan.span_origin = makeSpanOrigin("openai-agents");
6374
6922
  const payload = {
6375
6923
  id: randomUuid(),
6376
6924
  type: "openai",
@@ -6500,7 +7048,8 @@ var BitfabVercelAiHandler = class {
6500
7048
  {
6501
7049
  type: "llm",
6502
7050
  finalize: (result) => summarizeGenerate(result ?? {}, label),
6503
- surface: "inherit"
7051
+ surface: "inherit",
7052
+ instrumentation: "vercel-ai"
6504
7053
  },
6505
7054
  () => doGenerate()
6506
7055
  );
@@ -6518,7 +7067,12 @@ var BitfabVercelAiHandler = class {
6518
7067
  // The wrapped fn returns immediately with the live stream, so the span
6519
7068
  // output cannot be read from the return value. `finalize` instead
6520
7069
  // awaits the summary the accumulator resolves once the stream drains.
6521
- { type: "llm", finalize: () => summary, surface: "inherit" },
7070
+ {
7071
+ type: "llm",
7072
+ finalize: () => summary,
7073
+ surface: "inherit",
7074
+ instrumentation: "vercel-ai"
7075
+ },
6522
7076
  async () => {
6523
7077
  const result = await doStream();
6524
7078
  const stream = result.stream.pipeThrough(
@@ -6536,6 +7090,19 @@ var BitfabVercelAiHandler = class {
6536
7090
  // src/client.ts
6537
7091
  init_warnOnce();
6538
7092
  var activeTraceStates = /* @__PURE__ */ new Map();
7093
+ function rootTraceFunctionKeyOf(payload) {
7094
+ const captured = payload[ROOT_TRACE_FUNCTION_KEY_FIELD];
7095
+ if (typeof captured === "string") {
7096
+ return captured;
7097
+ }
7098
+ const traceId = payload.traceId;
7099
+ const live = typeof traceId === "string" ? activeTraceStates.get(traceId)?.traceFunctionKey : void 0;
7100
+ if (typeof live === "string") {
7101
+ return live;
7102
+ }
7103
+ const own = payload.traceFunctionKey;
7104
+ return typeof own === "string" ? own : void 0;
7105
+ }
6539
7106
  var asyncLocalStorage = null;
6540
7107
  var SPAN_CONTEXT_STORAGE_SYMBOL = /* @__PURE__ */ Symbol.for("bitfab.spanContextStorage");
6541
7108
  var initializeAsyncContext = () => {
@@ -6940,6 +7507,14 @@ var Bitfab = class {
6940
7507
  serviceUrl: this.serviceUrl,
6941
7508
  timeout: this.timeout
6942
7509
  });
7510
+ this.simulationPlan = new SimulationPlan(
7511
+ this.httpClient,
7512
+ config.simulationPlan ?? true
7513
+ );
7514
+ this.httpClient.externalSpanTransform = (payload, submit) => this.applySimulationPlan(payload, submit);
7515
+ this.httpClient.externalTraceTransform = (payload, submit) => this.simulationPlan.sendTrace(payload, submit);
7516
+ this.httpClient.releaseHeldExternalSpans = (timeoutMs) => this.simulationPlan.release(timeoutMs);
7517
+ this.httpClient.stopSimulationPlan = () => this.simulationPlan.stop();
6943
7518
  this.datasets = new DatasetsClient(this.httpClient);
6944
7519
  this.traces = new TracesClient(this.httpClient);
6945
7520
  this.labels = new LabelsClient(this.httpClient);
@@ -7090,6 +7665,7 @@ var Bitfab = class {
7090
7665
  }
7091
7666
  createAutoTraceRoot(traceFunctionKey, name, options, fn) {
7092
7667
  const self = this;
7668
+ self.simulationPlan.refresh();
7093
7669
  const maxDepth = autoTraceLimit(
7094
7670
  options.maxDepth,
7095
7671
  DEFAULT_AUTO_TRACE_MAX_DEPTH
@@ -7105,7 +7681,8 @@ var Bitfab = class {
7105
7681
  name: options.name,
7106
7682
  nameFor: rootNameFor,
7107
7683
  type: options.type ?? "custom",
7108
- surface: "opt-out"
7684
+ surface: "opt-out",
7685
+ instrumentation: "trace"
7109
7686
  };
7110
7687
  const buildTracedRoot = (spanOptions) => this.withSpan(
7111
7688
  traceFunctionKey,
@@ -7116,6 +7693,7 @@ var Bitfab = class {
7116
7693
  traceFunctionKey
7117
7694
  );
7118
7695
  self.refreshAutoTraceCapturePolicy(traceFunctionKey);
7696
+ self.simulationPlan.refresh();
7119
7697
  let spansUsed = 0;
7120
7698
  let truncated = false;
7121
7699
  const warnTruncated = () => {
@@ -7192,6 +7770,7 @@ var Bitfab = class {
7192
7770
  type: nodeConfiguration?.type ?? "function",
7193
7771
  captureWhen: "nested",
7194
7772
  surface: "opt-out",
7773
+ instrumentation: "trace",
7195
7774
  functionId: definition.id,
7196
7775
  captureContent: nodeConfiguration !== void 0 || capturePolicy === void 0 || capturePolicy.has(definition.id),
7197
7776
  autoTraceDefinition: definition,
@@ -7223,6 +7802,7 @@ var Bitfab = class {
7223
7802
  type: "function",
7224
7803
  captureWhen: "nested",
7225
7804
  surface: "opt-out",
7805
+ instrumentation: "trace",
7226
7806
  captureContent: true,
7227
7807
  ...link !== void 0 && { nestedTrace: link }
7228
7808
  };
@@ -7288,38 +7868,36 @@ var Bitfab = class {
7288
7868
  Object.defineProperty(autoTraceRoot, "_bitfabWrappedFn", { value: fn });
7289
7869
  return autoTraceRoot;
7290
7870
  }
7871
+ applySimulationPlan(payload, submit) {
7872
+ this.simulationPlan.send(payload, rootTraceFunctionKeyOf(payload), submit);
7873
+ }
7291
7874
  refreshAutoTraceCapturePolicy(traceFunctionKey) {
7292
- const now = Date.now();
7293
- const state = this.autoTracePolicyRefreshes.get(traceFunctionKey) ?? {
7294
- refreshAfter: 0
7295
- };
7296
- if (state.inFlight || now < state.refreshAfter) {
7297
- return;
7298
- }
7299
- const request = this.httpClient.getAutoTracePolicy(
7300
- traceFunctionKey,
7301
- AUTO_TRACE_PROTOCOL
7302
- ).then((policy) => {
7303
- if (policy.protocol !== AUTO_TRACE_PROTOCOL) {
7304
- state.refreshAfter = Date.now() + AUTO_TRACE_POLICY_RETRY_MS;
7305
- return;
7306
- }
7307
- const functionIds = Array.isArray(policy.functionIds) ? policy.functionIds.filter(
7308
- (id) => typeof id === "string" && id.startsWith(`${AUTO_TRACE_PROTOCOL}:`)
7309
- ).slice(0, DEFAULT_AUTO_TRACE_MAX_SPANS) : [];
7310
- __setBitfabAutoTraceCapturePolicy(
7311
- this,
7312
- traceFunctionKey,
7313
- policy.revision === null ? void 0 : functionIds
7314
- );
7315
- state.refreshAfter = Date.now() + AUTO_TRACE_POLICY_REFRESH_MS;
7316
- }).catch(() => {
7317
- state.refreshAfter = Date.now() + AUTO_TRACE_POLICY_RETRY_MS;
7318
- }).finally(() => {
7319
- state.inFlight = void 0;
7320
- });
7321
- state.inFlight = request;
7875
+ const state = this.autoTracePolicyRefreshes.get(traceFunctionKey) ?? new PolicyRefresh();
7322
7876
  this.autoTracePolicyRefreshes.set(traceFunctionKey, state);
7877
+ state.run(
7878
+ () => this.httpClient.getAutoTracePolicy(
7879
+ traceFunctionKey,
7880
+ AUTO_TRACE_PROTOCOL
7881
+ ),
7882
+ (policy) => {
7883
+ if (policy.protocol !== AUTO_TRACE_PROTOCOL) {
7884
+ state.hold(AUTO_TRACE_POLICY_RETRY_MS);
7885
+ return;
7886
+ }
7887
+ const functionIds = Array.isArray(policy.functionIds) ? policy.functionIds.filter(
7888
+ (id) => typeof id === "string" && id.startsWith(`${AUTO_TRACE_PROTOCOL}:`)
7889
+ ).slice(0, DEFAULT_AUTO_TRACE_MAX_SPANS) : [];
7890
+ __setBitfabAutoTraceCapturePolicy(
7891
+ this,
7892
+ traceFunctionKey,
7893
+ policy.revision === null ? void 0 : functionIds
7894
+ );
7895
+ state.hold(AUTO_TRACE_POLICY_REFRESH_MS);
7896
+ },
7897
+ () => {
7898
+ state.hold(AUTO_TRACE_POLICY_RETRY_MS);
7899
+ }
7900
+ );
7323
7901
  }
7324
7902
  /**
7325
7903
  * Flush and permanently close this client's tracing resources: its pending
@@ -7803,6 +8381,7 @@ var Bitfab = class {
7803
8381
  * @returns A wrapped function with the same signature that creates spans for inputs and outputs
7804
8382
  */
7805
8383
  withSpan(traceFunctionKey, optionsOrFn, maybeFn) {
8384
+ this.simulationPlan.refresh();
7806
8385
  const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
7807
8386
  const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
7808
8387
  const self = this;
@@ -7894,8 +8473,10 @@ var Bitfab = class {
7894
8473
  const testRunId = replayCtxAtStart?.testRunId ?? options.testRunId;
7895
8474
  if (isRootSpan && !activeTraceStates.has(traceId)) {
7896
8475
  const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
8476
+ self.simulationPlan.refresh();
7897
8477
  activeTraceStates.set(traceId, {
7898
8478
  traceId,
8479
+ traceFunctionKey,
7899
8480
  startedAt,
7900
8481
  contexts: [],
7901
8482
  ...testRunId !== void 0 && { testRunId },
@@ -7910,8 +8491,10 @@ var Bitfab = class {
7910
8491
  registeredTraceId = traceId;
7911
8492
  }
7912
8493
  const functionName = fn.name !== "" ? fn.name : void 0;
8494
+ const rootTraceFunctionKey = activeTraceStates.get(traceId)?.traceFunctionKey ?? traceFunctionKey;
7913
8495
  const baseSpanParams = {
7914
8496
  traceFunctionKey,
8497
+ rootTraceFunctionKey,
7915
8498
  functionName,
7916
8499
  spanName: options.name ?? options.nameFor?.(this) ?? qualifiedSpanName(this, functionName) ?? traceFunctionKey,
7917
8500
  traceId,
@@ -7920,6 +8503,7 @@ var Bitfab = class {
7920
8503
  inputs,
7921
8504
  startedAt,
7922
8505
  spanType: options.type ?? "custom",
8506
+ instrumentation: options.instrumentation ?? "span",
7923
8507
  functionId: options.functionId,
7924
8508
  captureContent: options.captureContent ?? true,
7925
8509
  autoTraceDefinition: options.autoTraceDefinition,
@@ -8335,6 +8919,7 @@ var Bitfab = class {
8335
8919
  * @returns A BitfabFunction instance for wrapping functions
8336
8920
  */
8337
8921
  getFunction(traceFunctionKey) {
8922
+ this.simulationPlan.refresh();
8338
8923
  return new BitfabFunction(this, traceFunctionKey);
8339
8924
  }
8340
8925
  /**
@@ -8422,6 +9007,7 @@ var Bitfab = class {
8422
9007
  trace_id: params.traceId,
8423
9008
  started_at: params.startedAt,
8424
9009
  ended_at: params.endedAt,
9010
+ span_origin: makeSpanOrigin(params.instrumentation),
8425
9011
  span_data: {
8426
9012
  name: params.spanName,
8427
9013
  type: params.spanType,
@@ -8482,6 +9068,9 @@ var Bitfab = class {
8482
9068
  source: "typescript-sdk-function",
8483
9069
  sourceTraceId: params.traceId,
8484
9070
  traceFunctionKey: params.traceFunctionKey,
9071
+ ...params.rootTraceFunctionKey !== void 0 && {
9072
+ [ROOT_TRACE_FUNCTION_KEY_FIELD]: params.rootTraceFunctionKey
9073
+ },
8485
9074
  rawSpan: externalSpan,
8486
9075
  ...params.testRunId && { testRunId: params.testRunId },
8487
9076
  ...params.mocked && { mocked: true },
@@ -8550,6 +9139,7 @@ var Bitfab = class {
8550
9139
  }
8551
9140
  activeTraceStates.set(traceId, {
8552
9141
  traceId,
9142
+ traceFunctionKey,
8553
9143
  startedAt,
8554
9144
  contexts: [],
8555
9145
  ingestionType: "seeded",
@@ -8559,6 +9149,7 @@ var Bitfab = class {
8559
9149
  try {
8560
9150
  this.sendWrapperSpan({
8561
9151
  traceFunctionKey,
9152
+ rootTraceFunctionKey: traceFunctionKey,
8562
9153
  spanName: options.spanName ?? traceFunctionKey,
8563
9154
  traceId,
8564
9155
  spanId: randomUuid(),
@@ -8568,6 +9159,7 @@ var Bitfab = class {
8568
9159
  startedAt,
8569
9160
  endedAt: startedAt,
8570
9161
  spanType: options.spanType ?? "agent",
9162
+ instrumentation: "span",
8571
9163
  captureContent: true
8572
9164
  });
8573
9165
  this.sendTraceCompletion({
@@ -8613,6 +9205,7 @@ var Bitfab = class {
8613
9205
  }
8614
9206
  activeTraceStates.set(traceId, {
8615
9207
  traceId,
9208
+ traceFunctionKey,
8616
9209
  startedAt: nowIsoTimestamp(),
8617
9210
  contexts: [],
8618
9211
  ingestionType: "seeded",