@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.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";
@@ -32,6 +32,7 @@ var EVENT_TYPES = /* @__PURE__ */ new Set([
32
32
  "track_event",
33
33
  "outcome",
34
34
  "request_failure",
35
+ "csp_violation",
35
36
  "timing"
36
37
  ]);
37
38
  var SEVERITIES = /* @__PURE__ */ new Set(["fatal", "error", "warning", "info"]);
@@ -203,10 +204,10 @@ function createBrowserRelayHandler(opts) {
203
204
 
204
205
  // src/server.ts
205
206
  import { ServerResponse } from "http";
206
- import { randomUUID } from "crypto";
207
+ import { randomUUID as randomUUID2 } from "crypto";
207
208
  import {
208
209
  context,
209
- trace,
210
+ trace as trace2,
210
211
  SpanKind,
211
212
  SpanStatusCode,
212
213
  TraceFlags
@@ -309,13 +310,13 @@ function installAutterAutoFlush(options = {}) {
309
310
  const signals = options.signals ?? DEFAULT_SIGNALS;
310
311
  const flushOnUncaughtException = options.flushOnUncaughtException !== false;
311
312
  let disposed = false;
312
- let inFlight = null;
313
+ let inFlight2 = null;
313
314
  const doFlush = async () => {
314
315
  const targets = options.targets ?? activeFlushTargets();
315
316
  if (targets.length === 0) return true;
316
- let timer;
317
+ let timer2;
317
318
  const timedOut = new Promise((resolve) => {
318
- timer = setTimeout(() => resolve(false), timeoutMs);
319
+ timer2 = setTimeout(() => resolve(false), timeoutMs);
319
320
  });
320
321
  const drained = Promise.allSettled(
321
322
  targets.map(
@@ -328,7 +329,7 @@ function installAutterAutoFlush(options = {}) {
328
329
  try {
329
330
  ok = await Promise.race([drained, timedOut]);
330
331
  } finally {
331
- clearTimeout(timer);
332
+ clearTimeout(timer2);
332
333
  }
333
334
  if (ok) {
334
335
  telemetryStats.markAllFlushed();
@@ -344,13 +345,13 @@ function installAutterAutoFlush(options = {}) {
344
345
  };
345
346
  const flush = (reason = "exit") => {
346
347
  if (disposed) return Promise.resolve(false);
347
- if (!inFlight) {
348
- inFlight = doFlush().finally(() => {
349
- inFlight = null;
348
+ if (!inFlight2) {
349
+ inFlight2 = doFlush().finally(() => {
350
+ inFlight2 = null;
350
351
  });
351
352
  }
352
353
  if (isDebugEnabled()) debugLog(`auto-flush requested on ${reason}`);
353
- return inFlight;
354
+ return inFlight2;
354
355
  };
355
356
  const signalCounts = /* @__PURE__ */ new Map();
356
357
  let handlingUncaughtException = false;
@@ -489,8 +490,8 @@ function compile(options) {
489
490
  scrubEmailValues: options?.scrubEmailValues !== false
490
491
  };
491
492
  }
492
- function redactString(value, r) {
493
- let out = value.replace(r.privateKeyBlock, r.mask);
493
+ function redactString(value2, r) {
494
+ let out = value2.replace(r.privateKeyBlock, r.mask);
494
495
  for (const re of r.valuePatterns) {
495
496
  out = out.replace(re, r.mask);
496
497
  }
@@ -508,40 +509,40 @@ function canTraverse(depth, state) {
508
509
  state.remainingWork -= 1;
509
510
  return true;
510
511
  }
511
- function redactValue(value, r, state, depth) {
512
- if (typeof value === "string") return redactString(value, r);
512
+ function redactValue(value2, r, state, depth) {
513
+ if (typeof value2 === "string") return redactString(value2, r);
513
514
  let isArray = false;
514
515
  try {
515
- isArray = Array.isArray(value);
516
+ isArray = Array.isArray(value2);
516
517
  } catch {
517
518
  return r.mask;
518
519
  }
519
520
  if (isArray) {
520
- return redactArray(value, r, state, depth);
521
+ return redactArray(value2, r, state, depth);
521
522
  }
522
- if (typeof value === "object" && value !== null) {
523
- return redactObject(value, r, state, depth);
523
+ if (typeof value2 === "object" && value2 !== null) {
524
+ return redactObject(value2, r, state, depth);
524
525
  }
525
- return value;
526
+ return value2;
526
527
  }
527
- function redactArray(value, r, state, depth) {
528
- const existing = state.ancestors.get(value);
528
+ function redactArray(value2, r, state, depth) {
529
+ const existing = state.ancestors.get(value2);
529
530
  if (existing) return r.mask;
530
531
  if (!canTraverse(depth, state)) return r.mask;
531
532
  const out = [];
532
- state.ancestors.set(value, out);
533
+ state.ancestors.set(value2, out);
533
534
  let length;
534
535
  try {
535
- length = value.length;
536
+ length = value2.length;
536
537
  } catch {
537
- state.ancestors.delete(value);
538
+ state.ancestors.delete(value2);
538
539
  return r.mask;
539
540
  }
540
541
  const limit = Math.min(length, MAX_COLLECTION_ENTRIES);
541
542
  for (let i = 0; i < limit; i += 1) {
542
543
  let item;
543
544
  try {
544
- item = value[i];
545
+ item = value2[i];
545
546
  } catch {
546
547
  out.push(r.mask);
547
548
  continue;
@@ -551,21 +552,21 @@ function redactArray(value, r, state, depth) {
551
552
  if (length > limit) {
552
553
  out.push(r.mask);
553
554
  }
554
- state.ancestors.delete(value);
555
+ state.ancestors.delete(value2);
555
556
  return out;
556
557
  }
557
- function redactObject(value, r, state, depth) {
558
- const existing = state.ancestors.get(value);
558
+ function redactObject(value2, r, state, depth) {
559
+ const existing = state.ancestors.get(value2);
559
560
  if (existing) return r.mask;
560
561
  if (!canTraverse(depth, state)) return r.mask;
561
562
  const out = {};
562
- state.ancestors.set(value, out);
563
+ state.ancestors.set(value2, out);
563
564
  let count = 0;
564
565
  let truncated = false;
565
566
  try {
566
- for (const key in value) {
567
+ for (const key in value2) {
567
568
  if (!Object.prototype.propertyIsEnumerable.call(
568
- value,
569
+ value2,
569
570
  key
570
571
  )) {
571
572
  continue;
@@ -576,7 +577,7 @@ function redactObject(value, r, state, depth) {
576
577
  }
577
578
  let nestedValue;
578
579
  try {
579
- nestedValue = value[key];
580
+ nestedValue = value2[key];
580
581
  } catch {
581
582
  out[key] = r.mask;
582
583
  count += 1;
@@ -596,7 +597,7 @@ function redactObject(value, r, state, depth) {
596
597
  if (truncated) {
597
598
  out.__redaction_truncated__ = r.mask;
598
599
  }
599
- state.ancestors.delete(value);
600
+ state.ancestors.delete(value2);
600
601
  return out;
601
602
  }
602
603
  var USAGE_TOKEN_KEYS = /* @__PURE__ */ new Set([
@@ -613,10 +614,10 @@ var USAGE_TOKEN_KEYS = /* @__PURE__ */ new Set([
613
614
  "gen_ai.usage.total_tokens",
614
615
  "gen_ai.usage.token_count"
615
616
  ]);
616
- function isSensitiveKey(key, value, r) {
617
+ function isSensitiveKey(key, value2, r) {
617
618
  const lowered = key.toLowerCase();
618
619
  if (USAGE_TOKEN_KEYS.has(lowered)) {
619
- return !(typeof value === "number" && Number.isFinite(value) && value >= 0);
620
+ return !(typeof value2 === "number" && Number.isFinite(value2) && value2 >= 0);
620
621
  }
621
622
  return r.keyPatterns.some((re) => re.test(lowered));
622
623
  }
@@ -645,9 +646,9 @@ function redactWith(attributes, r) {
645
646
  truncated = true;
646
647
  break;
647
648
  }
648
- let value;
649
+ let value2;
649
650
  try {
650
- value = attributes[key];
651
+ value2 = attributes[key];
651
652
  } catch {
652
653
  out[key] = r.mask;
653
654
  count += 1;
@@ -656,8 +657,8 @@ function redactWith(attributes, r) {
656
657
  }
657
658
  count += 1;
658
659
  state.remainingWork -= 1;
659
- if (value === void 0) continue;
660
- 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);
661
662
  }
662
663
  } catch {
663
664
  truncated = true;
@@ -720,6 +721,390 @@ function startMemoryMetrics() {
720
721
  };
721
722
  }
722
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
+
723
1108
  // src/server.ts
724
1109
  var LLM_MARKER_ATTRIBUTES = [
725
1110
  "gen_ai.operation.name",
@@ -848,7 +1233,7 @@ var ErrorTraceRetentionProcessor = class {
848
1233
  }
849
1234
  /** Promote the active trace, if any — called on captured errors. */
850
1235
  retainActiveTrace() {
851
- const active2 = trace.getActiveSpan()?.spanContext();
1236
+ const active2 = trace2.getActiveSpan()?.spanContext();
852
1237
  if (!active2 || (active2.traceFlags & TraceFlags.SAMPLED) !== 0) return;
853
1238
  const entry = this.entryFor(active2.traceId);
854
1239
  if (entry.retained) return;
@@ -951,7 +1336,7 @@ async function runLlmSpan(tracer, info, fn) {
951
1336
  };
952
1337
  try {
953
1338
  const result = await context.with(
954
- trace.setSpan(context.active(), span),
1339
+ trace2.setSpan(context.active(), span),
955
1340
  () => fn(handle)
956
1341
  );
957
1342
  span.setStatus({ code: SpanStatusCode.OK });
@@ -1000,7 +1385,7 @@ async function runWithSpan(tracer, name, fn, attributes) {
1000
1385
  const span = tracer.startSpan(name, { attributes });
1001
1386
  try {
1002
1387
  const result = await context.with(
1003
- trace.setSpan(context.active(), span),
1388
+ trace2.setSpan(context.active(), span),
1004
1389
  fn
1005
1390
  );
1006
1391
  span.setStatus({ code: SpanStatusCode.OK });
@@ -1052,7 +1437,7 @@ function initAutterServer(options) {
1052
1437
  activeRedactor = makeRedactor(options.redactAttributes ?? true);
1053
1438
  const resource = new Resource({
1054
1439
  [ATTR_SERVICE_NAME]: options.service,
1055
- "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),
1056
1441
  ...options.release ? { [ATTR_SERVICE_VERSION]: options.release } : {},
1057
1442
  "deployment.environment": environment,
1058
1443
  // Tells the ingester request metrics arrive on the metrics pipe, so
@@ -1139,7 +1524,7 @@ function initAutterServer(options) {
1139
1524
  const isError = error instanceof Error;
1140
1525
  const message = isError ? error.message : String(error);
1141
1526
  const redacted = activeRedactor(attributes);
1142
- const activeSpan = trace.getActiveSpan();
1527
+ const activeSpan = trace2.getActiveSpan();
1143
1528
  const reuseActive = activeSpan?.isRecording() === true;
1144
1529
  const span = reuseActive ? activeSpan : errorTracer.startSpan(isError ? error.name : "Error");
1145
1530
  span.setAttributes({ "autter.severity": "error", ...redacted });
@@ -1167,20 +1552,20 @@ function initAutterServer(options) {
1167
1552
  span.setStatus({ code: SpanStatusCode.ERROR, message });
1168
1553
  if (!reuseActive) span.end();
1169
1554
  }
1170
- function captureMessage2(message, severity = "warning", attributes) {
1171
- if (severity === "error" || severity === "fatal") {
1555
+ function captureMessage2(message, severity2 = "warning", attributes) {
1556
+ if (severity2 === "error" || severity2 === "fatal") {
1172
1557
  errorTraceBuffer?.retainActiveTrace();
1173
1558
  }
1174
1559
  telemetryStats.markCaptured();
1175
1560
  const stack = new Error().stack?.split("\n").filter((line, i) => i === 0 || !line.includes("captureMessage")).join("\n");
1176
1561
  const span = errorTracer.startSpan("Message", {
1177
- attributes: { "autter.severity": severity, ...activeRedactor(attributes) }
1562
+ attributes: { "autter.severity": severity2, ...activeRedactor(attributes) }
1178
1563
  });
1179
1564
  span.addEvent("exception", {
1180
1565
  "exception.type": "Message",
1181
1566
  "exception.message": message,
1182
1567
  ...stack ? { "exception.stacktrace": stack } : {},
1183
- "autter.severity": severity
1568
+ "autter.severity": severity2
1184
1569
  });
1185
1570
  span.setStatus({ code: SpanStatusCode.ERROR, message });
1186
1571
  span.end();
@@ -1188,7 +1573,7 @@ function initAutterServer(options) {
1188
1573
  function reportOutcome2(name, message, attributes) {
1189
1574
  errorTraceBuffer?.retainActiveTrace();
1190
1575
  telemetryStats.markCaptured();
1191
- const activeSpan = trace.getActiveSpan();
1576
+ const activeSpan = trace2.getActiveSpan();
1192
1577
  const reuseActive = activeSpan?.isRecording() === true;
1193
1578
  const span = reuseActive ? activeSpan : errorTracer.startSpan("OutcomeFailure");
1194
1579
  const safeOutcome = activeRedactor({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
@@ -1203,6 +1588,17 @@ function initAutterServer(options) {
1203
1588
  span.setStatus({ code: SpanStatusCode.ERROR, message: safeMessage });
1204
1589
  if (!reuseActive) span.end();
1205
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
+ });
1206
1602
  if (options.captureGlobalErrors !== false) {
1207
1603
  process.on("uncaughtExceptionMonitor", (err) => {
1208
1604
  captureException2(err, { "autter.unhandled": true });
@@ -1213,6 +1609,7 @@ function initAutterServer(options) {
1213
1609
  const flushTarget = {
1214
1610
  forceFlush: async () => {
1215
1611
  const results = await Promise.allSettled([
1612
+ flushRuntimeLogs(),
1216
1613
  Promise.resolve().then(() => alwaysOnProvider.forceFlush()),
1217
1614
  Promise.resolve().then(() => mainSpanProcessor.forceFlush()),
1218
1615
  ...errorTraceBuffer ? [Promise.resolve().then(() => errorTraceBuffer.forceFlush())] : [],
@@ -1239,6 +1636,7 @@ function initAutterServer(options) {
1239
1636
  withLlmCall: (info, fn) => runLlmSpan(llmTracer, info, fn),
1240
1637
  trackLlmCall: (call) => recordLlmCall(llmTracer, call),
1241
1638
  shutdown: async () => {
1639
+ const logShutdown = await Promise.allSettled([shutdownRuntimeLogger()]);
1242
1640
  stopMemoryMetrics?.();
1243
1641
  active = null;
1244
1642
  activeAlwaysOnProvider = null;
@@ -1247,6 +1645,7 @@ function initAutterServer(options) {
1247
1645
  unregisterFlushTargets();
1248
1646
  telemetryStats.markAllFlushed();
1249
1647
  await Promise.allSettled([alwaysOnProvider.shutdown(), sdk.shutdown()]);
1648
+ if (logShutdown[0]?.status === "rejected") throw logShutdown[0].reason;
1250
1649
  }
1251
1650
  };
1252
1651
  active = server;
@@ -1258,7 +1657,7 @@ function captureException(error, attributes) {
1258
1657
  active.captureException(error, attributes);
1259
1658
  return;
1260
1659
  }
1261
- 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 });
1262
1661
  if (error instanceof Error) span.recordException(error);
1263
1662
  span.setStatus({
1264
1663
  code: SpanStatusCode.ERROR,
@@ -1268,38 +1667,38 @@ function captureException(error, attributes) {
1268
1667
  }
1269
1668
  function withProcessSpan(name, fn, attributes) {
1270
1669
  if (active) return active.withProcessSpan(name, fn, attributes);
1271
- return runWithSpan(trace.getTracer("autter-processes"), name, fn, attributes);
1670
+ return runWithSpan(trace2.getTracer("autter-processes"), name, fn, attributes);
1272
1671
  }
1273
1672
  function withLlmCall(info, fn) {
1274
1673
  if (active) return active.withLlmCall(info, fn);
1275
- return runLlmSpan(trace.getTracer("autter-llm"), info, fn);
1674
+ return runLlmSpan(trace2.getTracer("autter-llm"), info, fn);
1276
1675
  }
1277
1676
  function trackLlmCall(call) {
1278
1677
  if (active) {
1279
1678
  active.trackLlmCall(call);
1280
1679
  return;
1281
1680
  }
1282
- recordLlmCall(trace.getTracer("autter-llm"), call);
1681
+ recordLlmCall(trace2.getTracer("autter-llm"), call);
1283
1682
  }
1284
- function captureMessage(message, severity = "warning", attributes) {
1683
+ function captureMessage(message, severity2 = "warning", attributes) {
1285
1684
  if (active) {
1286
- active.captureMessage(message, severity, attributes);
1685
+ active.captureMessage(message, severity2, attributes);
1287
1686
  return;
1288
1687
  }
1289
- const span = trace.getTracer("autter-errors").startSpan("Message", {
1290
- attributes: { "autter.severity": severity, ...attributes }
1688
+ const span = trace2.getTracer("autter-errors").startSpan("Message", {
1689
+ attributes: { "autter.severity": severity2, ...attributes }
1291
1690
  });
1292
1691
  span.addEvent("exception", {
1293
1692
  "exception.type": "Message",
1294
1693
  "exception.message": message,
1295
- "autter.severity": severity
1694
+ "autter.severity": severity2
1296
1695
  });
1297
1696
  span.setStatus({ code: SpanStatusCode.ERROR, message });
1298
1697
  span.end();
1299
1698
  }
1300
1699
  function reportOutcome(name, message, attributes) {
1301
1700
  if (active) return active.reportOutcome(name, message, attributes);
1302
- const span = trace.getTracer("autter-outcomes").startSpan("OutcomeFailure");
1701
+ const span = trace2.getTracer("autter-outcomes").startSpan("OutcomeFailure");
1303
1702
  const safeOutcome = redactAttributes({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
1304
1703
  const safeName = String(safeOutcome["autter.outcome.name"] ?? "operation");
1305
1704
  const safeMessage = String(safeOutcome["autter.outcome.message"] ?? "failed");
@@ -1316,11 +1715,11 @@ function makeSafeCapture(options) {
1316
1715
  const clean = (attributes) => attributes ? { ...redactAttributes(attributes, options) } : void 0;
1317
1716
  return {
1318
1717
  captureException: (error, attributes) => captureException(error, clean(attributes)),
1319
- captureMessage: (message, severity, attributes) => captureMessage(message, severity ?? "warning", clean(attributes))
1718
+ captureMessage: (message, severity2, attributes) => captureMessage(message, severity2 ?? "warning", clean(attributes))
1320
1719
  };
1321
1720
  }
1322
1721
  function alwaysOnTracer(name) {
1323
- return activeAlwaysOnProvider ? activeAlwaysOnProvider.getTracer(name) : trace.getTracer(name);
1722
+ return activeAlwaysOnProvider ? activeAlwaysOnProvider.getTracer(name) : trace2.getTracer(name);
1324
1723
  }
1325
1724
  function autterLlmTracer() {
1326
1725
  return alwaysOnTracer("autter-llm");
@@ -1340,7 +1739,7 @@ async function emitLlmSelftestTrace() {
1340
1739
  attributes: { "autter.selftest": true }
1341
1740
  },
1342
1741
  (llm) => {
1343
- traceId = trace.getActiveSpan()?.spanContext().traceId ?? "";
1742
+ traceId = trace2.getActiveSpan()?.spanContext().traceId ?? "";
1344
1743
  llm.setUsage({ inputTokens: 1, outputTokens: 1 });
1345
1744
  llm.setCost(0);
1346
1745
  }
@@ -1458,9 +1857,9 @@ function wrapStream(stream, span) {
1458
1857
  throw err;
1459
1858
  }
1460
1859
  },
1461
- async return(value2) {
1860
+ async return(value3) {
1462
1861
  finish();
1463
- return inner.return ? inner.return(value2) : { done: true, value: void 0 };
1862
+ return inner.return ? inner.return(value3) : { done: true, value: void 0 };
1464
1863
  },
1465
1864
  async throw(err) {
1466
1865
  finish(err);
@@ -1470,8 +1869,8 @@ function wrapStream(stream, span) {
1470
1869
  };
1471
1870
  };
1472
1871
  }
1473
- const value = Reflect.get(target, prop, receiver);
1474
- 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;
1475
1874
  }
1476
1875
  });
1477
1876
  }
@@ -1533,30 +1932,30 @@ function instrumentLlmClient(client, options) {
1533
1932
  if (cached) return cached;
1534
1933
  const proxy = new Proxy(target, {
1535
1934
  get(t, prop, receiver) {
1536
- const value = Reflect.get(t, prop, receiver);
1537
- if (typeof prop === "symbol") return value;
1935
+ const value2 = Reflect.get(t, prop, receiver);
1936
+ if (typeof prop === "symbol") return value2;
1538
1937
  const propPath = path ? `${path}.${prop}` : prop;
1539
- if (typeof value === "function") {
1938
+ if (typeof value2 === "function") {
1540
1939
  if (CALL_METHODS.has(prop)) {
1541
1940
  return (...args) => {
1542
1941
  const first = args[0];
1543
1942
  if (first && typeof first === "object" && typeof first.model === "string") {
1544
1943
  return traceCall(
1545
- value,
1944
+ value2,
1546
1945
  t,
1547
1946
  propPath,
1548
1947
  args
1549
1948
  );
1550
1949
  }
1551
- return value.apply(t, args);
1950
+ return value2.apply(t, args);
1552
1951
  };
1553
1952
  }
1554
- return value.bind(t);
1953
+ return value2.bind(t);
1555
1954
  }
1556
- if (value !== null && typeof value === "object" && !(value instanceof Promise)) {
1557
- return deepProxy(value, propPath);
1955
+ if (value2 !== null && typeof value2 === "object" && !(value2 instanceof Promise)) {
1956
+ return deepProxy(value2, propPath);
1558
1957
  }
1559
- return value;
1958
+ return value2;
1560
1959
  }
1561
1960
  });
1562
1961
  proxied.set(target, proxy);
@@ -1613,16 +2012,21 @@ export {
1613
2012
  captureMessage,
1614
2013
  createBrowserRelayFetchHandler,
1615
2014
  createBrowserRelayHandler,
2015
+ createRuntimeLogger,
1616
2016
  emitLlmSelftestTrace,
2017
+ flushRuntimeLogs,
1617
2018
  initAutterServer,
1618
2019
  installAutterAutoFlush,
1619
2020
  instrumentLlmClient,
1620
2021
  makeSafeCapture,
1621
2022
  redactAttributes,
1622
2023
  reportOutcome,
2024
+ runtimeLogStats,
2025
+ runtimeLogger,
1623
2026
  sanitizeBrowserPayload,
1624
2027
  startCaughtExceptionSampler,
1625
2028
  trackLlmCall,
1626
2029
  withLlmCall,
1627
- withProcessSpan
2030
+ withProcessSpan,
2031
+ withRuntimeOperation
1628
2032
  };