autotel 2.26.0 → 2.26.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/attribute-redacting-processor.cjs +14 -6
  2. package/dist/attribute-redacting-processor.d.cts +63 -1
  3. package/dist/attribute-redacting-processor.d.ts +63 -1
  4. package/dist/attribute-redacting-processor.js +1 -1
  5. package/dist/attributes.cjs +21 -21
  6. package/dist/attributes.js +2 -2
  7. package/dist/auto.cjs +8 -8
  8. package/dist/auto.js +6 -6
  9. package/dist/{chunk-RUD7KS4R.js → chunk-3SDILILG.js} +3 -3
  10. package/dist/{chunk-RUD7KS4R.js.map → chunk-3SDILILG.js.map} +1 -1
  11. package/dist/{chunk-B33XPEKY.js → chunk-55ER2KD5.js} +4 -4
  12. package/dist/chunk-55ER2KD5.js.map +1 -0
  13. package/dist/{chunk-UJJPTSEI.cjs → chunk-563EL6O6.cjs} +81 -14
  14. package/dist/chunk-563EL6O6.cjs.map +1 -0
  15. package/dist/{chunk-TS7IHIRW.cjs → chunk-6YGUN7IY.cjs} +5 -5
  16. package/dist/{chunk-TS7IHIRW.cjs.map → chunk-6YGUN7IY.cjs.map} +1 -1
  17. package/dist/{chunk-XDKK53OL.js → chunk-A4E5AQFK.js} +3 -3
  18. package/dist/{chunk-XDKK53OL.js.map → chunk-A4E5AQFK.js.map} +1 -1
  19. package/dist/{chunk-WAB4CHBU.js → chunk-BJ2XPN77.js} +3 -3
  20. package/dist/{chunk-WAB4CHBU.js.map → chunk-BJ2XPN77.js.map} +1 -1
  21. package/dist/{chunk-KZEC4CHV.cjs → chunk-CEAQK2QY.cjs} +5 -5
  22. package/dist/{chunk-KZEC4CHV.cjs.map → chunk-CEAQK2QY.cjs.map} +1 -1
  23. package/dist/chunk-CMNGGTQL.cjs +349 -0
  24. package/dist/chunk-CMNGGTQL.cjs.map +1 -0
  25. package/dist/{chunk-VYA6QDNA.js → chunk-DPSA4QLA.js} +4 -2
  26. package/dist/chunk-DPSA4QLA.js.map +1 -0
  27. package/dist/{chunk-M4US3P4K.js → chunk-ER43K7ES.js} +3 -3
  28. package/dist/{chunk-M4US3P4K.js.map → chunk-ER43K7ES.js.map} +1 -1
  29. package/dist/{chunk-AZ24DJAG.cjs → chunk-FU6R566Y.cjs} +4 -4
  30. package/dist/chunk-FU6R566Y.cjs.map +1 -0
  31. package/dist/{chunk-4PTCDOZY.js → chunk-HPUGKUMZ.js} +4 -4
  32. package/dist/{chunk-4PTCDOZY.js.map → chunk-HPUGKUMZ.js.map} +1 -1
  33. package/dist/{chunk-XRBP4RYL.cjs → chunk-JKIMEPI2.cjs} +4 -4
  34. package/dist/{chunk-XRBP4RYL.cjs.map → chunk-JKIMEPI2.cjs.map} +1 -1
  35. package/dist/{chunk-N344PVE5.cjs → chunk-OBWXM4NN.cjs} +9 -9
  36. package/dist/{chunk-N344PVE5.cjs.map → chunk-OBWXM4NN.cjs.map} +1 -1
  37. package/dist/{chunk-OFPZULMQ.cjs → chunk-OC6X2VIN.cjs} +8 -8
  38. package/dist/{chunk-OFPZULMQ.cjs.map → chunk-OC6X2VIN.cjs.map} +1 -1
  39. package/dist/{chunk-GTD3NXOS.js → chunk-QC5MNKVF.js} +4 -4
  40. package/dist/{chunk-GTD3NXOS.js.map → chunk-QC5MNKVF.js.map} +1 -1
  41. package/dist/chunk-TDNKIHKT.js +341 -0
  42. package/dist/chunk-TDNKIHKT.js.map +1 -0
  43. package/dist/{chunk-DGPUZ6TE.js → chunk-U54FTVFH.js} +3 -3
  44. package/dist/{chunk-DGPUZ6TE.js.map → chunk-U54FTVFH.js.map} +1 -1
  45. package/dist/{chunk-ZJ5GXCOT.cjs → chunk-UTZR7P7E.cjs} +36 -36
  46. package/dist/{chunk-ZJ5GXCOT.cjs.map → chunk-UTZR7P7E.cjs.map} +1 -1
  47. package/dist/{chunk-7FIGORWI.cjs → chunk-VH77IPJN.cjs} +4 -2
  48. package/dist/chunk-VH77IPJN.cjs.map +1 -0
  49. package/dist/{chunk-EXOXDI5A.js → chunk-W35FVJBC.js} +73 -8
  50. package/dist/chunk-W35FVJBC.js.map +1 -0
  51. package/dist/{chunk-II7GFVAF.cjs → chunk-WZOKY3PW.cjs} +13 -13
  52. package/dist/{chunk-II7GFVAF.cjs.map → chunk-WZOKY3PW.cjs.map} +1 -1
  53. package/dist/{chunk-CMADDTHY.cjs → chunk-YEVCD6DR.cjs} +7 -7
  54. package/dist/{chunk-CMADDTHY.cjs.map → chunk-YEVCD6DR.cjs.map} +1 -1
  55. package/dist/{chunk-RXFZKLRQ.js → chunk-YN7USLHW.js} +3 -3
  56. package/dist/{chunk-RXFZKLRQ.js.map → chunk-YN7USLHW.js.map} +1 -1
  57. package/dist/decorators.cjs +7 -7
  58. package/dist/decorators.js +7 -7
  59. package/dist/event.cjs +10 -10
  60. package/dist/event.js +7 -7
  61. package/dist/functional.cjs +14 -14
  62. package/dist/functional.js +7 -7
  63. package/dist/index.cjs +340 -97
  64. package/dist/index.cjs.map +1 -1
  65. package/dist/index.d.cts +205 -3
  66. package/dist/index.d.ts +205 -3
  67. package/dist/index.js +257 -33
  68. package/dist/index.js.map +1 -1
  69. package/dist/{init-QSj7X6zU.d.cts → init-CMuTaFAV.d.cts} +26 -1
  70. package/dist/{init-FiR_glVc.d.ts → init-D6JfWEjL.d.ts} +26 -1
  71. package/dist/instrumentation.cjs +14 -14
  72. package/dist/instrumentation.js +6 -6
  73. package/dist/logger.cjs +8 -8
  74. package/dist/logger.js +1 -1
  75. package/dist/messaging.cjs +11 -11
  76. package/dist/messaging.js +8 -8
  77. package/dist/metric.cjs +1 -1
  78. package/dist/metric.js +1 -1
  79. package/dist/sampling.cjs +15 -15
  80. package/dist/sampling.js +2 -2
  81. package/dist/semantic-helpers.cjs +12 -12
  82. package/dist/semantic-helpers.js +8 -8
  83. package/dist/tail-sampling-processor.cjs +4 -4
  84. package/dist/tail-sampling-processor.js +3 -3
  85. package/dist/testing.cjs +1 -1
  86. package/dist/testing.js +1 -1
  87. package/dist/webhook.cjs +9 -8
  88. package/dist/webhook.cjs.map +1 -1
  89. package/dist/webhook.js +8 -7
  90. package/dist/webhook.js.map +1 -1
  91. package/dist/workflow-distributed.cjs +9 -9
  92. package/dist/workflow-distributed.js +7 -7
  93. package/dist/workflow.cjs +12 -12
  94. package/dist/workflow.js +8 -8
  95. package/dist/yaml-config.cjs +6 -6
  96. package/dist/yaml-config.d.cts +1 -1
  97. package/dist/yaml-config.d.ts +1 -1
  98. package/dist/yaml-config.js +3 -3
  99. package/package.json +1 -1
  100. package/src/attribute-redacting-processor.test.ts +81 -16
  101. package/src/attribute-redacting-processor.ts +278 -24
  102. package/src/autotel-logger.ts +2 -2
  103. package/src/gen-ai-events.test.ts +135 -0
  104. package/src/gen-ai-events.ts +199 -0
  105. package/src/gen-ai-metrics.test.ts +96 -0
  106. package/src/gen-ai-metrics.ts +128 -0
  107. package/src/index.ts +28 -1
  108. package/src/init.ts +117 -2
  109. package/src/request-logger.test.ts +266 -1
  110. package/src/request-logger.ts +115 -16
  111. package/src/structured-error.ts +54 -1
  112. package/dist/chunk-7FIGORWI.cjs.map +0 -1
  113. package/dist/chunk-AZ24DJAG.cjs.map +0 -1
  114. package/dist/chunk-B33XPEKY.js.map +0 -1
  115. package/dist/chunk-ELW34S4C.cjs +0 -173
  116. package/dist/chunk-ELW34S4C.cjs.map +0 -1
  117. package/dist/chunk-EXOXDI5A.js.map +0 -1
  118. package/dist/chunk-SNINLBEE.js +0 -167
  119. package/dist/chunk-SNINLBEE.js.map +0 -1
  120. package/dist/chunk-UJJPTSEI.cjs.map +0 -1
  121. package/dist/chunk-VYA6QDNA.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as AutotelConfig, i as init } from './init-FiR_glVc.js';
1
+ export { A as AutotelConfig, i as init, a as isLoggerLocked, l as lockLogger } from './init-D6JfWEjL.js';
2
2
  import { Span, Context } from '@opentelemetry/api';
3
3
  export { Context, ROOT_CONTEXT, Span, SpanContext, SpanKind, Link as SpanLink, SpanStatusCode, TextMapGetter, TextMapSetter, Tracer, context, trace as otelTrace, propagation } from '@opentelemetry/api';
4
4
  import { SpanProcessor, ReadableSpan } from '@opentelemetry/sdk-trace-base';
@@ -18,6 +18,7 @@ export { AUTOTEL_SAMPLING_TAIL_EVALUATED, AUTOTEL_SAMPLING_TAIL_KEEP, AdaptiveSa
18
18
  export { Event, EventsOptions, getEvents, resetEvents } from './event.js';
19
19
  export { Metric, MetricsOptions, getMetrics, resetMetrics } from './metric.js';
20
20
  export { createCounter, createHistogram, createObservableGauge, createUpDownCounter, getMeter } from './metric-helpers.js';
21
+ import { ViewOptions } from '@opentelemetry/sdk-metrics';
21
22
  export { TraceContext as OtelTraceContext, createDeterministicTraceId, enrichWithTraceContext, finalizeSpan, flattenMetadata, getActiveContext, getActiveSpan, getTraceContext, getTracer, isTracing, resolveTraceUrl, runWithSpan } from './trace-helpers.js';
22
23
  export { getAutotelTracer, getAutotelTracerProvider, setAutotelTracerProvider } from './tracer-provider.js';
23
24
  export { DBConfig, HTTPConfig, LLMConfig, MessagingConfig, traceDB, traceHTTP, traceLLM, traceMessaging } from './semantic-helpers.js';
@@ -30,7 +31,6 @@ import '@opentelemetry/sdk-node';
30
31
  import '@opentelemetry/resources';
31
32
  import './logger.js';
32
33
  import 'pino';
33
- import '@opentelemetry/sdk-metrics';
34
34
  import '@opentelemetry/sdk-logs';
35
35
  import './processors.js';
36
36
  import 'node:async_hooks';
@@ -428,6 +428,7 @@ interface RequestLogger {
428
428
  error(error: Error | string, fields?: Record<string, unknown>): void;
429
429
  getContext(): Record<string, unknown>;
430
430
  emitNow(overrides?: Record<string, unknown>): RequestLogSnapshot;
431
+ fork(label: string, fn: () => void | Promise<void>): void;
431
432
  }
432
433
  interface RequestLogSnapshot {
433
434
  timestamp: string;
@@ -452,6 +453,8 @@ interface StructuredErrorInput {
452
453
  cause?: unknown;
453
454
  details?: Record<string, unknown>;
454
455
  name?: string;
456
+ /** Backend-only context. Omitted from toJSON() and never serialized to clients. */
457
+ internal?: Record<string, unknown>;
455
458
  }
456
459
  interface StructuredError extends Error {
457
460
  why?: string;
@@ -460,8 +463,11 @@ interface StructuredError extends Error {
460
463
  code?: string | number;
461
464
  status?: number;
462
465
  details?: Record<string, unknown>;
466
+ /** Backend-only context. Omitted from toJSON() and never serialized to clients. */
467
+ readonly internal?: Record<string, unknown>;
463
468
  }
464
469
  declare function createStructuredError(input: StructuredErrorInput): StructuredError;
470
+ declare function structuredErrorToJSON(error: StructuredError): Record<string, unknown>;
465
471
  declare function getStructuredErrorAttributes(error: Error): Record<string, AttributeValue>;
466
472
  declare function recordStructuredError(ctx: Pick<TraceContext, 'recordException' | 'setAttributes' | 'setStatus'>, error: Error): void;
467
473
 
@@ -486,4 +492,200 @@ declare function flattenToAttributes(fields: Record<string, unknown>, prefix?: s
486
492
  */
487
493
  declare function formatDuration(ms: number): string;
488
494
 
489
- export { AttributeRedactorConfig, AttributeRedactorPreset, BaggageSpanProcessor, type BaggageSpanProcessorOptions, EventAttributes, EventSubscriber, type OperationContext, type RequestLogSnapshot, type RequestLogger, type RequestLoggerOptions, type StringRedactor, type StructuredError, type StructuredErrorInput, TraceContext, createStringRedactor, createStructuredError, flattenToAttributes, flush, formatDuration, getEventQueue, getOperationContext, getRequestLogger, getStructuredErrorAttributes, recordStructuredError, runInOperationContext, runWithRequestContext, shutdown, toAttributeValue, track };
495
+ /**
496
+ * LLM-tuned histogram buckets.
497
+ *
498
+ * Default OpenTelemetry histogram buckets target HTTP latency (0ms–10s)
499
+ * and small counter values. LLM workloads have very different shapes:
500
+ *
501
+ * - **Duration**: single-token prompts can be fast (50ms), long
502
+ * generations and reasoning models can run for minutes. Default buckets
503
+ * crush everything above 10s into one bucket.
504
+ * - **Token usage**: heavily right-skewed. A single request can range
505
+ * from tens of tokens to the million-token context windows.
506
+ * - **Cost (USD)**: per-request values are tiny (fractions of a cent),
507
+ * so linear buckets waste resolution at the low end.
508
+ *
509
+ * This module exposes empirically-chosen bucket arrays and a View helper
510
+ * so users can apply them to their `MeterProvider` without knowing the
511
+ * exact instrument names emitted by OpenAI/Anthropic/Traceloop plugins.
512
+ *
513
+ * @example
514
+ * ```typescript
515
+ * import { NodeSDK } from '@opentelemetry/sdk-node';
516
+ * import { genAiMetricViews } from 'autotel';
517
+ *
518
+ * const sdk = new NodeSDK({
519
+ * serviceName: 'my-agent',
520
+ * views: [...genAiMetricViews()],
521
+ * });
522
+ * sdk.start();
523
+ * ```
524
+ */
525
+
526
+ /**
527
+ * Duration buckets for LLM operations, in **seconds**. Covers fast
528
+ * completions (50ms) through long-running reasoning jobs (5 min).
529
+ *
530
+ * Aligns with the OTel GenAI semantic conventions' published advice for
531
+ * `gen_ai.client.operation.duration`.
532
+ */
533
+ declare const GEN_AI_DURATION_BUCKETS_SECONDS: readonly number[];
534
+ /**
535
+ * Token-count buckets for prompt, completion, and total token histograms.
536
+ * Ranges from tiny prompts to million-token context windows.
537
+ *
538
+ * Aligns with the OTel GenAI semantic conventions' published advice for
539
+ * `gen_ai.client.token.usage`.
540
+ */
541
+ declare const GEN_AI_TOKEN_USAGE_BUCKETS: readonly number[];
542
+ /**
543
+ * USD cost buckets. Sub-cent resolution at the low end (fractions of a
544
+ * cent per small call) up to tens of dollars (batch jobs, Opus/o1 runs).
545
+ */
546
+ declare const GEN_AI_COST_USD_BUCKETS: readonly number[];
547
+ /**
548
+ * Instrument-level advice object for `createHistogram(name, advice)`.
549
+ * Use when you control the instrument creation (e.g. custom business
550
+ * LLM metrics); `genAiMetricViews()` is better when the metric comes
551
+ * from a third-party plugin.
552
+ */
553
+ declare function llmHistogramAdvice(kind: 'duration' | 'tokens' | 'cost'): {
554
+ advice: {
555
+ explicitBucketBoundaries: number[];
556
+ };
557
+ };
558
+ /**
559
+ * Returns `View`s that re-bucket the standard OTel GenAI histograms. Pass
560
+ * the result to your `MeterProvider`'s `views` option.
561
+ *
562
+ * Matches instrument names emitted by:
563
+ * - OpenTelemetry GenAI autoinstrumentation
564
+ * - OpenInference / OpenLLMetry (traceloop)
565
+ * - Arize Phoenix, LangSmith, etc. that follow the OTel spec
566
+ *
567
+ * Add more instrument patterns via the `extra` argument if you emit
568
+ * custom LLM metrics.
569
+ */
570
+ declare function genAiMetricViews(extra?: {
571
+ instrumentName: string;
572
+ kind: 'duration' | 'tokens' | 'cost';
573
+ }[]): ViewOptions[];
574
+
575
+ /**
576
+ * Span event helpers for LLM lifecycle, aligned with the OpenTelemetry
577
+ * GenAI semantic conventions.
578
+ *
579
+ * Span events are timestamped points within a span — they render as dots
580
+ * on the trace timeline in Jaeger / Tempo / Langfuse / Arize. Use them
581
+ * to mark lifecycle moments the span attributes alone can't express:
582
+ *
583
+ * - When the prompt was sent (vs. when the first token arrived)
584
+ * - When each retry attempt started, and why
585
+ * - When a streaming response produced its first token (TTFT)
586
+ * - When a tool was invoked
587
+ *
588
+ * Every helper pins the event name + attribute keys to the published
589
+ * spec so downstream tooling (autotel-mcp, Langfuse, vendor UIs) can
590
+ * render them consistently.
591
+ *
592
+ * @example
593
+ * ```typescript
594
+ * import { trace, recordPromptSent, recordResponseReceived, recordRetry } from 'autotel';
595
+ *
596
+ * export const chat = trace('chat', ctx => async (prompt: string) => {
597
+ * recordPromptSent(ctx, { model: 'gpt-4o', messageCount: 1 });
598
+ *
599
+ * for (let attempt = 1; attempt <= 3; attempt++) {
600
+ * try {
601
+ * const res = await openai.chat.completions.create({...});
602
+ * recordResponseReceived(ctx, {
603
+ * model: res.model,
604
+ * promptTokens: res.usage?.prompt_tokens,
605
+ * completionTokens: res.usage?.completion_tokens,
606
+ * finishReasons: res.choices.map(c => c.finish_reason),
607
+ * });
608
+ * return res;
609
+ * } catch (err) {
610
+ * recordRetry(ctx, { attempt, reason: 'rate_limit', delayMs: 500 });
611
+ * await sleep(500 * attempt);
612
+ * }
613
+ * }
614
+ * });
615
+ * ```
616
+ */
617
+
618
+ /** Attributes expected on a `gen_ai.prompt.sent` event. */
619
+ interface PromptSentEvent {
620
+ /** Model the caller intends to invoke (may differ from response model). */
621
+ model?: string;
622
+ /** Estimated input token count, when known before the call. */
623
+ promptTokens?: number;
624
+ /** Number of messages in a chat request (system + user + assistant). */
625
+ messageCount?: number;
626
+ /** Free-form operation kind — `chat` / `completion` / `embedding`. */
627
+ operation?: string;
628
+ }
629
+ /** Attributes expected on a `gen_ai.response.received` event. */
630
+ interface ResponseReceivedEvent {
631
+ /** Model the provider actually served (may be more specific than requested). */
632
+ model?: string;
633
+ promptTokens?: number;
634
+ completionTokens?: number;
635
+ totalTokens?: number;
636
+ /** `stop`, `length`, `content_filter`, `tool_calls`, etc. */
637
+ finishReasons?: string[];
638
+ }
639
+ /** Attributes expected on a `gen_ai.retry` event. */
640
+ interface RetryEvent {
641
+ attempt: number;
642
+ /** `rate_limit` | `timeout` | `provider_error` | custom label. */
643
+ reason?: string;
644
+ /** How long we'll wait before the next attempt. */
645
+ delayMs?: number;
646
+ /** HTTP status that triggered the retry, when applicable. */
647
+ statusCode?: number;
648
+ }
649
+ /** Attributes expected on a `gen_ai.tool.call` event. */
650
+ interface ToolCallEvent {
651
+ toolName: string;
652
+ /** Call identifier so responses can be correlated back to calls. */
653
+ toolCallId?: string;
654
+ /** Pre-serialised tool arguments; omit if sensitive. */
655
+ arguments?: string;
656
+ }
657
+ /** Attributes expected on a `gen_ai.stream.first_token` event. */
658
+ interface StreamFirstTokenEvent {
659
+ /** Tokens streamed so far, if the caller tracks that. */
660
+ tokensSoFar?: number;
661
+ }
662
+ /**
663
+ * Record that a prompt was dispatched to the provider. Typically called
664
+ * before `await provider.chat.completions.create(...)`.
665
+ */
666
+ declare function recordPromptSent(ctx: TraceContext, event?: PromptSentEvent): void;
667
+ /**
668
+ * Record a successful provider response. Call after the response arrives
669
+ * (for non-streaming) or after the stream completes.
670
+ */
671
+ declare function recordResponseReceived(ctx: TraceContext, event?: ResponseReceivedEvent): void;
672
+ /**
673
+ * Record a retry attempt on an LLM call. Call *before* sleeping for
674
+ * `delayMs` so the event timestamp accurately marks when the retry
675
+ * decision was made.
676
+ */
677
+ declare function recordRetry(ctx: TraceContext, event: RetryEvent): void;
678
+ /**
679
+ * Record a tool / function call made in the course of an agent step.
680
+ * Emits an event rather than a child span because many frameworks fire
681
+ * several tool calls within a single provider response.
682
+ */
683
+ declare function recordToolCall(ctx: TraceContext, event: ToolCallEvent): void;
684
+ /**
685
+ * Record the time-to-first-token for a streaming response. Pair with
686
+ * `recordResponseReceived` at the end so the span carries both the TTFT
687
+ * marker and the final usage numbers.
688
+ */
689
+ declare function recordStreamFirstToken(ctx: TraceContext, event?: StreamFirstTokenEvent): void;
690
+
691
+ export { AttributeRedactorConfig, AttributeRedactorPreset, BaggageSpanProcessor, type BaggageSpanProcessorOptions, EventAttributes, EventSubscriber, GEN_AI_COST_USD_BUCKETS, GEN_AI_DURATION_BUCKETS_SECONDS, GEN_AI_TOKEN_USAGE_BUCKETS, type OperationContext, type PromptSentEvent, type RequestLogSnapshot, type RequestLogger, type RequestLoggerOptions, type ResponseReceivedEvent, type RetryEvent, type StreamFirstTokenEvent, type StringRedactor, type StructuredError, type StructuredErrorInput, type ToolCallEvent, TraceContext, createStringRedactor, createStructuredError, flattenToAttributes, flush, formatDuration, genAiMetricViews, getEventQueue, getOperationContext, getRequestLogger, getStructuredErrorAttributes, llmHistogramAdvice, recordPromptSent, recordResponseReceived, recordRetry, recordStreamFirstToken, recordStructuredError, recordToolCall, runInOperationContext, runWithRequestContext, shutdown, structuredErrorToJSON, toAttributeValue, track };
package/dist/index.js CHANGED
@@ -1,45 +1,46 @@
1
1
  export { createDrainPipeline } from './chunk-KFOHQK7X.js';
2
- export { getCurrentWorkflowContext, isInWorkflow, traceStep, traceWorkflow } from './chunk-RXFZKLRQ.js';
3
- export { attrs, autoRedactPII, dbClient, httpClient, httpServer, identify, mergeAttrs, mergeServiceResource, request, safeSetAttributes, setDevice, setError, setException, setSession, setUser, validateAttribute } from './chunk-M4US3P4K.js';
2
+ export { getCurrentWorkflowContext, isInWorkflow, traceStep, traceWorkflow } from './chunk-YN7USLHW.js';
3
+ export { attrs, autoRedactPII, dbClient, httpClient, httpServer, identify, mergeAttrs, mergeServiceResource, request, safeSetAttributes, setDevice, setError, setException, setSession, setUser, validateAttribute } from './chunk-ER43K7ES.js';
4
4
  export { httpRequestHeaderAttribute, httpResponseHeaderAttribute } from './chunk-7552UTQW.js';
5
5
  export { HTTPAttributes, ServiceAttributes, URLAttributes } from './chunk-4A53YIAX.js';
6
6
  export { parseError } from './chunk-J7VGRIAJ.js';
7
- export { traceConsumer, traceProducer } from './chunk-GTD3NXOS.js';
7
+ export { traceConsumer, traceProducer } from './chunk-QC5MNKVF.js';
8
8
  export { BusinessBaggage, createSafeBaggageSchema } from './chunk-4IFSYQVX.js';
9
9
  import { resetMetrics } from './chunk-7SAWIN74.js';
10
10
  export { Metric, getMetrics, resetMetrics } from './chunk-7SAWIN74.js';
11
11
  import './chunk-5ZN622AO.js';
12
12
  export { createCounter, createHistogram, createObservableGauge, createUpDownCounter, getMeter } from './chunk-TQ5UWA7S.js';
13
- export { traceDB, traceHTTP, traceLLM, traceMessaging } from './chunk-DGPUZ6TE.js';
14
- import { getEventQueue, resetEventQueue } from './chunk-4PTCDOZY.js';
15
- export { ctx, getEventQueue, instrument, span, trace, track, withBaggage, withNewContext, withTracing } from './chunk-4PTCDOZY.js';
13
+ export { traceDB, traceHTTP, traceLLM, traceMessaging } from './chunk-U54FTVFH.js';
14
+ import { getEventQueue, resetEventQueue } from './chunk-HPUGKUMZ.js';
15
+ export { ctx, getEventQueue, instrument, span, trace, track, withBaggage, withNewContext, withTracing } from './chunk-HPUGKUMZ.js';
16
16
  export { createDeterministicTraceId, enrichWithTraceContext, finalizeSpan, flattenMetadata, getActiveContext, getActiveSpan, getTraceContext, getTracer, isTracing, resolveTraceUrl, runWithSpan } from './chunk-B3ZHLLMP.js';
17
- import { resetEvents } from './chunk-WAB4CHBU.js';
18
- export { Event, getEvents, resetEvents } from './chunk-WAB4CHBU.js';
17
+ import { resetEvents } from './chunk-BJ2XPN77.js';
18
+ export { Event, getEvents, resetEvents } from './chunk-BJ2XPN77.js';
19
19
  import './chunk-LITNXTTT.js';
20
20
  import './chunk-BZHG5IZ4.js';
21
21
  export { getOperationContext, runInOperationContext } from './chunk-WD4RP6IV.js';
22
22
  export { CORRELATION_ID_BAGGAGE_KEY, generateCorrelationId, getCorrelationId, getOrCreateCorrelationId, runWithCorrelationId, setCorrelationId, setCorrelationIdInBaggage } from './chunk-S4OFEXLA.js';
23
23
  import { createTraceContext } from './chunk-BBBWDIYQ.js';
24
24
  export { defineBaggageSchema } from './chunk-BBBWDIYQ.js';
25
- import { getLogger, getSdk, _closeEmbeddedDevtools } from './chunk-EXOXDI5A.js';
26
- export { BaggageSpanProcessor, createStringRedactor, init } from './chunk-EXOXDI5A.js';
27
- import './chunk-RUD7KS4R.js';
28
- import './chunk-XDKK53OL.js';
25
+ import { getLogger, getSdk, _closeEmbeddedDevtools } from './chunk-W35FVJBC.js';
26
+ export { BaggageSpanProcessor, createStringRedactor, init, isLoggerLocked, lockLogger } from './chunk-W35FVJBC.js';
27
+ import './chunk-3SDILILG.js';
28
+ import './chunk-A4E5AQFK.js';
29
29
  export { FilteringSpanProcessor } from './chunk-WGWSHJ2N.js';
30
30
  export { NORMALIZER_PATTERNS, NORMALIZER_PRESETS, SpanNameNormalizingProcessor } from './chunk-GYR5K654.js';
31
- export { AttributeRedactingProcessor, REDACTOR_PATTERNS, REDACTOR_PRESETS, createAttributeRedactor, createRedactedSpan } from './chunk-SNINLBEE.js';
31
+ export { AttributeRedactingProcessor, REDACTOR_PATTERNS, REDACTOR_PRESETS, createAttributeRedactor, createRedactedSpan } from './chunk-TDNKIHKT.js';
32
32
  import './chunk-6UQRVUN3.js';
33
33
  export { formatDuration } from './chunk-3QXBFGKP.js';
34
34
  import './chunk-33WTKH7X.js';
35
- export { AUTOTEL_SAMPLING_TAIL_EVALUATED, AUTOTEL_SAMPLING_TAIL_KEEP, AdaptiveSampler, AlwaysSampler, NeverSampler, RandomSampler, UserIdSampler, createLinkFromHeaders, extractLinksFromBatch, resolveSamplingPreset, samplingPresets } from './chunk-VYA6QDNA.js';
36
- import './chunk-B33XPEKY.js';
35
+ export { AUTOTEL_SAMPLING_TAIL_EVALUATED, AUTOTEL_SAMPLING_TAIL_KEEP, AdaptiveSampler, AlwaysSampler, NeverSampler, RandomSampler, UserIdSampler, createLinkFromHeaders, extractLinksFromBatch, resolveSamplingPreset, samplingPresets } from './chunk-DPSA4QLA.js';
36
+ import './chunk-55ER2KD5.js';
37
37
  import './chunk-J5QENANM.js';
38
38
  export { getAutotelTracer, getAutotelTracerProvider, setAutotelTracerProvider } from './chunk-HA2WBOGQ.js';
39
39
  import './chunk-DGUM43GV.js';
40
40
  import { AsyncLocalStorage } from 'async_hooks';
41
41
  import { SpanStatusCode, trace } from '@opentelemetry/api';
42
42
  export { ROOT_CONTEXT, SpanKind, SpanStatusCode, context, trace as otelTrace, propagation } from '@opentelemetry/api';
43
+ import { AggregationType } from '@opentelemetry/sdk-metrics';
43
44
 
44
45
  // src/shutdown.ts
45
46
  async function flush(options) {
@@ -228,6 +229,7 @@ function flattenToAttributes(fields, prefix = "") {
228
229
  }
229
230
 
230
231
  // src/structured-error.ts
232
+ var internalKey = /* @__PURE__ */ Symbol.for("autotel.error.internal");
231
233
  function createStructuredError(input) {
232
234
  const error = new Error(input.message, {
233
235
  cause: input.cause
@@ -239,6 +241,21 @@ function createStructuredError(input) {
239
241
  if (input.code !== void 0) error.code = input.code;
240
242
  if (input.status !== void 0) error.status = input.status;
241
243
  if (input.details !== void 0) error.details = input.details;
244
+ if (input.internal !== void 0) {
245
+ Object.defineProperty(error, internalKey, {
246
+ value: input.internal,
247
+ enumerable: false,
248
+ writable: false,
249
+ configurable: true
250
+ });
251
+ }
252
+ Object.defineProperty(error, "internal", {
253
+ get() {
254
+ return this[internalKey];
255
+ },
256
+ enumerable: false,
257
+ configurable: true
258
+ });
242
259
  error.toString = () => {
243
260
  const lines = [`${error.name}: ${error.message}`];
244
261
  if (error.why) lines.push(` Why: ${error.why}`);
@@ -246,11 +263,34 @@ function createStructuredError(input) {
246
263
  if (error.link) lines.push(` Link: ${error.link}`);
247
264
  if (error.code !== void 0) lines.push(` Code: ${error.code}`);
248
265
  if (error.status !== void 0) lines.push(` Status: ${error.status}`);
249
- if (error.cause) lines.push(` Caused by: ${error.cause}`);
266
+ if (error.cause) {
267
+ const cause = error.cause;
268
+ lines.push(` Caused by: ${cause.name}: ${cause.message}`);
269
+ }
250
270
  return lines.join("\n");
251
271
  };
252
272
  return error;
253
273
  }
274
+ function structuredErrorToJSON(error) {
275
+ const result = {
276
+ name: error.name,
277
+ message: error.message
278
+ };
279
+ if (error.status !== void 0) result.status = error.status;
280
+ if (error.why || error.fix || error.link) {
281
+ result.data = {
282
+ ...error.why && { why: error.why },
283
+ ...error.fix && { fix: error.fix },
284
+ ...error.link && { link: error.link }
285
+ };
286
+ }
287
+ if (error.code !== void 0) result.code = error.code;
288
+ if (error.details) result.details = error.details;
289
+ if (error.cause instanceof Error) {
290
+ result.cause = { name: error.cause.name, message: error.cause.message };
291
+ }
292
+ return result;
293
+ }
254
294
  function getStructuredErrorAttributes(error) {
255
295
  const structured = error;
256
296
  const attributes = {
@@ -285,6 +325,29 @@ function recordStructuredError(ctx2, error) {
285
325
  }
286
326
 
287
327
  // src/request-logger.ts
328
+ var POST_EMIT_FORK_HINT = "For intentional background work tied to this request, use log.fork('label', fn) when available.";
329
+ function warnPostEmit(method, detail) {
330
+ console.warn(
331
+ `[autotel] ${method} called after the wide event was emitted \u2014 ${detail} This data will not appear in observability. ${POST_EMIT_FORK_HINT}`
332
+ );
333
+ }
334
+ function mergeInto(target, source) {
335
+ for (const key in source) {
336
+ const sourceVal = source[key];
337
+ if (sourceVal === void 0) continue;
338
+ const targetVal = target[key];
339
+ if (sourceVal !== null && typeof sourceVal === "object" && !Array.isArray(sourceVal) && targetVal !== null && typeof targetVal === "object" && !Array.isArray(targetVal)) {
340
+ mergeInto(
341
+ targetVal,
342
+ sourceVal
343
+ );
344
+ } else if (Array.isArray(targetVal) && Array.isArray(sourceVal)) {
345
+ target[key] = [...targetVal, ...sourceVal];
346
+ } else {
347
+ target[key] = sourceVal;
348
+ }
349
+ }
350
+ }
288
351
  var requestContextStore = new AsyncLocalStorage();
289
352
  function runWithRequestContext(ctx2, fn) {
290
353
  return requestContextStore.run(ctx2, fn);
@@ -304,6 +367,8 @@ function resolveContext(ctx2) {
304
367
  function getRequestLogger(ctx2, options) {
305
368
  const activeContext = resolveContext(ctx2);
306
369
  let contextState = {};
370
+ let emitted = false;
371
+ let lastSnapshot = null;
307
372
  const addLogEvent = (level, message, fields) => {
308
373
  const attrs2 = fields ? flattenToAttributes(fields) : void 0;
309
374
  activeContext.addEvent(`log.${level}`, {
@@ -311,44 +376,51 @@ function getRequestLogger(ctx2, options) {
311
376
  ...attrs2
312
377
  });
313
378
  };
379
+ const sealCheck = (method, keys) => {
380
+ if (emitted) {
381
+ warnPostEmit(
382
+ method,
383
+ `Keys dropped: ${keys.length ? keys.join(", ") : "(empty)"}.`
384
+ );
385
+ }
386
+ };
314
387
  return {
315
388
  set(fields) {
316
- contextState = {
317
- ...contextState,
318
- ...fields
319
- };
389
+ sealCheck("log.set()", Object.keys(fields));
390
+ if (emitted) return;
391
+ mergeInto(contextState, fields);
320
392
  activeContext.setAttributes(flattenToAttributes(fields));
321
393
  },
322
394
  info(message, fields) {
395
+ const keys = fields ? ["message", ...Object.keys(fields).filter((k) => k !== "requestLogs")] : ["message"];
396
+ sealCheck("log.info()", keys);
397
+ if (emitted) return;
323
398
  addLogEvent("info", message, fields);
324
399
  if (fields) {
325
- contextState = {
326
- ...contextState,
327
- ...fields
328
- };
400
+ mergeInto(contextState, fields);
329
401
  activeContext.setAttributes(flattenToAttributes(fields));
330
402
  }
331
403
  },
332
404
  warn(message, fields) {
405
+ const keys = fields ? ["message", ...Object.keys(fields).filter((k) => k !== "requestLogs")] : ["message"];
406
+ sealCheck("log.warn()", keys);
407
+ if (emitted) return;
333
408
  addLogEvent("warn", message, fields);
334
409
  activeContext.setAttribute("autotel.log.level", "warn");
335
410
  if (fields) {
336
- contextState = {
337
- ...contextState,
338
- ...fields
339
- };
411
+ mergeInto(contextState, fields);
340
412
  activeContext.setAttributes(flattenToAttributes(fields));
341
413
  }
342
414
  },
343
415
  error(error, fields) {
416
+ const keys = fields ? [...Object.keys(fields), "error"] : ["error"];
417
+ sealCheck("log.error()", keys);
418
+ if (emitted) return;
344
419
  const err = typeof error === "string" ? new Error(error) : error;
345
420
  recordStructuredError(activeContext, err);
346
421
  addLogEvent("error", err.message, fields);
347
422
  if (fields) {
348
- contextState = {
349
- ...contextState,
350
- ...fields
351
- };
423
+ mergeInto(contextState, fields);
352
424
  activeContext.setAttributes(flattenToAttributes(fields));
353
425
  }
354
426
  activeContext.setAttribute("autotel.log.level", "error");
@@ -357,6 +429,10 @@ function getRequestLogger(ctx2, options) {
357
429
  return { ...contextState };
358
430
  },
359
431
  emitNow(overrides) {
432
+ if (emitted) {
433
+ warnPostEmit("log.emitNow()", "Ignoring duplicate emit.");
434
+ return lastSnapshot;
435
+ }
360
436
  const mergedContext = {
361
437
  ...contextState,
362
438
  ...overrides ?? {}
@@ -378,11 +454,159 @@ function getRequestLogger(ctx2, options) {
378
454
  console.warn("[autotel] request logger onEmit failed:", error);
379
455
  });
380
456
  }
457
+ emitted = true;
458
+ lastSnapshot = snapshot;
381
459
  return snapshot;
460
+ },
461
+ fork(label, fn) {
462
+ const parentRequestId = activeContext.correlationId;
463
+ if (typeof parentRequestId !== "string" || parentRequestId.length === 0) {
464
+ throw new Error(
465
+ "[autotel] log.fork() requires the parent logger to have a correlationId. Ensure the request was created by autotel middleware."
466
+ );
467
+ }
468
+ const tracer = trace.getTracer("autotel.request-logger");
469
+ void tracer.startActiveSpan(`request.fork:${label}`, (childSpan) => {
470
+ const childContext = {
471
+ ...createTraceContext(childSpan),
472
+ correlationId: crypto.randomUUID()
473
+ };
474
+ requestContextStore.run(childContext, () => {
475
+ const childLog = getRequestLogger(childContext);
476
+ childLog.set({
477
+ operation: label,
478
+ _parentCorrelationId: parentRequestId
479
+ });
480
+ void Promise.resolve().then(() => fn()).then(() => {
481
+ childLog.emitNow();
482
+ }).catch((err) => {
483
+ const error = err instanceof Error ? err : new Error(String(err));
484
+ childLog.error(error);
485
+ childLog.emitNow();
486
+ }).finally(() => {
487
+ childSpan.end();
488
+ });
489
+ });
490
+ });
382
491
  }
383
492
  };
384
493
  }
494
+ var GEN_AI_DURATION_BUCKETS_SECONDS = Object.freeze(
495
+ [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 20, 30, 60, 120, 300]
496
+ );
497
+ var GEN_AI_TOKEN_USAGE_BUCKETS = Object.freeze([
498
+ 1,
499
+ 4,
500
+ 16,
501
+ 64,
502
+ 256,
503
+ 1024,
504
+ 4096,
505
+ 16384,
506
+ 65536,
507
+ 262144,
508
+ 1048576,
509
+ 4194304
510
+ ]);
511
+ var GEN_AI_COST_USD_BUCKETS = Object.freeze([
512
+ 1e-5,
513
+ 1e-4,
514
+ 1e-3,
515
+ 5e-3,
516
+ 0.01,
517
+ 0.05,
518
+ 0.1,
519
+ 0.5,
520
+ 1,
521
+ 5,
522
+ 10,
523
+ 50
524
+ ]);
525
+ function llmHistogramAdvice(kind) {
526
+ const boundaries = kind === "duration" ? GEN_AI_DURATION_BUCKETS_SECONDS : kind === "tokens" ? GEN_AI_TOKEN_USAGE_BUCKETS : GEN_AI_COST_USD_BUCKETS;
527
+ return { advice: { explicitBucketBoundaries: [...boundaries] } };
528
+ }
529
+ function genAiMetricViews(extra = []) {
530
+ const defaults = [
531
+ { instrumentName: "gen_ai.client.operation.duration", kind: "duration" },
532
+ { instrumentName: "gen_ai.client.token.usage", kind: "tokens" },
533
+ // Autotel-emitted cost metric. No-op if you don't emit it.
534
+ { instrumentName: "gen_ai.client.cost.usd", kind: "cost" }
535
+ ];
536
+ return [...defaults, ...extra].map(
537
+ ({ instrumentName, kind }) => ({
538
+ instrumentName,
539
+ aggregation: {
540
+ type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM,
541
+ options: {
542
+ boundaries: kind === "duration" ? [...GEN_AI_DURATION_BUCKETS_SECONDS] : kind === "tokens" ? [...GEN_AI_TOKEN_USAGE_BUCKETS] : [...GEN_AI_COST_USD_BUCKETS]
543
+ }
544
+ }
545
+ })
546
+ );
547
+ }
548
+
549
+ // src/gen-ai-events.ts
550
+ function recordPromptSent(ctx2, event = {}) {
551
+ ctx2.addEvent("gen_ai.prompt.sent", buildPromptSentAttrs(event));
552
+ }
553
+ function recordResponseReceived(ctx2, event = {}) {
554
+ ctx2.addEvent("gen_ai.response.received", buildResponseAttrs(event));
555
+ }
556
+ function recordRetry(ctx2, event) {
557
+ ctx2.addEvent("gen_ai.retry", buildRetryAttrs(event));
558
+ }
559
+ function recordToolCall(ctx2, event) {
560
+ ctx2.addEvent("gen_ai.tool.call", buildToolCallAttrs(event));
561
+ }
562
+ function recordStreamFirstToken(ctx2, event = {}) {
563
+ ctx2.addEvent("gen_ai.stream.first_token", buildStreamFirstTokenAttrs(event));
564
+ }
565
+ function buildPromptSentAttrs(event) {
566
+ const attrs2 = {};
567
+ if (event.model) attrs2["gen_ai.request.model"] = event.model;
568
+ if (event.promptTokens !== void 0)
569
+ attrs2["gen_ai.usage.input_tokens"] = event.promptTokens;
570
+ if (event.messageCount !== void 0)
571
+ attrs2["gen_ai.request.message_count"] = event.messageCount;
572
+ if (event.operation) attrs2["gen_ai.operation.name"] = event.operation;
573
+ return attrs2;
574
+ }
575
+ function buildResponseAttrs(event) {
576
+ const attrs2 = {};
577
+ if (event.model) attrs2["gen_ai.response.model"] = event.model;
578
+ if (event.promptTokens !== void 0)
579
+ attrs2["gen_ai.usage.input_tokens"] = event.promptTokens;
580
+ if (event.completionTokens !== void 0)
581
+ attrs2["gen_ai.usage.output_tokens"] = event.completionTokens;
582
+ if (event.totalTokens !== void 0)
583
+ attrs2["gen_ai.usage.total_tokens"] = event.totalTokens;
584
+ if (event.finishReasons && event.finishReasons.length > 0) {
585
+ attrs2["gen_ai.response.finish_reasons"] = event.finishReasons.join(",");
586
+ }
587
+ return attrs2;
588
+ }
589
+ function buildRetryAttrs(event) {
590
+ const attrs2 = { "retry.attempt": event.attempt };
591
+ if (event.reason) attrs2["retry.reason"] = event.reason;
592
+ if (event.delayMs !== void 0) attrs2["retry.delay_ms"] = event.delayMs;
593
+ if (event.statusCode !== void 0)
594
+ attrs2["http.response.status_code"] = event.statusCode;
595
+ return attrs2;
596
+ }
597
+ function buildToolCallAttrs(event) {
598
+ const attrs2 = { "gen_ai.tool.name": event.toolName };
599
+ if (event.toolCallId) attrs2["gen_ai.tool.call.id"] = event.toolCallId;
600
+ if (event.arguments) attrs2["gen_ai.tool.arguments"] = event.arguments;
601
+ return attrs2;
602
+ }
603
+ function buildStreamFirstTokenAttrs(event) {
604
+ const attrs2 = {};
605
+ if (event.tokensSoFar !== void 0)
606
+ attrs2["gen_ai.stream.tokens_so_far"] = event.tokensSoFar;
607
+ return attrs2;
608
+ }
385
609
 
386
- export { createStructuredError, flattenToAttributes, flush, getRequestLogger, getStructuredErrorAttributes, recordStructuredError, runWithRequestContext, shutdown, toAttributeValue };
610
+ export { GEN_AI_COST_USD_BUCKETS, GEN_AI_DURATION_BUCKETS_SECONDS, GEN_AI_TOKEN_USAGE_BUCKETS, createStructuredError, flattenToAttributes, flush, genAiMetricViews, getRequestLogger, getStructuredErrorAttributes, llmHistogramAdvice, recordPromptSent, recordResponseReceived, recordRetry, recordStreamFirstToken, recordStructuredError, recordToolCall, runWithRequestContext, shutdown, structuredErrorToJSON, toAttributeValue };
387
611
  //# sourceMappingURL=index.js.map
388
612
  //# sourceMappingURL=index.js.map