@autter/runtime-node 1.3.3 → 1.4.0

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
@@ -34,18 +34,23 @@ __export(index_exports, {
34
34
  captureMessage: () => captureMessage,
35
35
  createBrowserRelayFetchHandler: () => createBrowserRelayFetchHandler,
36
36
  createBrowserRelayHandler: () => createBrowserRelayHandler,
37
+ createRuntimeLogger: () => createRuntimeLogger,
37
38
  emitLlmSelftestTrace: () => emitLlmSelftestTrace,
39
+ flushRuntimeLogs: () => flushRuntimeLogs,
38
40
  initAutterServer: () => initAutterServer,
39
41
  installAutterAutoFlush: () => installAutterAutoFlush,
40
42
  instrumentLlmClient: () => instrumentLlmClient,
41
43
  makeSafeCapture: () => makeSafeCapture,
42
44
  redactAttributes: () => redactAttributes,
43
45
  reportOutcome: () => reportOutcome,
46
+ runtimeLogStats: () => runtimeLogStats,
47
+ runtimeLogger: () => runtimeLogger,
44
48
  sanitizeBrowserPayload: () => sanitizeBrowserPayload,
45
49
  startCaughtExceptionSampler: () => startCaughtExceptionSampler,
46
50
  trackLlmCall: () => trackLlmCall,
47
51
  withLlmCall: () => withLlmCall,
48
- withProcessSpan: () => withProcessSpan
52
+ withProcessSpan: () => withProcessSpan,
53
+ withRuntimeOperation: () => withRuntimeOperation
49
54
  });
50
55
  module.exports = __toCommonJS(index_exports);
51
56
 
@@ -68,8 +73,8 @@ var IpWindow = class {
68
73
  return entry.count <= this.limitPerMinute;
69
74
  }
70
75
  };
71
- function firstForwardedFor(value) {
72
- const raw = Array.isArray(value) ? value[0] : value;
76
+ function firstForwardedFor(value2) {
77
+ const raw = Array.isArray(value2) ? value2[0] : value2;
73
78
  return raw ? (raw.split(",")[0] ?? "").trim() : "";
74
79
  }
75
80
  var DEFAULT_ENDPOINT = "https://otlp.autter.dev";
@@ -83,6 +88,7 @@ var EVENT_TYPES = /* @__PURE__ */ new Set([
83
88
  "track_event",
84
89
  "outcome",
85
90
  "request_failure",
91
+ "csp_violation",
86
92
  "timing"
87
93
  ]);
88
94
  var SEVERITIES = /* @__PURE__ */ new Set(["fatal", "error", "warning", "info"]);
@@ -254,8 +260,8 @@ function createBrowserRelayHandler(opts) {
254
260
 
255
261
  // src/server.ts
256
262
  var import_node_http = require("http");
257
- var import_node_crypto = require("crypto");
258
- var import_api2 = require("@opentelemetry/api");
263
+ var import_node_crypto2 = require("crypto");
264
+ var import_api3 = require("@opentelemetry/api");
259
265
  var import_core2 = require("@opentelemetry/core");
260
266
  var import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
261
267
  var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
@@ -341,13 +347,13 @@ function installAutterAutoFlush(options = {}) {
341
347
  const signals = options.signals ?? DEFAULT_SIGNALS;
342
348
  const flushOnUncaughtException = options.flushOnUncaughtException !== false;
343
349
  let disposed = false;
344
- let inFlight = null;
350
+ let inFlight2 = null;
345
351
  const doFlush = async () => {
346
352
  const targets = options.targets ?? activeFlushTargets();
347
353
  if (targets.length === 0) return true;
348
- let timer;
354
+ let timer2;
349
355
  const timedOut = new Promise((resolve) => {
350
- timer = setTimeout(() => resolve(false), timeoutMs);
356
+ timer2 = setTimeout(() => resolve(false), timeoutMs);
351
357
  });
352
358
  const drained = Promise.allSettled(
353
359
  targets.map(
@@ -360,7 +366,7 @@ function installAutterAutoFlush(options = {}) {
360
366
  try {
361
367
  ok = await Promise.race([drained, timedOut]);
362
368
  } finally {
363
- clearTimeout(timer);
369
+ clearTimeout(timer2);
364
370
  }
365
371
  if (ok) {
366
372
  telemetryStats.markAllFlushed();
@@ -376,13 +382,13 @@ function installAutterAutoFlush(options = {}) {
376
382
  };
377
383
  const flush = (reason = "exit") => {
378
384
  if (disposed) return Promise.resolve(false);
379
- if (!inFlight) {
380
- inFlight = doFlush().finally(() => {
381
- inFlight = null;
385
+ if (!inFlight2) {
386
+ inFlight2 = doFlush().finally(() => {
387
+ inFlight2 = null;
382
388
  });
383
389
  }
384
390
  if (isDebugEnabled()) debugLog(`auto-flush requested on ${reason}`);
385
- return inFlight;
391
+ return inFlight2;
386
392
  };
387
393
  const signalCounts = /* @__PURE__ */ new Map();
388
394
  let handlingUncaughtException = false;
@@ -521,8 +527,8 @@ function compile(options) {
521
527
  scrubEmailValues: options?.scrubEmailValues !== false
522
528
  };
523
529
  }
524
- function redactString(value, r) {
525
- let out = value.replace(r.privateKeyBlock, r.mask);
530
+ function redactString(value2, r) {
531
+ let out = value2.replace(r.privateKeyBlock, r.mask);
526
532
  for (const re of r.valuePatterns) {
527
533
  out = out.replace(re, r.mask);
528
534
  }
@@ -540,40 +546,40 @@ function canTraverse(depth, state) {
540
546
  state.remainingWork -= 1;
541
547
  return true;
542
548
  }
543
- function redactValue(value, r, state, depth) {
544
- if (typeof value === "string") return redactString(value, r);
549
+ function redactValue(value2, r, state, depth) {
550
+ if (typeof value2 === "string") return redactString(value2, r);
545
551
  let isArray = false;
546
552
  try {
547
- isArray = Array.isArray(value);
553
+ isArray = Array.isArray(value2);
548
554
  } catch {
549
555
  return r.mask;
550
556
  }
551
557
  if (isArray) {
552
- return redactArray(value, r, state, depth);
558
+ return redactArray(value2, r, state, depth);
553
559
  }
554
- if (typeof value === "object" && value !== null) {
555
- return redactObject(value, r, state, depth);
560
+ if (typeof value2 === "object" && value2 !== null) {
561
+ return redactObject(value2, r, state, depth);
556
562
  }
557
- return value;
563
+ return value2;
558
564
  }
559
- function redactArray(value, r, state, depth) {
560
- const existing = state.ancestors.get(value);
565
+ function redactArray(value2, r, state, depth) {
566
+ const existing = state.ancestors.get(value2);
561
567
  if (existing) return r.mask;
562
568
  if (!canTraverse(depth, state)) return r.mask;
563
569
  const out = [];
564
- state.ancestors.set(value, out);
570
+ state.ancestors.set(value2, out);
565
571
  let length;
566
572
  try {
567
- length = value.length;
573
+ length = value2.length;
568
574
  } catch {
569
- state.ancestors.delete(value);
575
+ state.ancestors.delete(value2);
570
576
  return r.mask;
571
577
  }
572
578
  const limit = Math.min(length, MAX_COLLECTION_ENTRIES);
573
579
  for (let i = 0; i < limit; i += 1) {
574
580
  let item;
575
581
  try {
576
- item = value[i];
582
+ item = value2[i];
577
583
  } catch {
578
584
  out.push(r.mask);
579
585
  continue;
@@ -583,21 +589,21 @@ function redactArray(value, r, state, depth) {
583
589
  if (length > limit) {
584
590
  out.push(r.mask);
585
591
  }
586
- state.ancestors.delete(value);
592
+ state.ancestors.delete(value2);
587
593
  return out;
588
594
  }
589
- function redactObject(value, r, state, depth) {
590
- const existing = state.ancestors.get(value);
595
+ function redactObject(value2, r, state, depth) {
596
+ const existing = state.ancestors.get(value2);
591
597
  if (existing) return r.mask;
592
598
  if (!canTraverse(depth, state)) return r.mask;
593
599
  const out = {};
594
- state.ancestors.set(value, out);
600
+ state.ancestors.set(value2, out);
595
601
  let count = 0;
596
602
  let truncated = false;
597
603
  try {
598
- for (const key in value) {
604
+ for (const key in value2) {
599
605
  if (!Object.prototype.propertyIsEnumerable.call(
600
- value,
606
+ value2,
601
607
  key
602
608
  )) {
603
609
  continue;
@@ -608,7 +614,7 @@ function redactObject(value, r, state, depth) {
608
614
  }
609
615
  let nestedValue;
610
616
  try {
611
- nestedValue = value[key];
617
+ nestedValue = value2[key];
612
618
  } catch {
613
619
  out[key] = r.mask;
614
620
  count += 1;
@@ -628,7 +634,7 @@ function redactObject(value, r, state, depth) {
628
634
  if (truncated) {
629
635
  out.__redaction_truncated__ = r.mask;
630
636
  }
631
- state.ancestors.delete(value);
637
+ state.ancestors.delete(value2);
632
638
  return out;
633
639
  }
634
640
  var USAGE_TOKEN_KEYS = /* @__PURE__ */ new Set([
@@ -645,10 +651,10 @@ var USAGE_TOKEN_KEYS = /* @__PURE__ */ new Set([
645
651
  "gen_ai.usage.total_tokens",
646
652
  "gen_ai.usage.token_count"
647
653
  ]);
648
- function isSensitiveKey(key, value, r) {
654
+ function isSensitiveKey(key, value2, r) {
649
655
  const lowered = key.toLowerCase();
650
656
  if (USAGE_TOKEN_KEYS.has(lowered)) {
651
- return !(typeof value === "number" && Number.isFinite(value) && value >= 0);
657
+ return !(typeof value2 === "number" && Number.isFinite(value2) && value2 >= 0);
652
658
  }
653
659
  return r.keyPatterns.some((re) => re.test(lowered));
654
660
  }
@@ -677,9 +683,9 @@ function redactWith(attributes, r) {
677
683
  truncated = true;
678
684
  break;
679
685
  }
680
- let value;
686
+ let value2;
681
687
  try {
682
- value = attributes[key];
688
+ value2 = attributes[key];
683
689
  } catch {
684
690
  out[key] = r.mask;
685
691
  count += 1;
@@ -688,8 +694,8 @@ function redactWith(attributes, r) {
688
694
  }
689
695
  count += 1;
690
696
  state.remainingWork -= 1;
691
- if (value === void 0) continue;
692
- out[key] = isSensitiveKey(key, value, r) ? r.mask : redactValue(value, r, state, 0);
697
+ if (value2 === void 0) continue;
698
+ out[key] = isSensitiveKey(key, value2, r) ? r.mask : redactValue(value2, r, state, 0);
693
699
  }
694
700
  } catch {
695
701
  truncated = true;
@@ -752,6 +758,390 @@ function startMemoryMetrics() {
752
758
  };
753
759
  }
754
760
 
761
+ // src/logger.ts
762
+ var import_node_async_hooks = require("async_hooks");
763
+ var import_node_crypto = require("crypto");
764
+ var import_api2 = require("@opentelemetry/api");
765
+ var local = new import_node_async_hooks.AsyncLocalStorage();
766
+ var config = null;
767
+ var queue = [];
768
+ var queueBytes = 0;
769
+ var inFlightBytes = 0;
770
+ var timer;
771
+ var flushing = null;
772
+ var dropped = 0;
773
+ var inFlight = 0;
774
+ var stopping = false;
775
+ var failureStreak = 0;
776
+ var severity = { debug: 5, info: 9, warning: 13, error: 17 };
777
+ function safe(attributes = {}) {
778
+ const redacted = config?.redact(attributes) ?? redactAttributes(attributes);
779
+ let budget = 512;
780
+ let characters = 16384;
781
+ let truncated = false;
782
+ const visit = (input, depth) => {
783
+ if (--budget < 0 || depth > 6) {
784
+ truncated = true;
785
+ return "[truncated]";
786
+ }
787
+ if (typeof input === "string") {
788
+ const text = input.replace(/(https?:\/\/[^\s?#]+)[?#][^\s]*/gi, "$1");
789
+ const kept = text.slice(0, Math.max(0, characters));
790
+ truncated ||= kept.length < text.length;
791
+ characters -= kept.length;
792
+ return kept;
793
+ }
794
+ if (typeof input === "number")
795
+ return Number.isFinite(input) ? input : void 0;
796
+ if (typeof input === "boolean" || input === null) return input;
797
+ if (Array.isArray(input)) {
798
+ truncated ||= input.length > 64;
799
+ return input.slice(0, 64).map((item) => visit(item, depth + 1));
800
+ }
801
+ if (input && typeof input === "object") {
802
+ const entries = Object.entries(input);
803
+ if (depth === 0) {
804
+ const priority = (key) => /^autter\.(?:operation|event)\./.test(key) ? 2 : /^exception\./.test(key) ? 1 : 0;
805
+ entries.sort(([a], [b]) => priority(b) - priority(a));
806
+ }
807
+ truncated ||= entries.length > 100;
808
+ return Object.fromEntries(
809
+ entries.slice(0, 100).filter(([key]) => !/^(?:__proto__|constructor|prototype)$/.test(key)).map(([key, item]) => [key.slice(0, 200), visit(item, depth + 1)])
810
+ );
811
+ }
812
+ return void 0;
813
+ };
814
+ const result = visit(redacted, 0);
815
+ if (truncated) result["autter.context.truncated"] = true;
816
+ return result;
817
+ }
818
+ function operationAttributes(state) {
819
+ return state ? {
820
+ "autter.operation.id": state.id,
821
+ "autter.operation.name": state.name,
822
+ ...state.parentId ? { "autter.operation.parent_id": state.parentId } : {},
823
+ ...state.attributes
824
+ } : {};
825
+ }
826
+ function userAttributes(attributes = {}) {
827
+ return Object.fromEntries(
828
+ Object.entries(safe(attributes)).filter(
829
+ ([key]) => !/^autter\.(?:operation|event)\./.test(key)
830
+ )
831
+ );
832
+ }
833
+ function mergeContext(current, next = {}) {
834
+ const merge = (left, right, depth) => {
835
+ const result = { ...left };
836
+ for (const [key, value2] of Object.entries(right)) {
837
+ const prior = result[key];
838
+ result[key] = depth < 6 && value2 && typeof value2 === "object" && !Array.isArray(value2) && prior && typeof prior === "object" && !Array.isArray(prior) ? merge(prior, value2, depth + 1) : value2;
839
+ }
840
+ return result;
841
+ };
842
+ return userAttributes(merge(current, userAttributes(next), 0));
843
+ }
844
+ function traceAttributes(state) {
845
+ return Object.fromEntries(
846
+ Object.entries(operationAttributes(state)).filter(([, item]) => item !== null && item !== void 0).map(([key, item]) => [
847
+ key,
848
+ typeof item === "object" ? JSON.stringify(item) : item
849
+ ])
850
+ );
851
+ }
852
+ function scheduleFlush() {
853
+ if (!timer && config && !stopping && queue.length) {
854
+ timer = setTimeout(
855
+ () => {
856
+ timer = void 0;
857
+ void flushRuntimeLogs().catch(() => {
858
+ });
859
+ },
860
+ Math.min(3e4, 2e3 * 2 ** Math.min(failureStreak, 4))
861
+ );
862
+ timer.unref();
863
+ }
864
+ }
865
+ function value(input) {
866
+ if (typeof input === "number") return { doubleValue: input };
867
+ if (typeof input === "boolean") return { boolValue: input };
868
+ if (Array.isArray(input))
869
+ return { arrayValue: { values: input.slice(0, 64).map(value) } };
870
+ if (input && typeof input === "object")
871
+ return {
872
+ kvlistValue: {
873
+ values: Object.entries(input).slice(0, 128).map(([key, item]) => ({ key, value: value(item) }))
874
+ }
875
+ };
876
+ return { stringValue: String(input ?? "").slice(0, 32e3) };
877
+ }
878
+ function emit(level, message, attributes, extra = {}) {
879
+ const state = local.getStore();
880
+ if (state?.sealed) return;
881
+ if (extra["autter.event.type"] !== "operation" && severity[level] < severity[config?.options?.minLevel ?? "debug"])
882
+ return;
883
+ const span = import_api2.trace.getActiveSpan()?.spanContext();
884
+ const user = mergeContext(state?.attributes ?? {}, attributes);
885
+ const exceptions = Object.fromEntries(
886
+ Object.entries(user).filter(([key]) => key.startsWith("exception."))
887
+ );
888
+ const attrs = safe({
889
+ "autter.event.id": (0, import_node_crypto.randomUUID)(),
890
+ ...extra,
891
+ ...exceptions,
892
+ ...operationAttributes(state),
893
+ ...user
894
+ });
895
+ const record = {
896
+ timeUnixNano: String(BigInt(Date.now()) * 1000000n),
897
+ severityNumber: severity[level],
898
+ severityText: level.toUpperCase(),
899
+ body: value(String(safe({ message }).message ?? "").slice(0, 4e3)),
900
+ attributes: Object.entries(attrs).map(([key, item]) => ({
901
+ key,
902
+ value: value(item)
903
+ })),
904
+ ...span && /^[a-f0-9]{32}$/.test(span.traceId) && !/^0+$/.test(span.traceId) ? { traceId: span.traceId, spanId: span.spanId } : {}
905
+ };
906
+ if (config?.options?.console !== false)
907
+ console.log(
908
+ JSON.stringify({ ...attrs, level, message: record.body.stringValue })
909
+ );
910
+ if (!config) return;
911
+ if (stopping) {
912
+ dropped++;
913
+ return;
914
+ }
915
+ const bytes = Buffer.byteLength(JSON.stringify(record));
916
+ if (bytes > 256 * 1024 || queue.length + inFlight >= 1e3 || queueBytes + inFlightBytes + bytes > 4 * 1024 * 1024) {
917
+ dropped++;
918
+ console.warn(
919
+ "[autter-runtime] log buffer or record limit reached; record dropped"
920
+ );
921
+ return;
922
+ }
923
+ queue.push({ record, bytes });
924
+ queueBytes += bytes;
925
+ scheduleFlush();
926
+ }
927
+ function configureRuntimeLogger(next) {
928
+ config = next;
929
+ stopping = false;
930
+ failureStreak = 0;
931
+ }
932
+ function runtimeLogStats() {
933
+ return { buffered: queue.length + inFlight, dropped };
934
+ }
935
+ async function flushRuntimeLogs() {
936
+ if (flushing) return flushing;
937
+ if (!config || !queue.length) return;
938
+ if (timer) {
939
+ clearTimeout(timer);
940
+ timer = void 0;
941
+ }
942
+ const activeConfig = config;
943
+ const deadline = Date.now() + 1e4;
944
+ flushing = (async () => {
945
+ const count = queue.length;
946
+ for (let remaining = count; remaining > 0; ) {
947
+ const batch = [];
948
+ inFlightBytes = 0;
949
+ while (batch.length < Math.min(50, remaining) && queue.length) {
950
+ const next = queue[0];
951
+ if (batch.length && inFlightBytes + next.bytes > 512 * 1024) break;
952
+ batch.push(queue.shift());
953
+ inFlightBytes += next.bytes;
954
+ queueBytes -= next.bytes;
955
+ }
956
+ remaining -= batch.length;
957
+ inFlight = batch.length;
958
+ const body = JSON.stringify({
959
+ resourceLogs: [
960
+ {
961
+ resource: {
962
+ attributes: Object.entries({
963
+ "service.name": activeConfig.service,
964
+ "deployment.environment.name": activeConfig.environment,
965
+ ...activeConfig.release ? { "service.version": activeConfig.release } : {}
966
+ }).map(([key, item]) => ({ key, value: value(item) }))
967
+ },
968
+ scopeLogs: [
969
+ {
970
+ scope: { name: "autter-runtime" },
971
+ logRecords: batch.map((entry) => entry.record)
972
+ }
973
+ ]
974
+ }
975
+ ]
976
+ });
977
+ let failure;
978
+ for (let attempt = 0; attempt < 3; attempt++) {
979
+ try {
980
+ if (Date.now() >= deadline)
981
+ throw new Error("Runtime log flush exceeded 10 seconds");
982
+ const response = await fetch(`${activeConfig.endpoint}/v1/logs`, {
983
+ method: "POST",
984
+ headers: {
985
+ "content-type": "application/json",
986
+ authorization: `Bearer ${activeConfig.apiKey}`
987
+ },
988
+ body,
989
+ signal: AbortSignal.timeout(
990
+ Math.max(1, Math.min(3e3, deadline - Date.now()))
991
+ )
992
+ });
993
+ if (!response.ok)
994
+ throw new Error(`Runtime log export failed (${response.status})`);
995
+ failure = void 0;
996
+ break;
997
+ } catch (error) {
998
+ failure = error;
999
+ }
1000
+ }
1001
+ inFlight = 0;
1002
+ if (failure) {
1003
+ failureStreak++;
1004
+ queue = [...batch, ...queue];
1005
+ queueBytes += inFlightBytes;
1006
+ inFlightBytes = 0;
1007
+ throw failure;
1008
+ }
1009
+ inFlightBytes = 0;
1010
+ failureStreak = 0;
1011
+ }
1012
+ })().finally(() => {
1013
+ flushing = null;
1014
+ scheduleFlush();
1015
+ });
1016
+ return flushing;
1017
+ }
1018
+ async function shutdownRuntimeLogger() {
1019
+ stopping = true;
1020
+ try {
1021
+ await flushRuntimeLogs();
1022
+ } finally {
1023
+ if (queue.length) {
1024
+ dropped += queue.length;
1025
+ console.warn(
1026
+ `[autter-runtime] ${queue.length} log records could not be delivered before shutdown`
1027
+ );
1028
+ }
1029
+ queue = [];
1030
+ queueBytes = 0;
1031
+ config = null;
1032
+ if (timer) clearTimeout(timer);
1033
+ timer = void 0;
1034
+ }
1035
+ }
1036
+ function createRuntimeLogger(attributes = {}) {
1037
+ const attrs = userAttributes(attributes);
1038
+ return {
1039
+ debug: (message, extra) => emit("debug", message, mergeContext(attrs, extra)),
1040
+ info: (message, extra) => emit("info", message, mergeContext(attrs, extra)),
1041
+ warn: (message, extra) => emit("warning", message, mergeContext(attrs, extra)),
1042
+ error: (error, extra) => emit("error", error instanceof Error ? error.message : String(error), {
1043
+ ...attrs,
1044
+ ...extra,
1045
+ ...error instanceof Error ? {
1046
+ "exception.type": error.name,
1047
+ "exception.stacktrace": error.stack ?? ""
1048
+ } : {}
1049
+ })
1050
+ };
1051
+ }
1052
+ var runtimeLogger = createRuntimeLogger();
1053
+ function withRuntimeOperation(name, fn, attributes = {}) {
1054
+ const parent = local.getStore();
1055
+ const state = {
1056
+ id: (0, import_node_crypto.randomUUID)(),
1057
+ name: String(safe({ name }).name).slice(0, 200),
1058
+ parentId: parent?.sealed ? void 0 : parent?.id,
1059
+ attributes: mergeContext(
1060
+ parent?.sealed ? {} : parent?.attributes ?? {},
1061
+ attributes
1062
+ ),
1063
+ startedAt: Date.now(),
1064
+ outcome: "succeeded",
1065
+ steps: []
1066
+ };
1067
+ const operation = {
1068
+ id: state.id,
1069
+ setContext: (attrs) => {
1070
+ if (!state.sealed) {
1071
+ state.attributes = mergeContext(state.attributes, attrs);
1072
+ import_api2.trace.getActiveSpan()?.setAttributes(traceAttributes(state));
1073
+ }
1074
+ },
1075
+ outcome: (status, message) => {
1076
+ if (state.sealed) return;
1077
+ state.outcome = status;
1078
+ state.message = message;
1079
+ if (status === "failed")
1080
+ state.reportingStack = new Error("Operation outcome reported here").stack ?? "";
1081
+ },
1082
+ step: async (stepName, stepFn) => {
1083
+ const started = Date.now();
1084
+ let status = "succeeded";
1085
+ try {
1086
+ return await stepFn();
1087
+ } catch (error) {
1088
+ status = "failed";
1089
+ throw error;
1090
+ } finally {
1091
+ if (!state.sealed && state.steps.length < 64)
1092
+ state.steps.push({
1093
+ name: String(safe({ name: stepName }).name).slice(0, 200),
1094
+ status,
1095
+ durationMs: Date.now() - started
1096
+ });
1097
+ }
1098
+ }
1099
+ };
1100
+ const run = () => local.run(state, async () => {
1101
+ let thrown;
1102
+ try {
1103
+ return await fn(operation);
1104
+ } catch (error) {
1105
+ state.outcome = "failed";
1106
+ thrown = error;
1107
+ throw error;
1108
+ } finally {
1109
+ import_api2.trace.getActiveSpan()?.setAttributes({
1110
+ ...traceAttributes(state),
1111
+ "autter.operation.outcome": state.outcome
1112
+ });
1113
+ if (state.outcome === "failed" && !thrown)
1114
+ config?.reportOutcome(
1115
+ state.name,
1116
+ state.message ?? "Operation failed",
1117
+ {
1118
+ ...traceAttributes(state),
1119
+ "autter.outcome.stack": state.reportingStack ?? ""
1120
+ }
1121
+ );
1122
+ emit(
1123
+ state.outcome === "failed" ? "error" : "info",
1124
+ state.message ?? `${state.name}: ${state.outcome}`,
1125
+ {
1126
+ ...operationAttributes(state),
1127
+ ...thrown instanceof Error ? {
1128
+ "exception.type": thrown.name,
1129
+ "exception.stacktrace": thrown.stack ?? ""
1130
+ } : {}
1131
+ },
1132
+ {
1133
+ "autter.event.type": "operation",
1134
+ "autter.operation.outcome": state.outcome,
1135
+ "autter.operation.duration_ms": Date.now() - state.startedAt,
1136
+ "autter.operation.steps": state.steps
1137
+ }
1138
+ );
1139
+ state.sealed = true;
1140
+ }
1141
+ });
1142
+ return config ? config.run(state.name, run, traceAttributes(state)) : run();
1143
+ }
1144
+
755
1145
  // src/server.ts
756
1146
  var LLM_MARKER_ATTRIBUTES = [
757
1147
  "gen_ai.operation.name",
@@ -814,14 +1204,14 @@ var RETENTION_MAX_BUFFERED_SPANS = 5e3;
814
1204
  var RETENTION_SWEEP_INTERVAL_MS = 1e4;
815
1205
  var RETENTION_TRACE_TTL_MS = 3e4;
816
1206
  function spanIndicatesError(span) {
817
- if (span.status.code === import_api2.SpanStatusCode.ERROR) return true;
1207
+ if (span.status.code === import_api3.SpanStatusCode.ERROR) return true;
818
1208
  return span.events.some((event) => event.name === "exception");
819
1209
  }
820
1210
  var TAIL_RETAINED_ATTR = "autter.tail_retained";
821
1211
  function withSampledFlag(span) {
822
1212
  const spanContext = {
823
1213
  ...span.spanContext(),
824
- traceFlags: span.spanContext().traceFlags | import_api2.TraceFlags.SAMPLED
1214
+ traceFlags: span.spanContext().traceFlags | import_api3.TraceFlags.SAMPLED
825
1215
  };
826
1216
  return {
827
1217
  name: span.name,
@@ -861,11 +1251,11 @@ var ErrorTraceRetentionProcessor = class {
861
1251
  }
862
1252
  onEnd(span) {
863
1253
  const ctx = span.spanContext();
864
- if ((ctx.traceFlags & import_api2.TraceFlags.SAMPLED) !== 0) return;
1254
+ if ((ctx.traceFlags & import_api3.TraceFlags.SAMPLED) !== 0) return;
865
1255
  const entry = this.entryFor(ctx.traceId);
866
1256
  if (entry.retained) {
867
1257
  this.forward(span);
868
- } else if (this.retainErrors && spanIndicatesError(span) || this.slowThresholdMs > 0 && span.kind === import_api2.SpanKind.SERVER && span.duration[0] * 1e3 + span.duration[1] / 1e6 >= this.slowThresholdMs) {
1258
+ } else if (this.retainErrors && spanIndicatesError(span) || this.slowThresholdMs > 0 && span.kind === import_api3.SpanKind.SERVER && span.duration[0] * 1e3 + span.duration[1] / 1e6 >= this.slowThresholdMs) {
869
1259
  entry.retained = true;
870
1260
  this.flush(entry);
871
1261
  this.forward(span);
@@ -880,8 +1270,8 @@ var ErrorTraceRetentionProcessor = class {
880
1270
  }
881
1271
  /** Promote the active trace, if any — called on captured errors. */
882
1272
  retainActiveTrace() {
883
- const active2 = import_api2.trace.getActiveSpan()?.spanContext();
884
- if (!active2 || (active2.traceFlags & import_api2.TraceFlags.SAMPLED) !== 0) return;
1273
+ const active2 = import_api3.trace.getActiveSpan()?.spanContext();
1274
+ if (!active2 || (active2.traceFlags & import_api3.TraceFlags.SAMPLED) !== 0) return;
885
1275
  const entry = this.entryFor(active2.traceId);
886
1276
  if (entry.retained) return;
887
1277
  entry.retained = true;
@@ -970,7 +1360,7 @@ function usageAttributes(usage) {
970
1360
  }
971
1361
  async function runLlmSpan(tracer, info, fn) {
972
1362
  const span = tracer.startSpan(`${info.operation ?? "chat"} ${info.model}`, {
973
- kind: import_api2.SpanKind.CLIENT,
1363
+ kind: import_api3.SpanKind.CLIENT,
974
1364
  attributes: llmBaseAttributes(info)
975
1365
  });
976
1366
  const setResponseModel = (model) => span.setAttribute("gen_ai.response.model", model);
@@ -982,11 +1372,11 @@ async function runLlmSpan(tracer, info, fn) {
982
1372
  setAttributes: (attributes) => span.setAttributes(attributes)
983
1373
  };
984
1374
  try {
985
- const result = await import_api2.context.with(
986
- import_api2.trace.setSpan(import_api2.context.active(), span),
1375
+ const result = await import_api3.context.with(
1376
+ import_api3.trace.setSpan(import_api3.context.active(), span),
987
1377
  () => fn(handle)
988
1378
  );
989
- span.setStatus({ code: import_api2.SpanStatusCode.OK });
1379
+ span.setStatus({ code: import_api3.SpanStatusCode.OK });
990
1380
  return result;
991
1381
  } catch (err) {
992
1382
  if (err instanceof Error) {
@@ -994,7 +1384,7 @@ async function runLlmSpan(tracer, info, fn) {
994
1384
  span.setAttribute("error.type", err.name);
995
1385
  }
996
1386
  span.setStatus({
997
- code: import_api2.SpanStatusCode.ERROR,
1387
+ code: import_api3.SpanStatusCode.ERROR,
998
1388
  message: err instanceof Error ? err.message : String(err)
999
1389
  });
1000
1390
  throw err;
@@ -1009,7 +1399,7 @@ function recordLlmCall(tracer, call) {
1009
1399
  const span = tracer.startSpan(
1010
1400
  `${call.operation ?? "chat"} ${call.model}`,
1011
1401
  {
1012
- kind: import_api2.SpanKind.CLIENT,
1402
+ kind: import_api3.SpanKind.CLIENT,
1013
1403
  startTime,
1014
1404
  attributes: { ...llmBaseAttributes(call), ...usageAttributes(call) }
1015
1405
  }
@@ -1020,27 +1410,27 @@ function recordLlmCall(tracer, call) {
1020
1410
  span.setAttribute("error.type", call.error.name);
1021
1411
  }
1022
1412
  span.setStatus({
1023
- code: import_api2.SpanStatusCode.ERROR,
1413
+ code: import_api3.SpanStatusCode.ERROR,
1024
1414
  message: call.error instanceof Error ? call.error.message : String(call.error)
1025
1415
  });
1026
1416
  } else {
1027
- span.setStatus({ code: import_api2.SpanStatusCode.OK });
1417
+ span.setStatus({ code: import_api3.SpanStatusCode.OK });
1028
1418
  }
1029
1419
  span.end(endTime);
1030
1420
  }
1031
1421
  async function runWithSpan(tracer, name, fn, attributes) {
1032
1422
  const span = tracer.startSpan(name, { attributes });
1033
1423
  try {
1034
- const result = await import_api2.context.with(
1035
- import_api2.trace.setSpan(import_api2.context.active(), span),
1424
+ const result = await import_api3.context.with(
1425
+ import_api3.trace.setSpan(import_api3.context.active(), span),
1036
1426
  fn
1037
1427
  );
1038
- span.setStatus({ code: import_api2.SpanStatusCode.OK });
1428
+ span.setStatus({ code: import_api3.SpanStatusCode.OK });
1039
1429
  return result;
1040
1430
  } catch (err) {
1041
1431
  if (err instanceof Error) span.recordException(err);
1042
1432
  span.setStatus({
1043
- code: import_api2.SpanStatusCode.ERROR,
1433
+ code: import_api3.SpanStatusCode.ERROR,
1044
1434
  message: err instanceof Error ? err.message : String(err)
1045
1435
  });
1046
1436
  throw err;
@@ -1058,7 +1448,7 @@ function expressRouteOf(req) {
1058
1448
  }
1059
1449
  function captureExpressRoute(_span, response) {
1060
1450
  if (!(response instanceof import_node_http.ServerResponse)) return;
1061
- const rpcMetadata = (0, import_core2.getRPCMetadata)(import_api2.context.active());
1451
+ const rpcMetadata = (0, import_core2.getRPCMetadata)(import_api3.context.active());
1062
1452
  if (rpcMetadata?.type !== import_core2.RPCType.HTTP) return;
1063
1453
  const setRoute = () => {
1064
1454
  if (rpcMetadata.route) return;
@@ -1084,7 +1474,7 @@ function initAutterServer(options) {
1084
1474
  activeRedactor = makeRedactor(options.redactAttributes ?? true);
1085
1475
  const resource = new import_resources.Resource({
1086
1476
  [import_semantic_conventions.ATTR_SERVICE_NAME]: options.service,
1087
- "service.instance.id": (options.instanceId || process.env.AUTTER_RUNTIME_INSTANCE_ID || (0, import_node_crypto.randomUUID)()).slice(0, 128),
1477
+ "service.instance.id": (options.instanceId || process.env.AUTTER_RUNTIME_INSTANCE_ID || (0, import_node_crypto2.randomUUID)()).slice(0, 128),
1088
1478
  ...options.release ? { [import_semantic_conventions.ATTR_SERVICE_VERSION]: options.release } : {},
1089
1479
  "deployment.environment": environment,
1090
1480
  // Tells the ingester request metrics arrive on the metrics pipe, so
@@ -1171,7 +1561,7 @@ function initAutterServer(options) {
1171
1561
  const isError = error instanceof Error;
1172
1562
  const message = isError ? error.message : String(error);
1173
1563
  const redacted = activeRedactor(attributes);
1174
- const activeSpan = import_api2.trace.getActiveSpan();
1564
+ const activeSpan = import_api3.trace.getActiveSpan();
1175
1565
  const reuseActive = activeSpan?.isRecording() === true;
1176
1566
  const span = reuseActive ? activeSpan : errorTracer.startSpan(isError ? error.name : "Error");
1177
1567
  span.setAttributes({ "autter.severity": "error", ...redacted });
@@ -1196,31 +1586,31 @@ function initAutterServer(options) {
1196
1586
  ...redacted["autter.handled"] === true ? { "autter.handled": true, "autter.sampled": redacted["autter.sampled"] === true } : {}
1197
1587
  });
1198
1588
  }
1199
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message });
1589
+ span.setStatus({ code: import_api3.SpanStatusCode.ERROR, message });
1200
1590
  if (!reuseActive) span.end();
1201
1591
  }
1202
- function captureMessage2(message, severity = "warning", attributes) {
1203
- if (severity === "error" || severity === "fatal") {
1592
+ function captureMessage2(message, severity2 = "warning", attributes) {
1593
+ if (severity2 === "error" || severity2 === "fatal") {
1204
1594
  errorTraceBuffer?.retainActiveTrace();
1205
1595
  }
1206
1596
  telemetryStats.markCaptured();
1207
1597
  const stack = new Error().stack?.split("\n").filter((line, i) => i === 0 || !line.includes("captureMessage")).join("\n");
1208
1598
  const span = errorTracer.startSpan("Message", {
1209
- attributes: { "autter.severity": severity, ...activeRedactor(attributes) }
1599
+ attributes: { "autter.severity": severity2, ...activeRedactor(attributes) }
1210
1600
  });
1211
1601
  span.addEvent("exception", {
1212
1602
  "exception.type": "Message",
1213
1603
  "exception.message": message,
1214
1604
  ...stack ? { "exception.stacktrace": stack } : {},
1215
- "autter.severity": severity
1605
+ "autter.severity": severity2
1216
1606
  });
1217
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message });
1607
+ span.setStatus({ code: import_api3.SpanStatusCode.ERROR, message });
1218
1608
  span.end();
1219
1609
  }
1220
1610
  function reportOutcome2(name, message, attributes) {
1221
1611
  errorTraceBuffer?.retainActiveTrace();
1222
1612
  telemetryStats.markCaptured();
1223
- const activeSpan = import_api2.trace.getActiveSpan();
1613
+ const activeSpan = import_api3.trace.getActiveSpan();
1224
1614
  const reuseActive = activeSpan?.isRecording() === true;
1225
1615
  const span = reuseActive ? activeSpan : errorTracer.startSpan("OutcomeFailure");
1226
1616
  const safeOutcome = activeRedactor({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
@@ -1232,9 +1622,20 @@ function initAutterServer(options) {
1232
1622
  "autter.outcome.name": safeName,
1233
1623
  "autter.outcome.message": safeMessage
1234
1624
  });
1235
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message: safeMessage });
1625
+ span.setStatus({ code: import_api3.SpanStatusCode.ERROR, message: safeMessage });
1236
1626
  if (!reuseActive) span.end();
1237
1627
  }
1628
+ configureRuntimeLogger({
1629
+ endpoint,
1630
+ apiKey: options.apiKey,
1631
+ service: options.service,
1632
+ environment,
1633
+ release: options.release,
1634
+ options: options.logging,
1635
+ redact: activeRedactor,
1636
+ run: (name, fn, attributes) => runWithSpan(processTracer, name, fn, activeRedactor(attributes)),
1637
+ reportOutcome: reportOutcome2
1638
+ });
1238
1639
  if (options.captureGlobalErrors !== false) {
1239
1640
  process.on("uncaughtExceptionMonitor", (err) => {
1240
1641
  captureException2(err, { "autter.unhandled": true });
@@ -1245,6 +1646,7 @@ function initAutterServer(options) {
1245
1646
  const flushTarget = {
1246
1647
  forceFlush: async () => {
1247
1648
  const results = await Promise.allSettled([
1649
+ flushRuntimeLogs(),
1248
1650
  Promise.resolve().then(() => alwaysOnProvider.forceFlush()),
1249
1651
  Promise.resolve().then(() => mainSpanProcessor.forceFlush()),
1250
1652
  ...errorTraceBuffer ? [Promise.resolve().then(() => errorTraceBuffer.forceFlush())] : [],
@@ -1271,6 +1673,7 @@ function initAutterServer(options) {
1271
1673
  withLlmCall: (info, fn) => runLlmSpan(llmTracer, info, fn),
1272
1674
  trackLlmCall: (call) => recordLlmCall(llmTracer, call),
1273
1675
  shutdown: async () => {
1676
+ const logShutdown = await Promise.allSettled([shutdownRuntimeLogger()]);
1274
1677
  stopMemoryMetrics?.();
1275
1678
  active = null;
1276
1679
  activeAlwaysOnProvider = null;
@@ -1279,6 +1682,7 @@ function initAutterServer(options) {
1279
1682
  unregisterFlushTargets();
1280
1683
  telemetryStats.markAllFlushed();
1281
1684
  await Promise.allSettled([alwaysOnProvider.shutdown(), sdk.shutdown()]);
1685
+ if (logShutdown[0]?.status === "rejected") throw logShutdown[0].reason;
1282
1686
  }
1283
1687
  };
1284
1688
  active = server;
@@ -1290,48 +1694,48 @@ function captureException(error, attributes) {
1290
1694
  active.captureException(error, attributes);
1291
1695
  return;
1292
1696
  }
1293
- const span = import_api2.trace.getTracer("autter-errors").startSpan(error instanceof Error ? error.name : "Error", { attributes });
1697
+ const span = import_api3.trace.getTracer("autter-errors").startSpan(error instanceof Error ? error.name : "Error", { attributes });
1294
1698
  if (error instanceof Error) span.recordException(error);
1295
1699
  span.setStatus({
1296
- code: import_api2.SpanStatusCode.ERROR,
1700
+ code: import_api3.SpanStatusCode.ERROR,
1297
1701
  message: error instanceof Error ? error.message : String(error)
1298
1702
  });
1299
1703
  span.end();
1300
1704
  }
1301
1705
  function withProcessSpan(name, fn, attributes) {
1302
1706
  if (active) return active.withProcessSpan(name, fn, attributes);
1303
- return runWithSpan(import_api2.trace.getTracer("autter-processes"), name, fn, attributes);
1707
+ return runWithSpan(import_api3.trace.getTracer("autter-processes"), name, fn, attributes);
1304
1708
  }
1305
1709
  function withLlmCall(info, fn) {
1306
1710
  if (active) return active.withLlmCall(info, fn);
1307
- return runLlmSpan(import_api2.trace.getTracer("autter-llm"), info, fn);
1711
+ return runLlmSpan(import_api3.trace.getTracer("autter-llm"), info, fn);
1308
1712
  }
1309
1713
  function trackLlmCall(call) {
1310
1714
  if (active) {
1311
1715
  active.trackLlmCall(call);
1312
1716
  return;
1313
1717
  }
1314
- recordLlmCall(import_api2.trace.getTracer("autter-llm"), call);
1718
+ recordLlmCall(import_api3.trace.getTracer("autter-llm"), call);
1315
1719
  }
1316
- function captureMessage(message, severity = "warning", attributes) {
1720
+ function captureMessage(message, severity2 = "warning", attributes) {
1317
1721
  if (active) {
1318
- active.captureMessage(message, severity, attributes);
1722
+ active.captureMessage(message, severity2, attributes);
1319
1723
  return;
1320
1724
  }
1321
- const span = import_api2.trace.getTracer("autter-errors").startSpan("Message", {
1322
- attributes: { "autter.severity": severity, ...attributes }
1725
+ const span = import_api3.trace.getTracer("autter-errors").startSpan("Message", {
1726
+ attributes: { "autter.severity": severity2, ...attributes }
1323
1727
  });
1324
1728
  span.addEvent("exception", {
1325
1729
  "exception.type": "Message",
1326
1730
  "exception.message": message,
1327
- "autter.severity": severity
1731
+ "autter.severity": severity2
1328
1732
  });
1329
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message });
1733
+ span.setStatus({ code: import_api3.SpanStatusCode.ERROR, message });
1330
1734
  span.end();
1331
1735
  }
1332
1736
  function reportOutcome(name, message, attributes) {
1333
1737
  if (active) return active.reportOutcome(name, message, attributes);
1334
- const span = import_api2.trace.getTracer("autter-outcomes").startSpan("OutcomeFailure");
1738
+ const span = import_api3.trace.getTracer("autter-outcomes").startSpan("OutcomeFailure");
1335
1739
  const safeOutcome = redactAttributes({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
1336
1740
  const safeName = String(safeOutcome["autter.outcome.name"] ?? "operation");
1337
1741
  const safeMessage = String(safeOutcome["autter.outcome.message"] ?? "failed");
@@ -1341,18 +1745,18 @@ function reportOutcome(name, message, attributes) {
1341
1745
  "autter.outcome.name": safeName,
1342
1746
  "autter.outcome.message": safeMessage
1343
1747
  });
1344
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message: safeMessage });
1748
+ span.setStatus({ code: import_api3.SpanStatusCode.ERROR, message: safeMessage });
1345
1749
  span.end();
1346
1750
  }
1347
1751
  function makeSafeCapture(options) {
1348
1752
  const clean = (attributes) => attributes ? { ...redactAttributes(attributes, options) } : void 0;
1349
1753
  return {
1350
1754
  captureException: (error, attributes) => captureException(error, clean(attributes)),
1351
- captureMessage: (message, severity, attributes) => captureMessage(message, severity ?? "warning", clean(attributes))
1755
+ captureMessage: (message, severity2, attributes) => captureMessage(message, severity2 ?? "warning", clean(attributes))
1352
1756
  };
1353
1757
  }
1354
1758
  function alwaysOnTracer(name) {
1355
- return activeAlwaysOnProvider ? activeAlwaysOnProvider.getTracer(name) : import_api2.trace.getTracer(name);
1759
+ return activeAlwaysOnProvider ? activeAlwaysOnProvider.getTracer(name) : import_api3.trace.getTracer(name);
1356
1760
  }
1357
1761
  function autterLlmTracer() {
1358
1762
  return alwaysOnTracer("autter-llm");
@@ -1372,7 +1776,7 @@ async function emitLlmSelftestTrace() {
1372
1776
  attributes: { "autter.selftest": true }
1373
1777
  },
1374
1778
  (llm) => {
1375
- traceId = import_api2.trace.getActiveSpan()?.spanContext().traceId ?? "";
1779
+ traceId = import_api3.trace.getActiveSpan()?.spanContext().traceId ?? "";
1376
1780
  llm.setUsage({ inputTokens: 1, outputTokens: 1 });
1377
1781
  llm.setCost(0);
1378
1782
  }
@@ -1382,7 +1786,7 @@ async function emitLlmSelftestTrace() {
1382
1786
  }
1383
1787
 
1384
1788
  // src/llm-instrument.ts
1385
- var import_api3 = require("@opentelemetry/api");
1789
+ var import_api4 = require("@opentelemetry/api");
1386
1790
  var CALL_METHODS = /* @__PURE__ */ new Set([
1387
1791
  "create",
1388
1792
  // openai chat/completions/embeddings/responses, anthropic messages
@@ -1440,7 +1844,7 @@ function applyUsage(span, usage) {
1440
1844
  function endWithError(span, err) {
1441
1845
  if (err instanceof Error) span.recordException(err);
1442
1846
  span.setStatus({
1443
- code: import_api3.SpanStatusCode.ERROR,
1847
+ code: import_api4.SpanStatusCode.ERROR,
1444
1848
  message: err instanceof Error ? err.message : String(err)
1445
1849
  });
1446
1850
  span.end();
@@ -1460,7 +1864,7 @@ function wrapStream(stream, span) {
1460
1864
  if (err !== void 0) {
1461
1865
  endWithError(span, err);
1462
1866
  } else {
1463
- span.setStatus({ code: import_api3.SpanStatusCode.OK });
1867
+ span.setStatus({ code: import_api4.SpanStatusCode.OK });
1464
1868
  span.end();
1465
1869
  }
1466
1870
  };
@@ -1487,9 +1891,9 @@ function wrapStream(stream, span) {
1487
1891
  throw err;
1488
1892
  }
1489
1893
  },
1490
- async return(value2) {
1894
+ async return(value3) {
1491
1895
  finish();
1492
- return inner.return ? inner.return(value2) : { done: true, value: void 0 };
1896
+ return inner.return ? inner.return(value3) : { done: true, value: void 0 };
1493
1897
  },
1494
1898
  async throw(err) {
1495
1899
  finish(err);
@@ -1499,8 +1903,8 @@ function wrapStream(stream, span) {
1499
1903
  };
1500
1904
  };
1501
1905
  }
1502
- const value = Reflect.get(target, prop, receiver);
1503
- return typeof value === "function" ? value.bind(target) : value;
1906
+ const value2 = Reflect.get(target, prop, receiver);
1907
+ return typeof value2 === "function" ? value2.bind(target) : value2;
1504
1908
  }
1505
1909
  });
1506
1910
  }
@@ -1515,7 +1919,7 @@ function instrumentLlmClient(client, options) {
1515
1919
  const operation = /embed/i.test(callPath) ? "embeddings" : "chat";
1516
1920
  const userId = resolveUserId();
1517
1921
  const span = autterLlmTracer().startSpan(`${operation} ${request.model}`, {
1518
- kind: import_api3.SpanKind.CLIENT,
1922
+ kind: import_api4.SpanKind.CLIENT,
1519
1923
  attributes: {
1520
1924
  "gen_ai.operation.name": operation,
1521
1925
  "gen_ai.provider.name": provider,
@@ -1537,7 +1941,7 @@ function instrumentLlmClient(client, options) {
1537
1941
  return wrapStream(result, span);
1538
1942
  }
1539
1943
  applyUsage(span, extractUsage(result));
1540
- span.setStatus({ code: import_api3.SpanStatusCode.OK });
1944
+ span.setStatus({ code: import_api4.SpanStatusCode.OK });
1541
1945
  span.end();
1542
1946
  return result;
1543
1947
  }
@@ -1547,7 +1951,7 @@ function instrumentLlmClient(client, options) {
1547
1951
  return wrapStream(resolved, span);
1548
1952
  }
1549
1953
  applyUsage(span, extractUsage(resolved));
1550
- span.setStatus({ code: import_api3.SpanStatusCode.OK });
1954
+ span.setStatus({ code: import_api4.SpanStatusCode.OK });
1551
1955
  span.end();
1552
1956
  return resolved;
1553
1957
  },
@@ -1562,30 +1966,30 @@ function instrumentLlmClient(client, options) {
1562
1966
  if (cached) return cached;
1563
1967
  const proxy = new Proxy(target, {
1564
1968
  get(t, prop, receiver) {
1565
- const value = Reflect.get(t, prop, receiver);
1566
- if (typeof prop === "symbol") return value;
1969
+ const value2 = Reflect.get(t, prop, receiver);
1970
+ if (typeof prop === "symbol") return value2;
1567
1971
  const propPath = path ? `${path}.${prop}` : prop;
1568
- if (typeof value === "function") {
1972
+ if (typeof value2 === "function") {
1569
1973
  if (CALL_METHODS.has(prop)) {
1570
1974
  return (...args) => {
1571
1975
  const first = args[0];
1572
1976
  if (first && typeof first === "object" && typeof first.model === "string") {
1573
1977
  return traceCall(
1574
- value,
1978
+ value2,
1575
1979
  t,
1576
1980
  propPath,
1577
1981
  args
1578
1982
  );
1579
1983
  }
1580
- return value.apply(t, args);
1984
+ return value2.apply(t, args);
1581
1985
  };
1582
1986
  }
1583
- return value.bind(t);
1987
+ return value2.bind(t);
1584
1988
  }
1585
- if (value !== null && typeof value === "object" && !(value instanceof Promise)) {
1586
- return deepProxy(value, propPath);
1989
+ if (value2 !== null && typeof value2 === "object" && !(value2 instanceof Promise)) {
1990
+ return deepProxy(value2, propPath);
1587
1991
  }
1588
- return value;
1992
+ return value2;
1589
1993
  }
1590
1994
  });
1591
1995
  proxied.set(target, proxy);
@@ -1643,16 +2047,21 @@ ${frames.join("\n")}`;
1643
2047
  captureMessage,
1644
2048
  createBrowserRelayFetchHandler,
1645
2049
  createBrowserRelayHandler,
2050
+ createRuntimeLogger,
1646
2051
  emitLlmSelftestTrace,
2052
+ flushRuntimeLogs,
1647
2053
  initAutterServer,
1648
2054
  installAutterAutoFlush,
1649
2055
  instrumentLlmClient,
1650
2056
  makeSafeCapture,
1651
2057
  redactAttributes,
1652
2058
  reportOutcome,
2059
+ runtimeLogStats,
2060
+ runtimeLogger,
1653
2061
  sanitizeBrowserPayload,
1654
2062
  startCaughtExceptionSampler,
1655
2063
  trackLlmCall,
1656
2064
  withLlmCall,
1657
- withProcessSpan
2065
+ withProcessSpan,
2066
+ withRuntimeOperation
1658
2067
  });