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/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.53.0";
100
+ __version__ = "0.53.2";
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,26 @@ function runWithSeedContext(ctx, fn) {
6187
6731
 
6188
6732
  // src/client.ts
6189
6733
  init_serialize();
6734
+ init_simulationPlan();
6735
+ init_spanOrigin();
6736
+
6737
+ // src/streamFinalizationError.ts
6738
+ var StreamFinalizationError = class extends Error {
6739
+ constructor(message, output) {
6740
+ super(message);
6741
+ this.output = output;
6742
+ this.name = "StreamFinalizationError";
6743
+ }
6744
+ };
6745
+
6746
+ // src/client.ts
6190
6747
  init_traceMetadata();
6191
6748
 
6192
6749
  // src/tracing.ts
6193
6750
  init_constants();
6194
6751
  init_http();
6195
6752
  init_randomUuid();
6753
+ init_spanOrigin();
6196
6754
  var BitfabOpenAITracingProcessor = class {
6197
6755
  /**
6198
6756
  * Initialize the tracing processor.
@@ -6394,6 +6952,7 @@ var BitfabOpenAITracingProcessor = class {
6394
6952
  * Build span payload for the external spans API.
6395
6953
  */
6396
6954
  buildSpanPayload(serializedSpan, errors) {
6955
+ serializedSpan.span_origin = makeSpanOrigin("openai-agents");
6397
6956
  const payload = {
6398
6957
  id: randomUuid(),
6399
6958
  type: "openai",
@@ -6458,52 +7017,110 @@ function summarizeGenerate(result, model) {
6458
7017
  }
6459
7018
  return summary;
6460
7019
  }
6461
- function accumulateStream(onComplete, model) {
7020
+ function accumulateStream(source, onComplete, model) {
7021
+ const reader = source.getReader();
6462
7022
  let text = "";
6463
7023
  const toolCalls = [];
6464
7024
  let usage;
6465
7025
  let finishReason;
6466
7026
  let completed = false;
6467
- const complete = () => {
7027
+ let streamFailure;
7028
+ const complete = (failure = streamFailure) => {
6468
7029
  if (completed) {
6469
7030
  return;
6470
7031
  }
6471
7032
  completed = true;
6472
- const summary = {
7033
+ let error;
7034
+ if (failure !== void 0) {
7035
+ try {
7036
+ error = failure.error instanceof Error ? failure.error.message : String(
7037
+ failure.error ?? (failure.cancelled === true ? "Stream cancelled" : "Stream failed")
7038
+ );
7039
+ } catch {
7040
+ error = "Stream failed";
7041
+ }
7042
+ }
7043
+ onComplete({
6473
7044
  text,
6474
7045
  toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
6475
7046
  usage,
6476
- finishReason
6477
- };
6478
- if (model) {
6479
- summary.model = model;
6480
- }
6481
- onComplete(summary);
7047
+ finishReason,
7048
+ ...model !== void 0 && { model },
7049
+ ...failure !== void 0 && { error },
7050
+ ...failure?.cancelled === true && { cancelled: true }
7051
+ });
6482
7052
  };
6483
- return new TransformStream({
6484
- transform(part, controller) {
6485
- try {
6486
- if (part?.type === "text-delta") {
6487
- text += part.delta ?? part.textDelta ?? "";
6488
- } else if (part?.type === "tool-call") {
6489
- toolCalls.push({
6490
- toolCallId: part.toolCallId,
6491
- toolName: part.toolName,
6492
- input: part.input ?? part.args
6493
- });
6494
- } else if (part?.type === "finish") {
6495
- usage = part.usage;
6496
- finishReason = part.finishReason;
6497
- complete();
7053
+ return new ReadableStream(
7054
+ {
7055
+ start(controller) {
7056
+ void reader.closed.then(
7057
+ () => {
7058
+ queueMicrotask(() => {
7059
+ if (!completed) {
7060
+ complete();
7061
+ reader.releaseLock();
7062
+ controller.close();
7063
+ }
7064
+ });
7065
+ },
7066
+ (error) => {
7067
+ if (!completed) {
7068
+ complete({ error });
7069
+ controller.error(error);
7070
+ reader.releaseLock();
7071
+ }
7072
+ }
7073
+ );
7074
+ },
7075
+ async pull(controller) {
7076
+ try {
7077
+ const { done, value: part } = await reader.read();
7078
+ if (completed) {
7079
+ return;
7080
+ }
7081
+ if (done) {
7082
+ complete();
7083
+ reader.releaseLock();
7084
+ controller.close();
7085
+ return;
7086
+ }
7087
+ try {
7088
+ if (part?.type === "text-delta") {
7089
+ text += part.delta ?? part.textDelta ?? "";
7090
+ } else if (part?.type === "tool-call") {
7091
+ toolCalls.push({
7092
+ toolCallId: part.toolCallId,
7093
+ toolName: part.toolName,
7094
+ input: part.input ?? part.args
7095
+ });
7096
+ } else if (part?.type === "error") {
7097
+ streamFailure = { error: part.error };
7098
+ } else if (part?.type === "finish") {
7099
+ usage = part.usage;
7100
+ finishReason = part.finishReason;
7101
+ }
7102
+ } catch {
7103
+ }
7104
+ controller.enqueue(part);
7105
+ } catch (error) {
7106
+ if (!completed) {
7107
+ complete({ error });
7108
+ reader.releaseLock();
7109
+ controller.error(error);
7110
+ }
7111
+ }
7112
+ },
7113
+ async cancel(reason) {
7114
+ complete({ error: reason, cancelled: true });
7115
+ try {
7116
+ await reader.cancel(reason);
7117
+ } finally {
7118
+ reader.releaseLock();
6498
7119
  }
6499
- } catch {
6500
7120
  }
6501
- controller.enqueue(part);
6502
7121
  },
6503
- flush() {
6504
- complete();
6505
- }
6506
- });
7122
+ { highWaterMark: 0 }
7123
+ );
6507
7124
  }
6508
7125
  var BitfabVercelAiHandler = class {
6509
7126
  constructor(config) {
@@ -6523,7 +7140,8 @@ var BitfabVercelAiHandler = class {
6523
7140
  {
6524
7141
  type: "llm",
6525
7142
  finalize: (result) => summarizeGenerate(result ?? {}, label),
6526
- surface: "inherit"
7143
+ surface: "inherit",
7144
+ instrumentation: "vercel-ai"
6527
7145
  },
6528
7146
  () => doGenerate()
6529
7147
  );
@@ -6541,11 +7159,24 @@ var BitfabVercelAiHandler = class {
6541
7159
  // The wrapped fn returns immediately with the live stream, so the span
6542
7160
  // output cannot be read from the return value. `finalize` instead
6543
7161
  // awaits the summary the accumulator resolves once the stream drains.
6544
- { type: "llm", finalize: () => summary, surface: "inherit" },
7162
+ {
7163
+ type: "llm",
7164
+ finalize: async () => {
7165
+ const output = await summary;
7166
+ if (output.error !== void 0) {
7167
+ throw new StreamFinalizationError(output.error, output);
7168
+ }
7169
+ return output;
7170
+ },
7171
+ surface: "inherit",
7172
+ instrumentation: "vercel-ai"
7173
+ },
6545
7174
  async () => {
6546
7175
  const result = await doStream();
6547
- const stream = result.stream.pipeThrough(
6548
- accumulateStream(resolveSummary, label)
7176
+ const stream = accumulateStream(
7177
+ result.stream,
7178
+ resolveSummary,
7179
+ label
6549
7180
  );
6550
7181
  return { ...result, stream };
6551
7182
  }
@@ -6559,6 +7190,19 @@ var BitfabVercelAiHandler = class {
6559
7190
  // src/client.ts
6560
7191
  init_warnOnce();
6561
7192
  var activeTraceStates = /* @__PURE__ */ new Map();
7193
+ function rootTraceFunctionKeyOf(payload) {
7194
+ const captured = payload[ROOT_TRACE_FUNCTION_KEY_FIELD];
7195
+ if (typeof captured === "string") {
7196
+ return captured;
7197
+ }
7198
+ const traceId = payload.traceId;
7199
+ const live = typeof traceId === "string" ? activeTraceStates.get(traceId)?.traceFunctionKey : void 0;
7200
+ if (typeof live === "string") {
7201
+ return live;
7202
+ }
7203
+ const own = payload.traceFunctionKey;
7204
+ return typeof own === "string" ? own : void 0;
7205
+ }
6562
7206
  var asyncLocalStorage = null;
6563
7207
  var SPAN_CONTEXT_STORAGE_SYMBOL = /* @__PURE__ */ Symbol.for("bitfab.spanContextStorage");
6564
7208
  var initializeAsyncContext = () => {
@@ -6963,6 +7607,14 @@ var Bitfab = class {
6963
7607
  serviceUrl: this.serviceUrl,
6964
7608
  timeout: this.timeout
6965
7609
  });
7610
+ this.simulationPlan = new SimulationPlan(
7611
+ this.httpClient,
7612
+ config.simulationPlan ?? true
7613
+ );
7614
+ this.httpClient.externalSpanTransform = (payload, submit) => this.applySimulationPlan(payload, submit);
7615
+ this.httpClient.externalTraceTransform = (payload, submit) => this.simulationPlan.sendTrace(payload, submit);
7616
+ this.httpClient.releaseHeldExternalSpans = (timeoutMs) => this.simulationPlan.release(timeoutMs);
7617
+ this.httpClient.stopSimulationPlan = () => this.simulationPlan.stop();
6966
7618
  this.datasets = new DatasetsClient(this.httpClient);
6967
7619
  this.traces = new TracesClient(this.httpClient);
6968
7620
  this.labels = new LabelsClient(this.httpClient);
@@ -7113,6 +7765,7 @@ var Bitfab = class {
7113
7765
  }
7114
7766
  createAutoTraceRoot(traceFunctionKey, name, options, fn) {
7115
7767
  const self = this;
7768
+ self.simulationPlan.refresh();
7116
7769
  const maxDepth = autoTraceLimit(
7117
7770
  options.maxDepth,
7118
7771
  DEFAULT_AUTO_TRACE_MAX_DEPTH
@@ -7128,7 +7781,8 @@ var Bitfab = class {
7128
7781
  name: options.name,
7129
7782
  nameFor: rootNameFor,
7130
7783
  type: options.type ?? "custom",
7131
- surface: "opt-out"
7784
+ surface: "opt-out",
7785
+ instrumentation: "trace"
7132
7786
  };
7133
7787
  const buildTracedRoot = (spanOptions) => this.withSpan(
7134
7788
  traceFunctionKey,
@@ -7139,6 +7793,7 @@ var Bitfab = class {
7139
7793
  traceFunctionKey
7140
7794
  );
7141
7795
  self.refreshAutoTraceCapturePolicy(traceFunctionKey);
7796
+ self.simulationPlan.refresh();
7142
7797
  let spansUsed = 0;
7143
7798
  let truncated = false;
7144
7799
  const warnTruncated = () => {
@@ -7215,6 +7870,7 @@ var Bitfab = class {
7215
7870
  type: nodeConfiguration?.type ?? "function",
7216
7871
  captureWhen: "nested",
7217
7872
  surface: "opt-out",
7873
+ instrumentation: "trace",
7218
7874
  functionId: definition.id,
7219
7875
  captureContent: nodeConfiguration !== void 0 || capturePolicy === void 0 || capturePolicy.has(definition.id),
7220
7876
  autoTraceDefinition: definition,
@@ -7246,6 +7902,7 @@ var Bitfab = class {
7246
7902
  type: "function",
7247
7903
  captureWhen: "nested",
7248
7904
  surface: "opt-out",
7905
+ instrumentation: "trace",
7249
7906
  captureContent: true,
7250
7907
  ...link !== void 0 && { nestedTrace: link }
7251
7908
  };
@@ -7311,38 +7968,36 @@ var Bitfab = class {
7311
7968
  Object.defineProperty(autoTraceRoot, "_bitfabWrappedFn", { value: fn });
7312
7969
  return autoTraceRoot;
7313
7970
  }
7971
+ applySimulationPlan(payload, submit) {
7972
+ this.simulationPlan.send(payload, rootTraceFunctionKeyOf(payload), submit);
7973
+ }
7314
7974
  refreshAutoTraceCapturePolicy(traceFunctionKey) {
7315
- const now = Date.now();
7316
- const state = this.autoTracePolicyRefreshes.get(traceFunctionKey) ?? {
7317
- refreshAfter: 0
7318
- };
7319
- if (state.inFlight || now < state.refreshAfter) {
7320
- return;
7321
- }
7322
- const request = this.httpClient.getAutoTracePolicy(
7323
- traceFunctionKey,
7324
- AUTO_TRACE_PROTOCOL
7325
- ).then((policy) => {
7326
- if (policy.protocol !== AUTO_TRACE_PROTOCOL) {
7327
- state.refreshAfter = Date.now() + AUTO_TRACE_POLICY_RETRY_MS;
7328
- return;
7329
- }
7330
- const functionIds = Array.isArray(policy.functionIds) ? policy.functionIds.filter(
7331
- (id) => typeof id === "string" && id.startsWith(`${AUTO_TRACE_PROTOCOL}:`)
7332
- ).slice(0, DEFAULT_AUTO_TRACE_MAX_SPANS) : [];
7333
- __setBitfabAutoTraceCapturePolicy(
7334
- this,
7335
- traceFunctionKey,
7336
- policy.revision === null ? void 0 : functionIds
7337
- );
7338
- state.refreshAfter = Date.now() + AUTO_TRACE_POLICY_REFRESH_MS;
7339
- }).catch(() => {
7340
- state.refreshAfter = Date.now() + AUTO_TRACE_POLICY_RETRY_MS;
7341
- }).finally(() => {
7342
- state.inFlight = void 0;
7343
- });
7344
- state.inFlight = request;
7975
+ const state = this.autoTracePolicyRefreshes.get(traceFunctionKey) ?? new PolicyRefresh();
7345
7976
  this.autoTracePolicyRefreshes.set(traceFunctionKey, state);
7977
+ state.run(
7978
+ () => this.httpClient.getAutoTracePolicy(
7979
+ traceFunctionKey,
7980
+ AUTO_TRACE_PROTOCOL
7981
+ ),
7982
+ (policy) => {
7983
+ if (policy.protocol !== AUTO_TRACE_PROTOCOL) {
7984
+ state.hold(AUTO_TRACE_POLICY_RETRY_MS);
7985
+ return;
7986
+ }
7987
+ const functionIds = Array.isArray(policy.functionIds) ? policy.functionIds.filter(
7988
+ (id) => typeof id === "string" && id.startsWith(`${AUTO_TRACE_PROTOCOL}:`)
7989
+ ).slice(0, DEFAULT_AUTO_TRACE_MAX_SPANS) : [];
7990
+ __setBitfabAutoTraceCapturePolicy(
7991
+ this,
7992
+ traceFunctionKey,
7993
+ policy.revision === null ? void 0 : functionIds
7994
+ );
7995
+ state.hold(AUTO_TRACE_POLICY_REFRESH_MS);
7996
+ },
7997
+ () => {
7998
+ state.hold(AUTO_TRACE_POLICY_RETRY_MS);
7999
+ }
8000
+ );
7346
8001
  }
7347
8002
  /**
7348
8003
  * Flush and permanently close this client's tracing resources: its pending
@@ -7826,6 +8481,7 @@ var Bitfab = class {
7826
8481
  * @returns A wrapped function with the same signature that creates spans for inputs and outputs
7827
8482
  */
7828
8483
  withSpan(traceFunctionKey, optionsOrFn, maybeFn) {
8484
+ this.simulationPlan.refresh();
7829
8485
  const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
7830
8486
  const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
7831
8487
  const self = this;
@@ -7917,8 +8573,10 @@ var Bitfab = class {
7917
8573
  const testRunId = replayCtxAtStart?.testRunId ?? options.testRunId;
7918
8574
  if (isRootSpan && !activeTraceStates.has(traceId)) {
7919
8575
  const dbSnapshotRef = buildSnapshotRef(self.dbSnapshot, startedAt);
8576
+ self.simulationPlan.refresh();
7920
8577
  activeTraceStates.set(traceId, {
7921
8578
  traceId,
8579
+ traceFunctionKey,
7922
8580
  startedAt,
7923
8581
  contexts: [],
7924
8582
  ...testRunId !== void 0 && { testRunId },
@@ -7933,8 +8591,10 @@ var Bitfab = class {
7933
8591
  registeredTraceId = traceId;
7934
8592
  }
7935
8593
  const functionName = fn.name !== "" ? fn.name : void 0;
8594
+ const rootTraceFunctionKey = activeTraceStates.get(traceId)?.traceFunctionKey ?? traceFunctionKey;
7936
8595
  const baseSpanParams = {
7937
8596
  traceFunctionKey,
8597
+ rootTraceFunctionKey,
7938
8598
  functionName,
7939
8599
  spanName: options.name ?? options.nameFor?.(this) ?? qualifiedSpanName(this, functionName) ?? traceFunctionKey,
7940
8600
  traceId,
@@ -7943,6 +8603,7 @@ var Bitfab = class {
7943
8603
  inputs,
7944
8604
  startedAt,
7945
8605
  spanType: options.type ?? "custom",
8606
+ instrumentation: options.instrumentation ?? "span",
7946
8607
  functionId: options.functionId,
7947
8608
  captureContent: options.captureContent ?? true,
7948
8609
  autoTraceDefinition: options.autoTraceDefinition,
@@ -8010,8 +8671,8 @@ var Bitfab = class {
8010
8671
  void self.httpClient.trackDeferred(
8011
8672
  Promise.resolve().then(() => options.finalize(result)).then((output) => sendSpan({ result: output })).catch(
8012
8673
  (error) => sendSpan({
8013
- result: void 0,
8014
- error: error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
8674
+ result: error instanceof StreamFinalizationError ? error.output : void 0,
8675
+ error: error instanceof StreamFinalizationError ? error.message : error instanceof Error ? `finalize failed: ${error.message}` : `finalize failed: ${String(error)}`
8015
8676
  })
8016
8677
  )
8017
8678
  );
@@ -8358,6 +9019,7 @@ var Bitfab = class {
8358
9019
  * @returns A BitfabFunction instance for wrapping functions
8359
9020
  */
8360
9021
  getFunction(traceFunctionKey) {
9022
+ this.simulationPlan.refresh();
8361
9023
  return new BitfabFunction(this, traceFunctionKey);
8362
9024
  }
8363
9025
  /**
@@ -8445,6 +9107,7 @@ var Bitfab = class {
8445
9107
  trace_id: params.traceId,
8446
9108
  started_at: params.startedAt,
8447
9109
  ended_at: params.endedAt,
9110
+ span_origin: makeSpanOrigin(params.instrumentation),
8448
9111
  span_data: {
8449
9112
  name: params.spanName,
8450
9113
  type: params.spanType,
@@ -8505,6 +9168,9 @@ var Bitfab = class {
8505
9168
  source: "typescript-sdk-function",
8506
9169
  sourceTraceId: params.traceId,
8507
9170
  traceFunctionKey: params.traceFunctionKey,
9171
+ ...params.rootTraceFunctionKey !== void 0 && {
9172
+ [ROOT_TRACE_FUNCTION_KEY_FIELD]: params.rootTraceFunctionKey
9173
+ },
8508
9174
  rawSpan: externalSpan,
8509
9175
  ...params.testRunId && { testRunId: params.testRunId },
8510
9176
  ...params.mocked && { mocked: true },
@@ -8573,6 +9239,7 @@ var Bitfab = class {
8573
9239
  }
8574
9240
  activeTraceStates.set(traceId, {
8575
9241
  traceId,
9242
+ traceFunctionKey,
8576
9243
  startedAt,
8577
9244
  contexts: [],
8578
9245
  ingestionType: "seeded",
@@ -8582,6 +9249,7 @@ var Bitfab = class {
8582
9249
  try {
8583
9250
  this.sendWrapperSpan({
8584
9251
  traceFunctionKey,
9252
+ rootTraceFunctionKey: traceFunctionKey,
8585
9253
  spanName: options.spanName ?? traceFunctionKey,
8586
9254
  traceId,
8587
9255
  spanId: randomUuid(),
@@ -8591,6 +9259,7 @@ var Bitfab = class {
8591
9259
  startedAt,
8592
9260
  endedAt: startedAt,
8593
9261
  spanType: options.spanType ?? "agent",
9262
+ instrumentation: "span",
8594
9263
  captureContent: true
8595
9264
  });
8596
9265
  this.sendTraceCompletion({
@@ -8636,6 +9305,7 @@ var Bitfab = class {
8636
9305
  }
8637
9306
  activeTraceStates.set(traceId, {
8638
9307
  traceId,
9308
+ traceFunctionKey,
8639
9309
  startedAt: nowIsoTimestamp(),
8640
9310
  contexts: [],
8641
9311
  ingestionType: "seeded",