@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.js CHANGED
@@ -17,8 +17,8 @@ var IpWindow = class {
17
17
  return entry.count <= this.limitPerMinute;
18
18
  }
19
19
  };
20
- function firstForwardedFor(value) {
21
- const raw = Array.isArray(value) ? value[0] : value;
20
+ function firstForwardedFor(value2) {
21
+ const raw = Array.isArray(value2) ? value2[0] : value2;
22
22
  return raw ? (raw.split(",")[0] ?? "").trim() : "";
23
23
  }
24
24
  var DEFAULT_ENDPOINT = "https://otlp.autter.dev";
@@ -204,10 +204,10 @@ function createBrowserRelayHandler(opts) {
204
204
 
205
205
  // src/server.ts
206
206
  import { ServerResponse } from "http";
207
- import { randomUUID } from "crypto";
207
+ import { randomUUID as randomUUID2 } from "crypto";
208
208
  import {
209
209
  context,
210
- trace,
210
+ trace as trace2,
211
211
  SpanKind,
212
212
  SpanStatusCode,
213
213
  TraceFlags
@@ -310,13 +310,13 @@ function installAutterAutoFlush(options = {}) {
310
310
  const signals = options.signals ?? DEFAULT_SIGNALS;
311
311
  const flushOnUncaughtException = options.flushOnUncaughtException !== false;
312
312
  let disposed = false;
313
- let inFlight = null;
313
+ let inFlight2 = null;
314
314
  const doFlush = async () => {
315
315
  const targets = options.targets ?? activeFlushTargets();
316
316
  if (targets.length === 0) return true;
317
- let timer;
317
+ let timer2;
318
318
  const timedOut = new Promise((resolve) => {
319
- timer = setTimeout(() => resolve(false), timeoutMs);
319
+ timer2 = setTimeout(() => resolve(false), timeoutMs);
320
320
  });
321
321
  const drained = Promise.allSettled(
322
322
  targets.map(
@@ -329,7 +329,7 @@ function installAutterAutoFlush(options = {}) {
329
329
  try {
330
330
  ok = await Promise.race([drained, timedOut]);
331
331
  } finally {
332
- clearTimeout(timer);
332
+ clearTimeout(timer2);
333
333
  }
334
334
  if (ok) {
335
335
  telemetryStats.markAllFlushed();
@@ -345,13 +345,13 @@ function installAutterAutoFlush(options = {}) {
345
345
  };
346
346
  const flush = (reason = "exit") => {
347
347
  if (disposed) return Promise.resolve(false);
348
- if (!inFlight) {
349
- inFlight = doFlush().finally(() => {
350
- inFlight = null;
348
+ if (!inFlight2) {
349
+ inFlight2 = doFlush().finally(() => {
350
+ inFlight2 = null;
351
351
  });
352
352
  }
353
353
  if (isDebugEnabled()) debugLog(`auto-flush requested on ${reason}`);
354
- return inFlight;
354
+ return inFlight2;
355
355
  };
356
356
  const signalCounts = /* @__PURE__ */ new Map();
357
357
  let handlingUncaughtException = false;
@@ -490,8 +490,8 @@ function compile(options) {
490
490
  scrubEmailValues: options?.scrubEmailValues !== false
491
491
  };
492
492
  }
493
- function redactString(value, r) {
494
- let out = value.replace(r.privateKeyBlock, r.mask);
493
+ function redactString(value2, r) {
494
+ let out = value2.replace(r.privateKeyBlock, r.mask);
495
495
  for (const re of r.valuePatterns) {
496
496
  out = out.replace(re, r.mask);
497
497
  }
@@ -509,40 +509,40 @@ function canTraverse(depth, state) {
509
509
  state.remainingWork -= 1;
510
510
  return true;
511
511
  }
512
- function redactValue(value, r, state, depth) {
513
- if (typeof value === "string") return redactString(value, r);
512
+ function redactValue(value2, r, state, depth) {
513
+ if (typeof value2 === "string") return redactString(value2, r);
514
514
  let isArray = false;
515
515
  try {
516
- isArray = Array.isArray(value);
516
+ isArray = Array.isArray(value2);
517
517
  } catch {
518
518
  return r.mask;
519
519
  }
520
520
  if (isArray) {
521
- return redactArray(value, r, state, depth);
521
+ return redactArray(value2, r, state, depth);
522
522
  }
523
- if (typeof value === "object" && value !== null) {
524
- return redactObject(value, r, state, depth);
523
+ if (typeof value2 === "object" && value2 !== null) {
524
+ return redactObject(value2, r, state, depth);
525
525
  }
526
- return value;
526
+ return value2;
527
527
  }
528
- function redactArray(value, r, state, depth) {
529
- const existing = state.ancestors.get(value);
528
+ function redactArray(value2, r, state, depth) {
529
+ const existing = state.ancestors.get(value2);
530
530
  if (existing) return r.mask;
531
531
  if (!canTraverse(depth, state)) return r.mask;
532
532
  const out = [];
533
- state.ancestors.set(value, out);
533
+ state.ancestors.set(value2, out);
534
534
  let length;
535
535
  try {
536
- length = value.length;
536
+ length = value2.length;
537
537
  } catch {
538
- state.ancestors.delete(value);
538
+ state.ancestors.delete(value2);
539
539
  return r.mask;
540
540
  }
541
541
  const limit = Math.min(length, MAX_COLLECTION_ENTRIES);
542
542
  for (let i = 0; i < limit; i += 1) {
543
543
  let item;
544
544
  try {
545
- item = value[i];
545
+ item = value2[i];
546
546
  } catch {
547
547
  out.push(r.mask);
548
548
  continue;
@@ -552,21 +552,21 @@ function redactArray(value, r, state, depth) {
552
552
  if (length > limit) {
553
553
  out.push(r.mask);
554
554
  }
555
- state.ancestors.delete(value);
555
+ state.ancestors.delete(value2);
556
556
  return out;
557
557
  }
558
- function redactObject(value, r, state, depth) {
559
- const existing = state.ancestors.get(value);
558
+ function redactObject(value2, r, state, depth) {
559
+ const existing = state.ancestors.get(value2);
560
560
  if (existing) return r.mask;
561
561
  if (!canTraverse(depth, state)) return r.mask;
562
562
  const out = {};
563
- state.ancestors.set(value, out);
563
+ state.ancestors.set(value2, out);
564
564
  let count = 0;
565
565
  let truncated = false;
566
566
  try {
567
- for (const key in value) {
567
+ for (const key in value2) {
568
568
  if (!Object.prototype.propertyIsEnumerable.call(
569
- value,
569
+ value2,
570
570
  key
571
571
  )) {
572
572
  continue;
@@ -577,7 +577,7 @@ function redactObject(value, r, state, depth) {
577
577
  }
578
578
  let nestedValue;
579
579
  try {
580
- nestedValue = value[key];
580
+ nestedValue = value2[key];
581
581
  } catch {
582
582
  out[key] = r.mask;
583
583
  count += 1;
@@ -597,7 +597,7 @@ function redactObject(value, r, state, depth) {
597
597
  if (truncated) {
598
598
  out.__redaction_truncated__ = r.mask;
599
599
  }
600
- state.ancestors.delete(value);
600
+ state.ancestors.delete(value2);
601
601
  return out;
602
602
  }
603
603
  var USAGE_TOKEN_KEYS = /* @__PURE__ */ new Set([
@@ -614,10 +614,10 @@ var USAGE_TOKEN_KEYS = /* @__PURE__ */ new Set([
614
614
  "gen_ai.usage.total_tokens",
615
615
  "gen_ai.usage.token_count"
616
616
  ]);
617
- function isSensitiveKey(key, value, r) {
617
+ function isSensitiveKey(key, value2, r) {
618
618
  const lowered = key.toLowerCase();
619
619
  if (USAGE_TOKEN_KEYS.has(lowered)) {
620
- return !(typeof value === "number" && Number.isFinite(value) && value >= 0);
620
+ return !(typeof value2 === "number" && Number.isFinite(value2) && value2 >= 0);
621
621
  }
622
622
  return r.keyPatterns.some((re) => re.test(lowered));
623
623
  }
@@ -646,9 +646,9 @@ function redactWith(attributes, r) {
646
646
  truncated = true;
647
647
  break;
648
648
  }
649
- let value;
649
+ let value2;
650
650
  try {
651
- value = attributes[key];
651
+ value2 = attributes[key];
652
652
  } catch {
653
653
  out[key] = r.mask;
654
654
  count += 1;
@@ -657,8 +657,8 @@ function redactWith(attributes, r) {
657
657
  }
658
658
  count += 1;
659
659
  state.remainingWork -= 1;
660
- if (value === void 0) continue;
661
- out[key] = isSensitiveKey(key, value, r) ? r.mask : redactValue(value, r, state, 0);
660
+ if (value2 === void 0) continue;
661
+ out[key] = isSensitiveKey(key, value2, r) ? r.mask : redactValue(value2, r, state, 0);
662
662
  }
663
663
  } catch {
664
664
  truncated = true;
@@ -721,6 +721,390 @@ function startMemoryMetrics() {
721
721
  };
722
722
  }
723
723
 
724
+ // src/logger.ts
725
+ import { AsyncLocalStorage } from "async_hooks";
726
+ import { randomUUID } from "crypto";
727
+ import { trace } from "@opentelemetry/api";
728
+ var local = new AsyncLocalStorage();
729
+ var config = null;
730
+ var queue = [];
731
+ var queueBytes = 0;
732
+ var inFlightBytes = 0;
733
+ var timer;
734
+ var flushing = null;
735
+ var dropped = 0;
736
+ var inFlight = 0;
737
+ var stopping = false;
738
+ var failureStreak = 0;
739
+ var severity = { debug: 5, info: 9, warning: 13, error: 17 };
740
+ function safe(attributes = {}) {
741
+ const redacted = config?.redact(attributes) ?? redactAttributes(attributes);
742
+ let budget = 512;
743
+ let characters = 16384;
744
+ let truncated = false;
745
+ const visit = (input, depth) => {
746
+ if (--budget < 0 || depth > 6) {
747
+ truncated = true;
748
+ return "[truncated]";
749
+ }
750
+ if (typeof input === "string") {
751
+ const text = input.replace(/(https?:\/\/[^\s?#]+)[?#][^\s]*/gi, "$1");
752
+ const kept = text.slice(0, Math.max(0, characters));
753
+ truncated ||= kept.length < text.length;
754
+ characters -= kept.length;
755
+ return kept;
756
+ }
757
+ if (typeof input === "number")
758
+ return Number.isFinite(input) ? input : void 0;
759
+ if (typeof input === "boolean" || input === null) return input;
760
+ if (Array.isArray(input)) {
761
+ truncated ||= input.length > 64;
762
+ return input.slice(0, 64).map((item) => visit(item, depth + 1));
763
+ }
764
+ if (input && typeof input === "object") {
765
+ const entries = Object.entries(input);
766
+ if (depth === 0) {
767
+ const priority = (key) => /^autter\.(?:operation|event)\./.test(key) ? 2 : /^exception\./.test(key) ? 1 : 0;
768
+ entries.sort(([a], [b]) => priority(b) - priority(a));
769
+ }
770
+ truncated ||= entries.length > 100;
771
+ return Object.fromEntries(
772
+ entries.slice(0, 100).filter(([key]) => !/^(?:__proto__|constructor|prototype)$/.test(key)).map(([key, item]) => [key.slice(0, 200), visit(item, depth + 1)])
773
+ );
774
+ }
775
+ return void 0;
776
+ };
777
+ const result = visit(redacted, 0);
778
+ if (truncated) result["autter.context.truncated"] = true;
779
+ return result;
780
+ }
781
+ function operationAttributes(state) {
782
+ return state ? {
783
+ "autter.operation.id": state.id,
784
+ "autter.operation.name": state.name,
785
+ ...state.parentId ? { "autter.operation.parent_id": state.parentId } : {},
786
+ ...state.attributes
787
+ } : {};
788
+ }
789
+ function userAttributes(attributes = {}) {
790
+ return Object.fromEntries(
791
+ Object.entries(safe(attributes)).filter(
792
+ ([key]) => !/^autter\.(?:operation|event)\./.test(key)
793
+ )
794
+ );
795
+ }
796
+ function mergeContext(current, next = {}) {
797
+ const merge = (left, right, depth) => {
798
+ const result = { ...left };
799
+ for (const [key, value2] of Object.entries(right)) {
800
+ const prior = result[key];
801
+ result[key] = depth < 6 && value2 && typeof value2 === "object" && !Array.isArray(value2) && prior && typeof prior === "object" && !Array.isArray(prior) ? merge(prior, value2, depth + 1) : value2;
802
+ }
803
+ return result;
804
+ };
805
+ return userAttributes(merge(current, userAttributes(next), 0));
806
+ }
807
+ function traceAttributes(state) {
808
+ return Object.fromEntries(
809
+ Object.entries(operationAttributes(state)).filter(([, item]) => item !== null && item !== void 0).map(([key, item]) => [
810
+ key,
811
+ typeof item === "object" ? JSON.stringify(item) : item
812
+ ])
813
+ );
814
+ }
815
+ function scheduleFlush() {
816
+ if (!timer && config && !stopping && queue.length) {
817
+ timer = setTimeout(
818
+ () => {
819
+ timer = void 0;
820
+ void flushRuntimeLogs().catch(() => {
821
+ });
822
+ },
823
+ Math.min(3e4, 2e3 * 2 ** Math.min(failureStreak, 4))
824
+ );
825
+ timer.unref();
826
+ }
827
+ }
828
+ function value(input) {
829
+ if (typeof input === "number") return { doubleValue: input };
830
+ if (typeof input === "boolean") return { boolValue: input };
831
+ if (Array.isArray(input))
832
+ return { arrayValue: { values: input.slice(0, 64).map(value) } };
833
+ if (input && typeof input === "object")
834
+ return {
835
+ kvlistValue: {
836
+ values: Object.entries(input).slice(0, 128).map(([key, item]) => ({ key, value: value(item) }))
837
+ }
838
+ };
839
+ return { stringValue: String(input ?? "").slice(0, 32e3) };
840
+ }
841
+ function emit(level, message, attributes, extra = {}) {
842
+ const state = local.getStore();
843
+ if (state?.sealed) return;
844
+ if (extra["autter.event.type"] !== "operation" && severity[level] < severity[config?.options?.minLevel ?? "debug"])
845
+ return;
846
+ const span = trace.getActiveSpan()?.spanContext();
847
+ const user = mergeContext(state?.attributes ?? {}, attributes);
848
+ const exceptions = Object.fromEntries(
849
+ Object.entries(user).filter(([key]) => key.startsWith("exception."))
850
+ );
851
+ const attrs = safe({
852
+ "autter.event.id": randomUUID(),
853
+ ...extra,
854
+ ...exceptions,
855
+ ...operationAttributes(state),
856
+ ...user
857
+ });
858
+ const record = {
859
+ timeUnixNano: String(BigInt(Date.now()) * 1000000n),
860
+ severityNumber: severity[level],
861
+ severityText: level.toUpperCase(),
862
+ body: value(String(safe({ message }).message ?? "").slice(0, 4e3)),
863
+ attributes: Object.entries(attrs).map(([key, item]) => ({
864
+ key,
865
+ value: value(item)
866
+ })),
867
+ ...span && /^[a-f0-9]{32}$/.test(span.traceId) && !/^0+$/.test(span.traceId) ? { traceId: span.traceId, spanId: span.spanId } : {}
868
+ };
869
+ if (config?.options?.console !== false)
870
+ console.log(
871
+ JSON.stringify({ ...attrs, level, message: record.body.stringValue })
872
+ );
873
+ if (!config) return;
874
+ if (stopping) {
875
+ dropped++;
876
+ return;
877
+ }
878
+ const bytes = Buffer.byteLength(JSON.stringify(record));
879
+ if (bytes > 256 * 1024 || queue.length + inFlight >= 1e3 || queueBytes + inFlightBytes + bytes > 4 * 1024 * 1024) {
880
+ dropped++;
881
+ console.warn(
882
+ "[autter-runtime] log buffer or record limit reached; record dropped"
883
+ );
884
+ return;
885
+ }
886
+ queue.push({ record, bytes });
887
+ queueBytes += bytes;
888
+ scheduleFlush();
889
+ }
890
+ function configureRuntimeLogger(next) {
891
+ config = next;
892
+ stopping = false;
893
+ failureStreak = 0;
894
+ }
895
+ function runtimeLogStats() {
896
+ return { buffered: queue.length + inFlight, dropped };
897
+ }
898
+ async function flushRuntimeLogs() {
899
+ if (flushing) return flushing;
900
+ if (!config || !queue.length) return;
901
+ if (timer) {
902
+ clearTimeout(timer);
903
+ timer = void 0;
904
+ }
905
+ const activeConfig = config;
906
+ const deadline = Date.now() + 1e4;
907
+ flushing = (async () => {
908
+ const count = queue.length;
909
+ for (let remaining = count; remaining > 0; ) {
910
+ const batch = [];
911
+ inFlightBytes = 0;
912
+ while (batch.length < Math.min(50, remaining) && queue.length) {
913
+ const next = queue[0];
914
+ if (batch.length && inFlightBytes + next.bytes > 512 * 1024) break;
915
+ batch.push(queue.shift());
916
+ inFlightBytes += next.bytes;
917
+ queueBytes -= next.bytes;
918
+ }
919
+ remaining -= batch.length;
920
+ inFlight = batch.length;
921
+ const body = JSON.stringify({
922
+ resourceLogs: [
923
+ {
924
+ resource: {
925
+ attributes: Object.entries({
926
+ "service.name": activeConfig.service,
927
+ "deployment.environment.name": activeConfig.environment,
928
+ ...activeConfig.release ? { "service.version": activeConfig.release } : {}
929
+ }).map(([key, item]) => ({ key, value: value(item) }))
930
+ },
931
+ scopeLogs: [
932
+ {
933
+ scope: { name: "autter-runtime" },
934
+ logRecords: batch.map((entry) => entry.record)
935
+ }
936
+ ]
937
+ }
938
+ ]
939
+ });
940
+ let failure;
941
+ for (let attempt = 0; attempt < 3; attempt++) {
942
+ try {
943
+ if (Date.now() >= deadline)
944
+ throw new Error("Runtime log flush exceeded 10 seconds");
945
+ const response = await fetch(`${activeConfig.endpoint}/v1/logs`, {
946
+ method: "POST",
947
+ headers: {
948
+ "content-type": "application/json",
949
+ authorization: `Bearer ${activeConfig.apiKey}`
950
+ },
951
+ body,
952
+ signal: AbortSignal.timeout(
953
+ Math.max(1, Math.min(3e3, deadline - Date.now()))
954
+ )
955
+ });
956
+ if (!response.ok)
957
+ throw new Error(`Runtime log export failed (${response.status})`);
958
+ failure = void 0;
959
+ break;
960
+ } catch (error) {
961
+ failure = error;
962
+ }
963
+ }
964
+ inFlight = 0;
965
+ if (failure) {
966
+ failureStreak++;
967
+ queue = [...batch, ...queue];
968
+ queueBytes += inFlightBytes;
969
+ inFlightBytes = 0;
970
+ throw failure;
971
+ }
972
+ inFlightBytes = 0;
973
+ failureStreak = 0;
974
+ }
975
+ })().finally(() => {
976
+ flushing = null;
977
+ scheduleFlush();
978
+ });
979
+ return flushing;
980
+ }
981
+ async function shutdownRuntimeLogger() {
982
+ stopping = true;
983
+ try {
984
+ await flushRuntimeLogs();
985
+ } finally {
986
+ if (queue.length) {
987
+ dropped += queue.length;
988
+ console.warn(
989
+ `[autter-runtime] ${queue.length} log records could not be delivered before shutdown`
990
+ );
991
+ }
992
+ queue = [];
993
+ queueBytes = 0;
994
+ config = null;
995
+ if (timer) clearTimeout(timer);
996
+ timer = void 0;
997
+ }
998
+ }
999
+ function createRuntimeLogger(attributes = {}) {
1000
+ const attrs = userAttributes(attributes);
1001
+ return {
1002
+ debug: (message, extra) => emit("debug", message, mergeContext(attrs, extra)),
1003
+ info: (message, extra) => emit("info", message, mergeContext(attrs, extra)),
1004
+ warn: (message, extra) => emit("warning", message, mergeContext(attrs, extra)),
1005
+ error: (error, extra) => emit("error", error instanceof Error ? error.message : String(error), {
1006
+ ...attrs,
1007
+ ...extra,
1008
+ ...error instanceof Error ? {
1009
+ "exception.type": error.name,
1010
+ "exception.stacktrace": error.stack ?? ""
1011
+ } : {}
1012
+ })
1013
+ };
1014
+ }
1015
+ var runtimeLogger = createRuntimeLogger();
1016
+ function withRuntimeOperation(name, fn, attributes = {}) {
1017
+ const parent = local.getStore();
1018
+ const state = {
1019
+ id: randomUUID(),
1020
+ name: String(safe({ name }).name).slice(0, 200),
1021
+ parentId: parent?.sealed ? void 0 : parent?.id,
1022
+ attributes: mergeContext(
1023
+ parent?.sealed ? {} : parent?.attributes ?? {},
1024
+ attributes
1025
+ ),
1026
+ startedAt: Date.now(),
1027
+ outcome: "succeeded",
1028
+ steps: []
1029
+ };
1030
+ const operation = {
1031
+ id: state.id,
1032
+ setContext: (attrs) => {
1033
+ if (!state.sealed) {
1034
+ state.attributes = mergeContext(state.attributes, attrs);
1035
+ trace.getActiveSpan()?.setAttributes(traceAttributes(state));
1036
+ }
1037
+ },
1038
+ outcome: (status, message) => {
1039
+ if (state.sealed) return;
1040
+ state.outcome = status;
1041
+ state.message = message;
1042
+ if (status === "failed")
1043
+ state.reportingStack = new Error("Operation outcome reported here").stack ?? "";
1044
+ },
1045
+ step: async (stepName, stepFn) => {
1046
+ const started = Date.now();
1047
+ let status = "succeeded";
1048
+ try {
1049
+ return await stepFn();
1050
+ } catch (error) {
1051
+ status = "failed";
1052
+ throw error;
1053
+ } finally {
1054
+ if (!state.sealed && state.steps.length < 64)
1055
+ state.steps.push({
1056
+ name: String(safe({ name: stepName }).name).slice(0, 200),
1057
+ status,
1058
+ durationMs: Date.now() - started
1059
+ });
1060
+ }
1061
+ }
1062
+ };
1063
+ const run = () => local.run(state, async () => {
1064
+ let thrown;
1065
+ try {
1066
+ return await fn(operation);
1067
+ } catch (error) {
1068
+ state.outcome = "failed";
1069
+ thrown = error;
1070
+ throw error;
1071
+ } finally {
1072
+ trace.getActiveSpan()?.setAttributes({
1073
+ ...traceAttributes(state),
1074
+ "autter.operation.outcome": state.outcome
1075
+ });
1076
+ if (state.outcome === "failed" && !thrown)
1077
+ config?.reportOutcome(
1078
+ state.name,
1079
+ state.message ?? "Operation failed",
1080
+ {
1081
+ ...traceAttributes(state),
1082
+ "autter.outcome.stack": state.reportingStack ?? ""
1083
+ }
1084
+ );
1085
+ emit(
1086
+ state.outcome === "failed" ? "error" : "info",
1087
+ state.message ?? `${state.name}: ${state.outcome}`,
1088
+ {
1089
+ ...operationAttributes(state),
1090
+ ...thrown instanceof Error ? {
1091
+ "exception.type": thrown.name,
1092
+ "exception.stacktrace": thrown.stack ?? ""
1093
+ } : {}
1094
+ },
1095
+ {
1096
+ "autter.event.type": "operation",
1097
+ "autter.operation.outcome": state.outcome,
1098
+ "autter.operation.duration_ms": Date.now() - state.startedAt,
1099
+ "autter.operation.steps": state.steps
1100
+ }
1101
+ );
1102
+ state.sealed = true;
1103
+ }
1104
+ });
1105
+ return config ? config.run(state.name, run, traceAttributes(state)) : run();
1106
+ }
1107
+
724
1108
  // src/server.ts
725
1109
  var LLM_MARKER_ATTRIBUTES = [
726
1110
  "gen_ai.operation.name",
@@ -849,7 +1233,7 @@ var ErrorTraceRetentionProcessor = class {
849
1233
  }
850
1234
  /** Promote the active trace, if any — called on captured errors. */
851
1235
  retainActiveTrace() {
852
- const active2 = trace.getActiveSpan()?.spanContext();
1236
+ const active2 = trace2.getActiveSpan()?.spanContext();
853
1237
  if (!active2 || (active2.traceFlags & TraceFlags.SAMPLED) !== 0) return;
854
1238
  const entry = this.entryFor(active2.traceId);
855
1239
  if (entry.retained) return;
@@ -952,7 +1336,7 @@ async function runLlmSpan(tracer, info, fn) {
952
1336
  };
953
1337
  try {
954
1338
  const result = await context.with(
955
- trace.setSpan(context.active(), span),
1339
+ trace2.setSpan(context.active(), span),
956
1340
  () => fn(handle)
957
1341
  );
958
1342
  span.setStatus({ code: SpanStatusCode.OK });
@@ -1001,7 +1385,7 @@ async function runWithSpan(tracer, name, fn, attributes) {
1001
1385
  const span = tracer.startSpan(name, { attributes });
1002
1386
  try {
1003
1387
  const result = await context.with(
1004
- trace.setSpan(context.active(), span),
1388
+ trace2.setSpan(context.active(), span),
1005
1389
  fn
1006
1390
  );
1007
1391
  span.setStatus({ code: SpanStatusCode.OK });
@@ -1053,7 +1437,7 @@ function initAutterServer(options) {
1053
1437
  activeRedactor = makeRedactor(options.redactAttributes ?? true);
1054
1438
  const resource = new Resource({
1055
1439
  [ATTR_SERVICE_NAME]: options.service,
1056
- "service.instance.id": (options.instanceId || process.env.AUTTER_RUNTIME_INSTANCE_ID || randomUUID()).slice(0, 128),
1440
+ "service.instance.id": (options.instanceId || process.env.AUTTER_RUNTIME_INSTANCE_ID || randomUUID2()).slice(0, 128),
1057
1441
  ...options.release ? { [ATTR_SERVICE_VERSION]: options.release } : {},
1058
1442
  "deployment.environment": environment,
1059
1443
  // Tells the ingester request metrics arrive on the metrics pipe, so
@@ -1140,7 +1524,7 @@ function initAutterServer(options) {
1140
1524
  const isError = error instanceof Error;
1141
1525
  const message = isError ? error.message : String(error);
1142
1526
  const redacted = activeRedactor(attributes);
1143
- const activeSpan = trace.getActiveSpan();
1527
+ const activeSpan = trace2.getActiveSpan();
1144
1528
  const reuseActive = activeSpan?.isRecording() === true;
1145
1529
  const span = reuseActive ? activeSpan : errorTracer.startSpan(isError ? error.name : "Error");
1146
1530
  span.setAttributes({ "autter.severity": "error", ...redacted });
@@ -1168,20 +1552,20 @@ function initAutterServer(options) {
1168
1552
  span.setStatus({ code: SpanStatusCode.ERROR, message });
1169
1553
  if (!reuseActive) span.end();
1170
1554
  }
1171
- function captureMessage2(message, severity = "warning", attributes) {
1172
- if (severity === "error" || severity === "fatal") {
1555
+ function captureMessage2(message, severity2 = "warning", attributes) {
1556
+ if (severity2 === "error" || severity2 === "fatal") {
1173
1557
  errorTraceBuffer?.retainActiveTrace();
1174
1558
  }
1175
1559
  telemetryStats.markCaptured();
1176
1560
  const stack = new Error().stack?.split("\n").filter((line, i) => i === 0 || !line.includes("captureMessage")).join("\n");
1177
1561
  const span = errorTracer.startSpan("Message", {
1178
- attributes: { "autter.severity": severity, ...activeRedactor(attributes) }
1562
+ attributes: { "autter.severity": severity2, ...activeRedactor(attributes) }
1179
1563
  });
1180
1564
  span.addEvent("exception", {
1181
1565
  "exception.type": "Message",
1182
1566
  "exception.message": message,
1183
1567
  ...stack ? { "exception.stacktrace": stack } : {},
1184
- "autter.severity": severity
1568
+ "autter.severity": severity2
1185
1569
  });
1186
1570
  span.setStatus({ code: SpanStatusCode.ERROR, message });
1187
1571
  span.end();
@@ -1189,7 +1573,7 @@ function initAutterServer(options) {
1189
1573
  function reportOutcome2(name, message, attributes) {
1190
1574
  errorTraceBuffer?.retainActiveTrace();
1191
1575
  telemetryStats.markCaptured();
1192
- const activeSpan = trace.getActiveSpan();
1576
+ const activeSpan = trace2.getActiveSpan();
1193
1577
  const reuseActive = activeSpan?.isRecording() === true;
1194
1578
  const span = reuseActive ? activeSpan : errorTracer.startSpan("OutcomeFailure");
1195
1579
  const safeOutcome = activeRedactor({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
@@ -1204,6 +1588,17 @@ function initAutterServer(options) {
1204
1588
  span.setStatus({ code: SpanStatusCode.ERROR, message: safeMessage });
1205
1589
  if (!reuseActive) span.end();
1206
1590
  }
1591
+ configureRuntimeLogger({
1592
+ endpoint,
1593
+ apiKey: options.apiKey,
1594
+ service: options.service,
1595
+ environment,
1596
+ release: options.release,
1597
+ options: options.logging,
1598
+ redact: activeRedactor,
1599
+ run: (name, fn, attributes) => runWithSpan(processTracer, name, fn, activeRedactor(attributes)),
1600
+ reportOutcome: reportOutcome2
1601
+ });
1207
1602
  if (options.captureGlobalErrors !== false) {
1208
1603
  process.on("uncaughtExceptionMonitor", (err) => {
1209
1604
  captureException2(err, { "autter.unhandled": true });
@@ -1214,6 +1609,7 @@ function initAutterServer(options) {
1214
1609
  const flushTarget = {
1215
1610
  forceFlush: async () => {
1216
1611
  const results = await Promise.allSettled([
1612
+ flushRuntimeLogs(),
1217
1613
  Promise.resolve().then(() => alwaysOnProvider.forceFlush()),
1218
1614
  Promise.resolve().then(() => mainSpanProcessor.forceFlush()),
1219
1615
  ...errorTraceBuffer ? [Promise.resolve().then(() => errorTraceBuffer.forceFlush())] : [],
@@ -1240,6 +1636,7 @@ function initAutterServer(options) {
1240
1636
  withLlmCall: (info, fn) => runLlmSpan(llmTracer, info, fn),
1241
1637
  trackLlmCall: (call) => recordLlmCall(llmTracer, call),
1242
1638
  shutdown: async () => {
1639
+ const logShutdown = await Promise.allSettled([shutdownRuntimeLogger()]);
1243
1640
  stopMemoryMetrics?.();
1244
1641
  active = null;
1245
1642
  activeAlwaysOnProvider = null;
@@ -1248,6 +1645,7 @@ function initAutterServer(options) {
1248
1645
  unregisterFlushTargets();
1249
1646
  telemetryStats.markAllFlushed();
1250
1647
  await Promise.allSettled([alwaysOnProvider.shutdown(), sdk.shutdown()]);
1648
+ if (logShutdown[0]?.status === "rejected") throw logShutdown[0].reason;
1251
1649
  }
1252
1650
  };
1253
1651
  active = server;
@@ -1259,7 +1657,7 @@ function captureException(error, attributes) {
1259
1657
  active.captureException(error, attributes);
1260
1658
  return;
1261
1659
  }
1262
- const span = trace.getTracer("autter-errors").startSpan(error instanceof Error ? error.name : "Error", { attributes });
1660
+ const span = trace2.getTracer("autter-errors").startSpan(error instanceof Error ? error.name : "Error", { attributes });
1263
1661
  if (error instanceof Error) span.recordException(error);
1264
1662
  span.setStatus({
1265
1663
  code: SpanStatusCode.ERROR,
@@ -1269,38 +1667,38 @@ function captureException(error, attributes) {
1269
1667
  }
1270
1668
  function withProcessSpan(name, fn, attributes) {
1271
1669
  if (active) return active.withProcessSpan(name, fn, attributes);
1272
- return runWithSpan(trace.getTracer("autter-processes"), name, fn, attributes);
1670
+ return runWithSpan(trace2.getTracer("autter-processes"), name, fn, attributes);
1273
1671
  }
1274
1672
  function withLlmCall(info, fn) {
1275
1673
  if (active) return active.withLlmCall(info, fn);
1276
- return runLlmSpan(trace.getTracer("autter-llm"), info, fn);
1674
+ return runLlmSpan(trace2.getTracer("autter-llm"), info, fn);
1277
1675
  }
1278
1676
  function trackLlmCall(call) {
1279
1677
  if (active) {
1280
1678
  active.trackLlmCall(call);
1281
1679
  return;
1282
1680
  }
1283
- recordLlmCall(trace.getTracer("autter-llm"), call);
1681
+ recordLlmCall(trace2.getTracer("autter-llm"), call);
1284
1682
  }
1285
- function captureMessage(message, severity = "warning", attributes) {
1683
+ function captureMessage(message, severity2 = "warning", attributes) {
1286
1684
  if (active) {
1287
- active.captureMessage(message, severity, attributes);
1685
+ active.captureMessage(message, severity2, attributes);
1288
1686
  return;
1289
1687
  }
1290
- const span = trace.getTracer("autter-errors").startSpan("Message", {
1291
- attributes: { "autter.severity": severity, ...attributes }
1688
+ const span = trace2.getTracer("autter-errors").startSpan("Message", {
1689
+ attributes: { "autter.severity": severity2, ...attributes }
1292
1690
  });
1293
1691
  span.addEvent("exception", {
1294
1692
  "exception.type": "Message",
1295
1693
  "exception.message": message,
1296
- "autter.severity": severity
1694
+ "autter.severity": severity2
1297
1695
  });
1298
1696
  span.setStatus({ code: SpanStatusCode.ERROR, message });
1299
1697
  span.end();
1300
1698
  }
1301
1699
  function reportOutcome(name, message, attributes) {
1302
1700
  if (active) return active.reportOutcome(name, message, attributes);
1303
- const span = trace.getTracer("autter-outcomes").startSpan("OutcomeFailure");
1701
+ const span = trace2.getTracer("autter-outcomes").startSpan("OutcomeFailure");
1304
1702
  const safeOutcome = redactAttributes({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
1305
1703
  const safeName = String(safeOutcome["autter.outcome.name"] ?? "operation");
1306
1704
  const safeMessage = String(safeOutcome["autter.outcome.message"] ?? "failed");
@@ -1317,11 +1715,11 @@ function makeSafeCapture(options) {
1317
1715
  const clean = (attributes) => attributes ? { ...redactAttributes(attributes, options) } : void 0;
1318
1716
  return {
1319
1717
  captureException: (error, attributes) => captureException(error, clean(attributes)),
1320
- captureMessage: (message, severity, attributes) => captureMessage(message, severity ?? "warning", clean(attributes))
1718
+ captureMessage: (message, severity2, attributes) => captureMessage(message, severity2 ?? "warning", clean(attributes))
1321
1719
  };
1322
1720
  }
1323
1721
  function alwaysOnTracer(name) {
1324
- return activeAlwaysOnProvider ? activeAlwaysOnProvider.getTracer(name) : trace.getTracer(name);
1722
+ return activeAlwaysOnProvider ? activeAlwaysOnProvider.getTracer(name) : trace2.getTracer(name);
1325
1723
  }
1326
1724
  function autterLlmTracer() {
1327
1725
  return alwaysOnTracer("autter-llm");
@@ -1341,7 +1739,7 @@ async function emitLlmSelftestTrace() {
1341
1739
  attributes: { "autter.selftest": true }
1342
1740
  },
1343
1741
  (llm) => {
1344
- traceId = trace.getActiveSpan()?.spanContext().traceId ?? "";
1742
+ traceId = trace2.getActiveSpan()?.spanContext().traceId ?? "";
1345
1743
  llm.setUsage({ inputTokens: 1, outputTokens: 1 });
1346
1744
  llm.setCost(0);
1347
1745
  }
@@ -1459,9 +1857,9 @@ function wrapStream(stream, span) {
1459
1857
  throw err;
1460
1858
  }
1461
1859
  },
1462
- async return(value2) {
1860
+ async return(value3) {
1463
1861
  finish();
1464
- return inner.return ? inner.return(value2) : { done: true, value: void 0 };
1862
+ return inner.return ? inner.return(value3) : { done: true, value: void 0 };
1465
1863
  },
1466
1864
  async throw(err) {
1467
1865
  finish(err);
@@ -1471,8 +1869,8 @@ function wrapStream(stream, span) {
1471
1869
  };
1472
1870
  };
1473
1871
  }
1474
- const value = Reflect.get(target, prop, receiver);
1475
- return typeof value === "function" ? value.bind(target) : value;
1872
+ const value2 = Reflect.get(target, prop, receiver);
1873
+ return typeof value2 === "function" ? value2.bind(target) : value2;
1476
1874
  }
1477
1875
  });
1478
1876
  }
@@ -1534,30 +1932,30 @@ function instrumentLlmClient(client, options) {
1534
1932
  if (cached) return cached;
1535
1933
  const proxy = new Proxy(target, {
1536
1934
  get(t, prop, receiver) {
1537
- const value = Reflect.get(t, prop, receiver);
1538
- if (typeof prop === "symbol") return value;
1935
+ const value2 = Reflect.get(t, prop, receiver);
1936
+ if (typeof prop === "symbol") return value2;
1539
1937
  const propPath = path ? `${path}.${prop}` : prop;
1540
- if (typeof value === "function") {
1938
+ if (typeof value2 === "function") {
1541
1939
  if (CALL_METHODS.has(prop)) {
1542
1940
  return (...args) => {
1543
1941
  const first = args[0];
1544
1942
  if (first && typeof first === "object" && typeof first.model === "string") {
1545
1943
  return traceCall(
1546
- value,
1944
+ value2,
1547
1945
  t,
1548
1946
  propPath,
1549
1947
  args
1550
1948
  );
1551
1949
  }
1552
- return value.apply(t, args);
1950
+ return value2.apply(t, args);
1553
1951
  };
1554
1952
  }
1555
- return value.bind(t);
1953
+ return value2.bind(t);
1556
1954
  }
1557
- if (value !== null && typeof value === "object" && !(value instanceof Promise)) {
1558
- return deepProxy(value, propPath);
1955
+ if (value2 !== null && typeof value2 === "object" && !(value2 instanceof Promise)) {
1956
+ return deepProxy(value2, propPath);
1559
1957
  }
1560
- return value;
1958
+ return value2;
1561
1959
  }
1562
1960
  });
1563
1961
  proxied.set(target, proxy);
@@ -1614,16 +2012,21 @@ export {
1614
2012
  captureMessage,
1615
2013
  createBrowserRelayFetchHandler,
1616
2014
  createBrowserRelayHandler,
2015
+ createRuntimeLogger,
1617
2016
  emitLlmSelftestTrace,
2017
+ flushRuntimeLogs,
1618
2018
  initAutterServer,
1619
2019
  installAutterAutoFlush,
1620
2020
  instrumentLlmClient,
1621
2021
  makeSafeCapture,
1622
2022
  redactAttributes,
1623
2023
  reportOutcome,
2024
+ runtimeLogStats,
2025
+ runtimeLogger,
1624
2026
  sanitizeBrowserPayload,
1625
2027
  startCaughtExceptionSampler,
1626
2028
  trackLlmCall,
1627
2029
  withLlmCall,
1628
- withProcessSpan
2030
+ withProcessSpan,
2031
+ withRuntimeOperation
1629
2032
  };