@autter/runtime-node 1.3.4 → 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";
@@ -255,8 +260,8 @@ function createBrowserRelayHandler(opts) {
255
260
 
256
261
  // src/server.ts
257
262
  var import_node_http = require("http");
258
- var import_node_crypto = require("crypto");
259
- var import_api2 = require("@opentelemetry/api");
263
+ var import_node_crypto2 = require("crypto");
264
+ var import_api3 = require("@opentelemetry/api");
260
265
  var import_core2 = require("@opentelemetry/core");
261
266
  var import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
262
267
  var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
@@ -342,13 +347,13 @@ function installAutterAutoFlush(options = {}) {
342
347
  const signals = options.signals ?? DEFAULT_SIGNALS;
343
348
  const flushOnUncaughtException = options.flushOnUncaughtException !== false;
344
349
  let disposed = false;
345
- let inFlight = null;
350
+ let inFlight2 = null;
346
351
  const doFlush = async () => {
347
352
  const targets = options.targets ?? activeFlushTargets();
348
353
  if (targets.length === 0) return true;
349
- let timer;
354
+ let timer2;
350
355
  const timedOut = new Promise((resolve) => {
351
- timer = setTimeout(() => resolve(false), timeoutMs);
356
+ timer2 = setTimeout(() => resolve(false), timeoutMs);
352
357
  });
353
358
  const drained = Promise.allSettled(
354
359
  targets.map(
@@ -361,7 +366,7 @@ function installAutterAutoFlush(options = {}) {
361
366
  try {
362
367
  ok = await Promise.race([drained, timedOut]);
363
368
  } finally {
364
- clearTimeout(timer);
369
+ clearTimeout(timer2);
365
370
  }
366
371
  if (ok) {
367
372
  telemetryStats.markAllFlushed();
@@ -377,13 +382,13 @@ function installAutterAutoFlush(options = {}) {
377
382
  };
378
383
  const flush = (reason = "exit") => {
379
384
  if (disposed) return Promise.resolve(false);
380
- if (!inFlight) {
381
- inFlight = doFlush().finally(() => {
382
- inFlight = null;
385
+ if (!inFlight2) {
386
+ inFlight2 = doFlush().finally(() => {
387
+ inFlight2 = null;
383
388
  });
384
389
  }
385
390
  if (isDebugEnabled()) debugLog(`auto-flush requested on ${reason}`);
386
- return inFlight;
391
+ return inFlight2;
387
392
  };
388
393
  const signalCounts = /* @__PURE__ */ new Map();
389
394
  let handlingUncaughtException = false;
@@ -522,8 +527,8 @@ function compile(options) {
522
527
  scrubEmailValues: options?.scrubEmailValues !== false
523
528
  };
524
529
  }
525
- function redactString(value, r) {
526
- let out = value.replace(r.privateKeyBlock, r.mask);
530
+ function redactString(value2, r) {
531
+ let out = value2.replace(r.privateKeyBlock, r.mask);
527
532
  for (const re of r.valuePatterns) {
528
533
  out = out.replace(re, r.mask);
529
534
  }
@@ -541,40 +546,40 @@ function canTraverse(depth, state) {
541
546
  state.remainingWork -= 1;
542
547
  return true;
543
548
  }
544
- function redactValue(value, r, state, depth) {
545
- if (typeof value === "string") return redactString(value, r);
549
+ function redactValue(value2, r, state, depth) {
550
+ if (typeof value2 === "string") return redactString(value2, r);
546
551
  let isArray = false;
547
552
  try {
548
- isArray = Array.isArray(value);
553
+ isArray = Array.isArray(value2);
549
554
  } catch {
550
555
  return r.mask;
551
556
  }
552
557
  if (isArray) {
553
- return redactArray(value, r, state, depth);
558
+ return redactArray(value2, r, state, depth);
554
559
  }
555
- if (typeof value === "object" && value !== null) {
556
- return redactObject(value, r, state, depth);
560
+ if (typeof value2 === "object" && value2 !== null) {
561
+ return redactObject(value2, r, state, depth);
557
562
  }
558
- return value;
563
+ return value2;
559
564
  }
560
- function redactArray(value, r, state, depth) {
561
- const existing = state.ancestors.get(value);
565
+ function redactArray(value2, r, state, depth) {
566
+ const existing = state.ancestors.get(value2);
562
567
  if (existing) return r.mask;
563
568
  if (!canTraverse(depth, state)) return r.mask;
564
569
  const out = [];
565
- state.ancestors.set(value, out);
570
+ state.ancestors.set(value2, out);
566
571
  let length;
567
572
  try {
568
- length = value.length;
573
+ length = value2.length;
569
574
  } catch {
570
- state.ancestors.delete(value);
575
+ state.ancestors.delete(value2);
571
576
  return r.mask;
572
577
  }
573
578
  const limit = Math.min(length, MAX_COLLECTION_ENTRIES);
574
579
  for (let i = 0; i < limit; i += 1) {
575
580
  let item;
576
581
  try {
577
- item = value[i];
582
+ item = value2[i];
578
583
  } catch {
579
584
  out.push(r.mask);
580
585
  continue;
@@ -584,21 +589,21 @@ function redactArray(value, r, state, depth) {
584
589
  if (length > limit) {
585
590
  out.push(r.mask);
586
591
  }
587
- state.ancestors.delete(value);
592
+ state.ancestors.delete(value2);
588
593
  return out;
589
594
  }
590
- function redactObject(value, r, state, depth) {
591
- const existing = state.ancestors.get(value);
595
+ function redactObject(value2, r, state, depth) {
596
+ const existing = state.ancestors.get(value2);
592
597
  if (existing) return r.mask;
593
598
  if (!canTraverse(depth, state)) return r.mask;
594
599
  const out = {};
595
- state.ancestors.set(value, out);
600
+ state.ancestors.set(value2, out);
596
601
  let count = 0;
597
602
  let truncated = false;
598
603
  try {
599
- for (const key in value) {
604
+ for (const key in value2) {
600
605
  if (!Object.prototype.propertyIsEnumerable.call(
601
- value,
606
+ value2,
602
607
  key
603
608
  )) {
604
609
  continue;
@@ -609,7 +614,7 @@ function redactObject(value, r, state, depth) {
609
614
  }
610
615
  let nestedValue;
611
616
  try {
612
- nestedValue = value[key];
617
+ nestedValue = value2[key];
613
618
  } catch {
614
619
  out[key] = r.mask;
615
620
  count += 1;
@@ -629,7 +634,7 @@ function redactObject(value, r, state, depth) {
629
634
  if (truncated) {
630
635
  out.__redaction_truncated__ = r.mask;
631
636
  }
632
- state.ancestors.delete(value);
637
+ state.ancestors.delete(value2);
633
638
  return out;
634
639
  }
635
640
  var USAGE_TOKEN_KEYS = /* @__PURE__ */ new Set([
@@ -646,10 +651,10 @@ var USAGE_TOKEN_KEYS = /* @__PURE__ */ new Set([
646
651
  "gen_ai.usage.total_tokens",
647
652
  "gen_ai.usage.token_count"
648
653
  ]);
649
- function isSensitiveKey(key, value, r) {
654
+ function isSensitiveKey(key, value2, r) {
650
655
  const lowered = key.toLowerCase();
651
656
  if (USAGE_TOKEN_KEYS.has(lowered)) {
652
- return !(typeof value === "number" && Number.isFinite(value) && value >= 0);
657
+ return !(typeof value2 === "number" && Number.isFinite(value2) && value2 >= 0);
653
658
  }
654
659
  return r.keyPatterns.some((re) => re.test(lowered));
655
660
  }
@@ -678,9 +683,9 @@ function redactWith(attributes, r) {
678
683
  truncated = true;
679
684
  break;
680
685
  }
681
- let value;
686
+ let value2;
682
687
  try {
683
- value = attributes[key];
688
+ value2 = attributes[key];
684
689
  } catch {
685
690
  out[key] = r.mask;
686
691
  count += 1;
@@ -689,8 +694,8 @@ function redactWith(attributes, r) {
689
694
  }
690
695
  count += 1;
691
696
  state.remainingWork -= 1;
692
- if (value === void 0) continue;
693
- 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);
694
699
  }
695
700
  } catch {
696
701
  truncated = true;
@@ -753,6 +758,390 @@ function startMemoryMetrics() {
753
758
  };
754
759
  }
755
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
+
756
1145
  // src/server.ts
757
1146
  var LLM_MARKER_ATTRIBUTES = [
758
1147
  "gen_ai.operation.name",
@@ -815,14 +1204,14 @@ var RETENTION_MAX_BUFFERED_SPANS = 5e3;
815
1204
  var RETENTION_SWEEP_INTERVAL_MS = 1e4;
816
1205
  var RETENTION_TRACE_TTL_MS = 3e4;
817
1206
  function spanIndicatesError(span) {
818
- if (span.status.code === import_api2.SpanStatusCode.ERROR) return true;
1207
+ if (span.status.code === import_api3.SpanStatusCode.ERROR) return true;
819
1208
  return span.events.some((event) => event.name === "exception");
820
1209
  }
821
1210
  var TAIL_RETAINED_ATTR = "autter.tail_retained";
822
1211
  function withSampledFlag(span) {
823
1212
  const spanContext = {
824
1213
  ...span.spanContext(),
825
- traceFlags: span.spanContext().traceFlags | import_api2.TraceFlags.SAMPLED
1214
+ traceFlags: span.spanContext().traceFlags | import_api3.TraceFlags.SAMPLED
826
1215
  };
827
1216
  return {
828
1217
  name: span.name,
@@ -862,11 +1251,11 @@ var ErrorTraceRetentionProcessor = class {
862
1251
  }
863
1252
  onEnd(span) {
864
1253
  const ctx = span.spanContext();
865
- if ((ctx.traceFlags & import_api2.TraceFlags.SAMPLED) !== 0) return;
1254
+ if ((ctx.traceFlags & import_api3.TraceFlags.SAMPLED) !== 0) return;
866
1255
  const entry = this.entryFor(ctx.traceId);
867
1256
  if (entry.retained) {
868
1257
  this.forward(span);
869
- } 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) {
870
1259
  entry.retained = true;
871
1260
  this.flush(entry);
872
1261
  this.forward(span);
@@ -881,8 +1270,8 @@ var ErrorTraceRetentionProcessor = class {
881
1270
  }
882
1271
  /** Promote the active trace, if any — called on captured errors. */
883
1272
  retainActiveTrace() {
884
- const active2 = import_api2.trace.getActiveSpan()?.spanContext();
885
- 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;
886
1275
  const entry = this.entryFor(active2.traceId);
887
1276
  if (entry.retained) return;
888
1277
  entry.retained = true;
@@ -971,7 +1360,7 @@ function usageAttributes(usage) {
971
1360
  }
972
1361
  async function runLlmSpan(tracer, info, fn) {
973
1362
  const span = tracer.startSpan(`${info.operation ?? "chat"} ${info.model}`, {
974
- kind: import_api2.SpanKind.CLIENT,
1363
+ kind: import_api3.SpanKind.CLIENT,
975
1364
  attributes: llmBaseAttributes(info)
976
1365
  });
977
1366
  const setResponseModel = (model) => span.setAttribute("gen_ai.response.model", model);
@@ -983,11 +1372,11 @@ async function runLlmSpan(tracer, info, fn) {
983
1372
  setAttributes: (attributes) => span.setAttributes(attributes)
984
1373
  };
985
1374
  try {
986
- const result = await import_api2.context.with(
987
- 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),
988
1377
  () => fn(handle)
989
1378
  );
990
- span.setStatus({ code: import_api2.SpanStatusCode.OK });
1379
+ span.setStatus({ code: import_api3.SpanStatusCode.OK });
991
1380
  return result;
992
1381
  } catch (err) {
993
1382
  if (err instanceof Error) {
@@ -995,7 +1384,7 @@ async function runLlmSpan(tracer, info, fn) {
995
1384
  span.setAttribute("error.type", err.name);
996
1385
  }
997
1386
  span.setStatus({
998
- code: import_api2.SpanStatusCode.ERROR,
1387
+ code: import_api3.SpanStatusCode.ERROR,
999
1388
  message: err instanceof Error ? err.message : String(err)
1000
1389
  });
1001
1390
  throw err;
@@ -1010,7 +1399,7 @@ function recordLlmCall(tracer, call) {
1010
1399
  const span = tracer.startSpan(
1011
1400
  `${call.operation ?? "chat"} ${call.model}`,
1012
1401
  {
1013
- kind: import_api2.SpanKind.CLIENT,
1402
+ kind: import_api3.SpanKind.CLIENT,
1014
1403
  startTime,
1015
1404
  attributes: { ...llmBaseAttributes(call), ...usageAttributes(call) }
1016
1405
  }
@@ -1021,27 +1410,27 @@ function recordLlmCall(tracer, call) {
1021
1410
  span.setAttribute("error.type", call.error.name);
1022
1411
  }
1023
1412
  span.setStatus({
1024
- code: import_api2.SpanStatusCode.ERROR,
1413
+ code: import_api3.SpanStatusCode.ERROR,
1025
1414
  message: call.error instanceof Error ? call.error.message : String(call.error)
1026
1415
  });
1027
1416
  } else {
1028
- span.setStatus({ code: import_api2.SpanStatusCode.OK });
1417
+ span.setStatus({ code: import_api3.SpanStatusCode.OK });
1029
1418
  }
1030
1419
  span.end(endTime);
1031
1420
  }
1032
1421
  async function runWithSpan(tracer, name, fn, attributes) {
1033
1422
  const span = tracer.startSpan(name, { attributes });
1034
1423
  try {
1035
- const result = await import_api2.context.with(
1036
- 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),
1037
1426
  fn
1038
1427
  );
1039
- span.setStatus({ code: import_api2.SpanStatusCode.OK });
1428
+ span.setStatus({ code: import_api3.SpanStatusCode.OK });
1040
1429
  return result;
1041
1430
  } catch (err) {
1042
1431
  if (err instanceof Error) span.recordException(err);
1043
1432
  span.setStatus({
1044
- code: import_api2.SpanStatusCode.ERROR,
1433
+ code: import_api3.SpanStatusCode.ERROR,
1045
1434
  message: err instanceof Error ? err.message : String(err)
1046
1435
  });
1047
1436
  throw err;
@@ -1059,7 +1448,7 @@ function expressRouteOf(req) {
1059
1448
  }
1060
1449
  function captureExpressRoute(_span, response) {
1061
1450
  if (!(response instanceof import_node_http.ServerResponse)) return;
1062
- const rpcMetadata = (0, import_core2.getRPCMetadata)(import_api2.context.active());
1451
+ const rpcMetadata = (0, import_core2.getRPCMetadata)(import_api3.context.active());
1063
1452
  if (rpcMetadata?.type !== import_core2.RPCType.HTTP) return;
1064
1453
  const setRoute = () => {
1065
1454
  if (rpcMetadata.route) return;
@@ -1085,7 +1474,7 @@ function initAutterServer(options) {
1085
1474
  activeRedactor = makeRedactor(options.redactAttributes ?? true);
1086
1475
  const resource = new import_resources.Resource({
1087
1476
  [import_semantic_conventions.ATTR_SERVICE_NAME]: options.service,
1088
- "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),
1089
1478
  ...options.release ? { [import_semantic_conventions.ATTR_SERVICE_VERSION]: options.release } : {},
1090
1479
  "deployment.environment": environment,
1091
1480
  // Tells the ingester request metrics arrive on the metrics pipe, so
@@ -1172,7 +1561,7 @@ function initAutterServer(options) {
1172
1561
  const isError = error instanceof Error;
1173
1562
  const message = isError ? error.message : String(error);
1174
1563
  const redacted = activeRedactor(attributes);
1175
- const activeSpan = import_api2.trace.getActiveSpan();
1564
+ const activeSpan = import_api3.trace.getActiveSpan();
1176
1565
  const reuseActive = activeSpan?.isRecording() === true;
1177
1566
  const span = reuseActive ? activeSpan : errorTracer.startSpan(isError ? error.name : "Error");
1178
1567
  span.setAttributes({ "autter.severity": "error", ...redacted });
@@ -1197,31 +1586,31 @@ function initAutterServer(options) {
1197
1586
  ...redacted["autter.handled"] === true ? { "autter.handled": true, "autter.sampled": redacted["autter.sampled"] === true } : {}
1198
1587
  });
1199
1588
  }
1200
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message });
1589
+ span.setStatus({ code: import_api3.SpanStatusCode.ERROR, message });
1201
1590
  if (!reuseActive) span.end();
1202
1591
  }
1203
- function captureMessage2(message, severity = "warning", attributes) {
1204
- if (severity === "error" || severity === "fatal") {
1592
+ function captureMessage2(message, severity2 = "warning", attributes) {
1593
+ if (severity2 === "error" || severity2 === "fatal") {
1205
1594
  errorTraceBuffer?.retainActiveTrace();
1206
1595
  }
1207
1596
  telemetryStats.markCaptured();
1208
1597
  const stack = new Error().stack?.split("\n").filter((line, i) => i === 0 || !line.includes("captureMessage")).join("\n");
1209
1598
  const span = errorTracer.startSpan("Message", {
1210
- attributes: { "autter.severity": severity, ...activeRedactor(attributes) }
1599
+ attributes: { "autter.severity": severity2, ...activeRedactor(attributes) }
1211
1600
  });
1212
1601
  span.addEvent("exception", {
1213
1602
  "exception.type": "Message",
1214
1603
  "exception.message": message,
1215
1604
  ...stack ? { "exception.stacktrace": stack } : {},
1216
- "autter.severity": severity
1605
+ "autter.severity": severity2
1217
1606
  });
1218
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message });
1607
+ span.setStatus({ code: import_api3.SpanStatusCode.ERROR, message });
1219
1608
  span.end();
1220
1609
  }
1221
1610
  function reportOutcome2(name, message, attributes) {
1222
1611
  errorTraceBuffer?.retainActiveTrace();
1223
1612
  telemetryStats.markCaptured();
1224
- const activeSpan = import_api2.trace.getActiveSpan();
1613
+ const activeSpan = import_api3.trace.getActiveSpan();
1225
1614
  const reuseActive = activeSpan?.isRecording() === true;
1226
1615
  const span = reuseActive ? activeSpan : errorTracer.startSpan("OutcomeFailure");
1227
1616
  const safeOutcome = activeRedactor({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
@@ -1233,9 +1622,20 @@ function initAutterServer(options) {
1233
1622
  "autter.outcome.name": safeName,
1234
1623
  "autter.outcome.message": safeMessage
1235
1624
  });
1236
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message: safeMessage });
1625
+ span.setStatus({ code: import_api3.SpanStatusCode.ERROR, message: safeMessage });
1237
1626
  if (!reuseActive) span.end();
1238
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
+ });
1239
1639
  if (options.captureGlobalErrors !== false) {
1240
1640
  process.on("uncaughtExceptionMonitor", (err) => {
1241
1641
  captureException2(err, { "autter.unhandled": true });
@@ -1246,6 +1646,7 @@ function initAutterServer(options) {
1246
1646
  const flushTarget = {
1247
1647
  forceFlush: async () => {
1248
1648
  const results = await Promise.allSettled([
1649
+ flushRuntimeLogs(),
1249
1650
  Promise.resolve().then(() => alwaysOnProvider.forceFlush()),
1250
1651
  Promise.resolve().then(() => mainSpanProcessor.forceFlush()),
1251
1652
  ...errorTraceBuffer ? [Promise.resolve().then(() => errorTraceBuffer.forceFlush())] : [],
@@ -1272,6 +1673,7 @@ function initAutterServer(options) {
1272
1673
  withLlmCall: (info, fn) => runLlmSpan(llmTracer, info, fn),
1273
1674
  trackLlmCall: (call) => recordLlmCall(llmTracer, call),
1274
1675
  shutdown: async () => {
1676
+ const logShutdown = await Promise.allSettled([shutdownRuntimeLogger()]);
1275
1677
  stopMemoryMetrics?.();
1276
1678
  active = null;
1277
1679
  activeAlwaysOnProvider = null;
@@ -1280,6 +1682,7 @@ function initAutterServer(options) {
1280
1682
  unregisterFlushTargets();
1281
1683
  telemetryStats.markAllFlushed();
1282
1684
  await Promise.allSettled([alwaysOnProvider.shutdown(), sdk.shutdown()]);
1685
+ if (logShutdown[0]?.status === "rejected") throw logShutdown[0].reason;
1283
1686
  }
1284
1687
  };
1285
1688
  active = server;
@@ -1291,48 +1694,48 @@ function captureException(error, attributes) {
1291
1694
  active.captureException(error, attributes);
1292
1695
  return;
1293
1696
  }
1294
- 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 });
1295
1698
  if (error instanceof Error) span.recordException(error);
1296
1699
  span.setStatus({
1297
- code: import_api2.SpanStatusCode.ERROR,
1700
+ code: import_api3.SpanStatusCode.ERROR,
1298
1701
  message: error instanceof Error ? error.message : String(error)
1299
1702
  });
1300
1703
  span.end();
1301
1704
  }
1302
1705
  function withProcessSpan(name, fn, attributes) {
1303
1706
  if (active) return active.withProcessSpan(name, fn, attributes);
1304
- return runWithSpan(import_api2.trace.getTracer("autter-processes"), name, fn, attributes);
1707
+ return runWithSpan(import_api3.trace.getTracer("autter-processes"), name, fn, attributes);
1305
1708
  }
1306
1709
  function withLlmCall(info, fn) {
1307
1710
  if (active) return active.withLlmCall(info, fn);
1308
- return runLlmSpan(import_api2.trace.getTracer("autter-llm"), info, fn);
1711
+ return runLlmSpan(import_api3.trace.getTracer("autter-llm"), info, fn);
1309
1712
  }
1310
1713
  function trackLlmCall(call) {
1311
1714
  if (active) {
1312
1715
  active.trackLlmCall(call);
1313
1716
  return;
1314
1717
  }
1315
- recordLlmCall(import_api2.trace.getTracer("autter-llm"), call);
1718
+ recordLlmCall(import_api3.trace.getTracer("autter-llm"), call);
1316
1719
  }
1317
- function captureMessage(message, severity = "warning", attributes) {
1720
+ function captureMessage(message, severity2 = "warning", attributes) {
1318
1721
  if (active) {
1319
- active.captureMessage(message, severity, attributes);
1722
+ active.captureMessage(message, severity2, attributes);
1320
1723
  return;
1321
1724
  }
1322
- const span = import_api2.trace.getTracer("autter-errors").startSpan("Message", {
1323
- attributes: { "autter.severity": severity, ...attributes }
1725
+ const span = import_api3.trace.getTracer("autter-errors").startSpan("Message", {
1726
+ attributes: { "autter.severity": severity2, ...attributes }
1324
1727
  });
1325
1728
  span.addEvent("exception", {
1326
1729
  "exception.type": "Message",
1327
1730
  "exception.message": message,
1328
- "autter.severity": severity
1731
+ "autter.severity": severity2
1329
1732
  });
1330
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message });
1733
+ span.setStatus({ code: import_api3.SpanStatusCode.ERROR, message });
1331
1734
  span.end();
1332
1735
  }
1333
1736
  function reportOutcome(name, message, attributes) {
1334
1737
  if (active) return active.reportOutcome(name, message, attributes);
1335
- const span = import_api2.trace.getTracer("autter-outcomes").startSpan("OutcomeFailure");
1738
+ const span = import_api3.trace.getTracer("autter-outcomes").startSpan("OutcomeFailure");
1336
1739
  const safeOutcome = redactAttributes({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
1337
1740
  const safeName = String(safeOutcome["autter.outcome.name"] ?? "operation");
1338
1741
  const safeMessage = String(safeOutcome["autter.outcome.message"] ?? "failed");
@@ -1342,18 +1745,18 @@ function reportOutcome(name, message, attributes) {
1342
1745
  "autter.outcome.name": safeName,
1343
1746
  "autter.outcome.message": safeMessage
1344
1747
  });
1345
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message: safeMessage });
1748
+ span.setStatus({ code: import_api3.SpanStatusCode.ERROR, message: safeMessage });
1346
1749
  span.end();
1347
1750
  }
1348
1751
  function makeSafeCapture(options) {
1349
1752
  const clean = (attributes) => attributes ? { ...redactAttributes(attributes, options) } : void 0;
1350
1753
  return {
1351
1754
  captureException: (error, attributes) => captureException(error, clean(attributes)),
1352
- captureMessage: (message, severity, attributes) => captureMessage(message, severity ?? "warning", clean(attributes))
1755
+ captureMessage: (message, severity2, attributes) => captureMessage(message, severity2 ?? "warning", clean(attributes))
1353
1756
  };
1354
1757
  }
1355
1758
  function alwaysOnTracer(name) {
1356
- return activeAlwaysOnProvider ? activeAlwaysOnProvider.getTracer(name) : import_api2.trace.getTracer(name);
1759
+ return activeAlwaysOnProvider ? activeAlwaysOnProvider.getTracer(name) : import_api3.trace.getTracer(name);
1357
1760
  }
1358
1761
  function autterLlmTracer() {
1359
1762
  return alwaysOnTracer("autter-llm");
@@ -1373,7 +1776,7 @@ async function emitLlmSelftestTrace() {
1373
1776
  attributes: { "autter.selftest": true }
1374
1777
  },
1375
1778
  (llm) => {
1376
- traceId = import_api2.trace.getActiveSpan()?.spanContext().traceId ?? "";
1779
+ traceId = import_api3.trace.getActiveSpan()?.spanContext().traceId ?? "";
1377
1780
  llm.setUsage({ inputTokens: 1, outputTokens: 1 });
1378
1781
  llm.setCost(0);
1379
1782
  }
@@ -1383,7 +1786,7 @@ async function emitLlmSelftestTrace() {
1383
1786
  }
1384
1787
 
1385
1788
  // src/llm-instrument.ts
1386
- var import_api3 = require("@opentelemetry/api");
1789
+ var import_api4 = require("@opentelemetry/api");
1387
1790
  var CALL_METHODS = /* @__PURE__ */ new Set([
1388
1791
  "create",
1389
1792
  // openai chat/completions/embeddings/responses, anthropic messages
@@ -1441,7 +1844,7 @@ function applyUsage(span, usage) {
1441
1844
  function endWithError(span, err) {
1442
1845
  if (err instanceof Error) span.recordException(err);
1443
1846
  span.setStatus({
1444
- code: import_api3.SpanStatusCode.ERROR,
1847
+ code: import_api4.SpanStatusCode.ERROR,
1445
1848
  message: err instanceof Error ? err.message : String(err)
1446
1849
  });
1447
1850
  span.end();
@@ -1461,7 +1864,7 @@ function wrapStream(stream, span) {
1461
1864
  if (err !== void 0) {
1462
1865
  endWithError(span, err);
1463
1866
  } else {
1464
- span.setStatus({ code: import_api3.SpanStatusCode.OK });
1867
+ span.setStatus({ code: import_api4.SpanStatusCode.OK });
1465
1868
  span.end();
1466
1869
  }
1467
1870
  };
@@ -1488,9 +1891,9 @@ function wrapStream(stream, span) {
1488
1891
  throw err;
1489
1892
  }
1490
1893
  },
1491
- async return(value2) {
1894
+ async return(value3) {
1492
1895
  finish();
1493
- return inner.return ? inner.return(value2) : { done: true, value: void 0 };
1896
+ return inner.return ? inner.return(value3) : { done: true, value: void 0 };
1494
1897
  },
1495
1898
  async throw(err) {
1496
1899
  finish(err);
@@ -1500,8 +1903,8 @@ function wrapStream(stream, span) {
1500
1903
  };
1501
1904
  };
1502
1905
  }
1503
- const value = Reflect.get(target, prop, receiver);
1504
- 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;
1505
1908
  }
1506
1909
  });
1507
1910
  }
@@ -1516,7 +1919,7 @@ function instrumentLlmClient(client, options) {
1516
1919
  const operation = /embed/i.test(callPath) ? "embeddings" : "chat";
1517
1920
  const userId = resolveUserId();
1518
1921
  const span = autterLlmTracer().startSpan(`${operation} ${request.model}`, {
1519
- kind: import_api3.SpanKind.CLIENT,
1922
+ kind: import_api4.SpanKind.CLIENT,
1520
1923
  attributes: {
1521
1924
  "gen_ai.operation.name": operation,
1522
1925
  "gen_ai.provider.name": provider,
@@ -1538,7 +1941,7 @@ function instrumentLlmClient(client, options) {
1538
1941
  return wrapStream(result, span);
1539
1942
  }
1540
1943
  applyUsage(span, extractUsage(result));
1541
- span.setStatus({ code: import_api3.SpanStatusCode.OK });
1944
+ span.setStatus({ code: import_api4.SpanStatusCode.OK });
1542
1945
  span.end();
1543
1946
  return result;
1544
1947
  }
@@ -1548,7 +1951,7 @@ function instrumentLlmClient(client, options) {
1548
1951
  return wrapStream(resolved, span);
1549
1952
  }
1550
1953
  applyUsage(span, extractUsage(resolved));
1551
- span.setStatus({ code: import_api3.SpanStatusCode.OK });
1954
+ span.setStatus({ code: import_api4.SpanStatusCode.OK });
1552
1955
  span.end();
1553
1956
  return resolved;
1554
1957
  },
@@ -1563,30 +1966,30 @@ function instrumentLlmClient(client, options) {
1563
1966
  if (cached) return cached;
1564
1967
  const proxy = new Proxy(target, {
1565
1968
  get(t, prop, receiver) {
1566
- const value = Reflect.get(t, prop, receiver);
1567
- if (typeof prop === "symbol") return value;
1969
+ const value2 = Reflect.get(t, prop, receiver);
1970
+ if (typeof prop === "symbol") return value2;
1568
1971
  const propPath = path ? `${path}.${prop}` : prop;
1569
- if (typeof value === "function") {
1972
+ if (typeof value2 === "function") {
1570
1973
  if (CALL_METHODS.has(prop)) {
1571
1974
  return (...args) => {
1572
1975
  const first = args[0];
1573
1976
  if (first && typeof first === "object" && typeof first.model === "string") {
1574
1977
  return traceCall(
1575
- value,
1978
+ value2,
1576
1979
  t,
1577
1980
  propPath,
1578
1981
  args
1579
1982
  );
1580
1983
  }
1581
- return value.apply(t, args);
1984
+ return value2.apply(t, args);
1582
1985
  };
1583
1986
  }
1584
- return value.bind(t);
1987
+ return value2.bind(t);
1585
1988
  }
1586
- if (value !== null && typeof value === "object" && !(value instanceof Promise)) {
1587
- return deepProxy(value, propPath);
1989
+ if (value2 !== null && typeof value2 === "object" && !(value2 instanceof Promise)) {
1990
+ return deepProxy(value2, propPath);
1588
1991
  }
1589
- return value;
1992
+ return value2;
1590
1993
  }
1591
1994
  });
1592
1995
  proxied.set(target, proxy);
@@ -1644,16 +2047,21 @@ ${frames.join("\n")}`;
1644
2047
  captureMessage,
1645
2048
  createBrowserRelayFetchHandler,
1646
2049
  createBrowserRelayHandler,
2050
+ createRuntimeLogger,
1647
2051
  emitLlmSelftestTrace,
2052
+ flushRuntimeLogs,
1648
2053
  initAutterServer,
1649
2054
  installAutterAutoFlush,
1650
2055
  instrumentLlmClient,
1651
2056
  makeSafeCapture,
1652
2057
  redactAttributes,
1653
2058
  reportOutcome,
2059
+ runtimeLogStats,
2060
+ runtimeLogger,
1654
2061
  sanitizeBrowserPayload,
1655
2062
  startCaughtExceptionSampler,
1656
2063
  trackLlmCall,
1657
2064
  withLlmCall,
1658
- withProcessSpan
2065
+ withProcessSpan,
2066
+ withRuntimeOperation
1659
2067
  });