@juspay/neurolink 12.59.0 → 13.0.1

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 (146) hide show
  1. package/CHANGELOG.md +3 -3
  2. package/dist/auth/anthropicOAuth.js +3 -3
  3. package/dist/auth/middleware/AuthMiddleware.d.ts +1 -1
  4. package/dist/auth/sessionManager.js +4 -4
  5. package/dist/auth/tokenStore.js +4 -4
  6. package/dist/browser/neurolink.min.js +474 -473
  7. package/dist/cli/commands/observability.d.ts +1 -6
  8. package/dist/cli/commands/observability.js +0 -106
  9. package/dist/cli/commands/proxy.js +9 -18
  10. package/dist/cli/commands/telemetry.d.ts +3 -13
  11. package/dist/cli/commands/telemetry.js +7 -354
  12. package/dist/cli/loop/session.js +1 -1
  13. package/dist/client/httpClient.js +1 -1
  14. package/dist/client/sseClient.js +1 -1
  15. package/dist/client/streamingClient.js +2 -2
  16. package/dist/client/wsClient.js +1 -1
  17. package/dist/constants/langfuse.d.ts +25 -0
  18. package/dist/constants/langfuse.js +38 -0
  19. package/dist/constants/telemetry.d.ts +20 -0
  20. package/dist/constants/telemetry.js +34 -0
  21. package/dist/core/baseProvider.d.ts +2 -1
  22. package/dist/core/baseProvider.js +4 -11
  23. package/dist/core/conversationMemoryManager.js +5 -8
  24. package/dist/core/factory.js +1 -1
  25. package/dist/core/geminiLoopAdapter.d.ts +8 -1
  26. package/dist/core/geminiLoopAdapter.js +25 -1
  27. package/dist/core/loopEngine.js +69 -40
  28. package/dist/core/modules/MessageBuilder.js +2 -2
  29. package/dist/core/modules/StreamHandler.js +2 -2
  30. package/dist/core/modules/TelemetryHandler.d.ts +2 -18
  31. package/dist/core/modules/TelemetryHandler.js +1 -66
  32. package/dist/core/modules/ToolsManager.js +40 -31
  33. package/dist/core/nativeGenerateLoop.js +78 -8
  34. package/dist/core/redisConversationMemoryManager.js +8 -15
  35. package/dist/core/toolExecutionGuards.js +1 -13
  36. package/dist/index.d.ts +5 -12
  37. package/dist/index.js +5 -37
  38. package/dist/mcp/batching/requestBatcher.js +1 -1
  39. package/dist/mcp/externalServerManager.js +3 -21
  40. package/dist/mcp/toolDiscoveryService.js +4 -16
  41. package/dist/mcp/toolRegistry.js +2 -5
  42. package/dist/memory/memoryRetrievalTools.js +1 -1
  43. package/dist/neurolink.d.ts +16 -27
  44. package/dist/neurolink.js +788 -792
  45. package/dist/observability/exporterSpanProcessor.d.ts +50 -0
  46. package/dist/observability/exporterSpanProcessor.js +181 -0
  47. package/dist/observability/exporters/langfuseExporter.d.ts +13 -34
  48. package/dist/observability/exporters/langfuseExporter.js +101 -154
  49. package/dist/observability/index.d.ts +1 -0
  50. package/dist/observability/index.js +1 -0
  51. package/dist/observability/langfuseMasking.d.ts +8 -0
  52. package/dist/observability/langfuseMasking.js +47 -0
  53. package/dist/observability/otlpEncoding.d.ts +20 -0
  54. package/dist/observability/otlpEncoding.js +85 -0
  55. package/dist/providers/amazonBedrock/client.js +142 -176
  56. package/dist/providers/amazonBedrock/loopAdapter.js +14 -1
  57. package/dist/providers/amazonSagemaker.js +118 -80
  58. package/dist/providers/anthropic/client.js +3 -52
  59. package/dist/providers/anthropic/loopAdapter.js +24 -1
  60. package/dist/providers/anthropic/rateLimitCapture.js +9 -12
  61. package/dist/providers/googleAiStudio/client.d.ts +4 -5
  62. package/dist/providers/googleAiStudio/client.js +38 -40
  63. package/dist/providers/googleVertex/client.d.ts +2 -7
  64. package/dist/providers/googleVertex/client.js +282 -378
  65. package/dist/providers/googleVertex/constants.js +2 -2
  66. package/dist/providers/litellm/client.d.ts +2 -9
  67. package/dist/providers/litellm/client.js +1 -63
  68. package/dist/providers/openAI/client.d.ts +1 -8
  69. package/dist/providers/openAI/client.js +0 -65
  70. package/dist/providers/openaiChatCompletionsBase.d.ts +1 -0
  71. package/dist/providers/openaiChatCompletionsBase.js +61 -4
  72. package/dist/providers/replicate.js +22 -4
  73. package/dist/proxy/codexUsage.js +7 -0
  74. package/dist/proxy/proxyFetch.js +19 -25
  75. package/dist/proxy/proxyTracer.d.ts +14 -7
  76. package/dist/proxy/proxyTracer.js +129 -83
  77. package/dist/proxy/proxyTranslationEngine.js +0 -1
  78. package/dist/proxy/requestLogger.d.ts +1 -1
  79. package/dist/proxy/requestLogger.js +39 -45
  80. package/dist/server/middleware/auth.d.ts +5 -1
  81. package/dist/server/middleware/auth.js +10 -3
  82. package/dist/server/middleware/common.js +1 -1
  83. package/dist/server/routes/agentRoutes.js +4 -4
  84. package/dist/server/routes/claudeProxyRoutes.js +99 -196
  85. package/dist/server/routes/codexProxyRoutes.js +621 -591
  86. package/dist/server/routes/healthRoutes.js +6 -6
  87. package/dist/server/routes/mcpRoutes.js +7 -7
  88. package/dist/server/routes/memoryRoutes.js +7 -7
  89. package/dist/server/routes/openApiRoutes.js +3 -3
  90. package/dist/server/routes/toolRoutes.js +5 -5
  91. package/dist/server/routes/transcribeRoutes.js +1 -1
  92. package/dist/server/utils/validation.d.ts +3 -3
  93. package/dist/services/server/ai/observability/instrumentation.d.ts +34 -201
  94. package/dist/services/server/ai/observability/instrumentation.js +220 -1267
  95. package/dist/services/server/ai/observability/langfuseSetup.d.ts +14 -0
  96. package/dist/services/server/ai/observability/langfuseSetup.js +55 -0
  97. package/dist/services/server/ai/observability/otlpSetup.d.ts +16 -0
  98. package/dist/services/server/ai/observability/otlpSetup.js +58 -0
  99. package/dist/session/globalSessionState.js +0 -11
  100. package/dist/telemetry/attributes.d.ts +23 -46
  101. package/dist/telemetry/attributes.js +76 -162
  102. package/dist/telemetry/chatMessages.d.ts +13 -0
  103. package/dist/telemetry/chatMessages.js +59 -0
  104. package/dist/telemetry/index.d.ts +5 -19
  105. package/dist/telemetry/index.js +5 -26
  106. package/dist/telemetry/observation.d.ts +37 -0
  107. package/dist/telemetry/observation.js +112 -0
  108. package/dist/telemetry/observationNames.d.ts +18 -0
  109. package/dist/telemetry/observationNames.js +24 -0
  110. package/dist/telemetry/spanSanitizer.d.ts +39 -0
  111. package/dist/telemetry/spanSanitizer.js +92 -0
  112. package/dist/telemetry/spanSerialization.d.ts +13 -0
  113. package/dist/telemetry/spanSerialization.js +46 -0
  114. package/dist/telemetry/tracers.d.ts +37 -18
  115. package/dist/telemetry/tracers.js +97 -19
  116. package/dist/telemetry/withSpan.d.ts +47 -25
  117. package/dist/telemetry/withSpan.js +158 -71
  118. package/dist/types/cli.d.ts +0 -11
  119. package/dist/types/decision.d.ts +3 -0
  120. package/dist/types/exporter.d.ts +54 -0
  121. package/dist/types/exporter.js +0 -4
  122. package/dist/types/generate.d.ts +4 -1
  123. package/dist/types/isolatedAgent.d.ts +5 -4
  124. package/dist/types/loopEngine.d.ts +18 -28
  125. package/dist/types/observability.d.ts +127 -160
  126. package/dist/types/providers.d.ts +15 -0
  127. package/dist/types/proxy.d.ts +41 -2
  128. package/dist/types/server.d.ts +9 -5
  129. package/dist/utils/anthropicTraceSanitizer.d.ts +2 -2
  130. package/dist/utils/anthropicTraceSanitizer.js +3 -1
  131. package/dist/utils/conversationMemory.js +4 -4
  132. package/dist/utils/fileDetector.js +1 -1
  133. package/dist/utils/messageBuilder.js +1 -1
  134. package/dist/utils/noOutputSentinel.d.ts +4 -16
  135. package/dist/utils/noOutputSentinel.js +12 -28
  136. package/dist/utils/providerRetry.js +4 -4
  137. package/dist/voice/livekit/brain.js +1 -1
  138. package/dist/voice/livekit/realtimeMemory.d.ts +1 -1
  139. package/dist/voice/livekit/realtimeMemory.js +66 -1
  140. package/dist/voice/livekit/realtimeVoiceAgent.js +1 -1
  141. package/docs-site/static/search-index.json +42 -64
  142. package/package.json +5 -4
  143. package/dist/telemetry/telemetryService.d.ts +0 -79
  144. package/dist/telemetry/telemetryService.js +0 -572
  145. package/dist/utils/observabilityHelpers.d.ts +0 -27
  146. package/dist/utils/observabilityHelpers.js +0 -47
@@ -1,7 +1,32 @@
1
- import { SpanKind, SpanStatusCode } from "@opentelemetry/api";
1
+ import { LangfuseOtelSpanAttributes } from "@langfuse/tracing";
2
+ import { context, SpanKind, SpanStatusCode, trace, } from "@opentelemetry/api";
3
+ import { isAbortError } from "../utils/errorHandling.js";
4
+ import { observe, observeGeneration } from "./observation.js";
5
+ import { generationName, stepSpanName } from "./observationNames.js";
6
+ import { tracers } from "./tracers.js";
7
+ function markFailure(span, error, expected = false) {
8
+ const message = error instanceof Error ? error.message : String(error);
9
+ if (isAbortError(error)) {
10
+ span.setAttributes({
11
+ [LangfuseOtelSpanAttributes.OBSERVATION_LEVEL]: "WARNING",
12
+ [LangfuseOtelSpanAttributes.OBSERVATION_STATUS_MESSAGE]: message,
13
+ });
14
+ return;
15
+ }
16
+ span.setStatus({ code: SpanStatusCode.ERROR, message });
17
+ if (!expected) {
18
+ span.setAttributes({
19
+ [LangfuseOtelSpanAttributes.OBSERVATION_LEVEL]: "ERROR",
20
+ [LangfuseOtelSpanAttributes.OBSERVATION_STATUS_MESSAGE]: message,
21
+ });
22
+ }
23
+ if (error instanceof Error) {
24
+ span.recordException(error);
25
+ }
26
+ }
2
27
  export async function withSpan(options, fn) {
3
- const { name, tracer, kind = SpanKind.INTERNAL, attributes } = options;
4
- return tracer.startActiveSpan(name, { kind }, async (span) => {
28
+ const { name, tracer, kind = SpanKind.INTERNAL, attributes, detached, } = options;
29
+ const run = async (span) => {
5
30
  if (attributes) {
6
31
  for (const [key, value] of Object.entries(attributes)) {
7
32
  if (value !== undefined) {
@@ -15,49 +40,31 @@ export async function withSpan(options, fn) {
15
40
  return result;
16
41
  }
17
42
  catch (error) {
18
- span.setStatus({
19
- code: SpanStatusCode.ERROR,
20
- message: error instanceof Error ? error.message : String(error),
21
- });
22
- if (error instanceof Error) {
23
- span.recordException(error);
24
- }
43
+ markFailure(span, error, options.expectedFailure?.(error));
25
44
  throw error;
26
45
  }
27
46
  finally {
28
47
  span.end();
29
48
  }
30
- });
49
+ };
50
+ return detached
51
+ ? run(tracer.startSpan(name, { kind }))
52
+ : tracer.startActiveSpan(name, { kind }, run);
31
53
  }
32
54
  export async function withClientSpan(options, fn) {
33
55
  return withSpan({ ...options, kind: SpanKind.CLIENT }, fn);
34
56
  }
35
57
  /**
36
- * Span wrapper for streaming operations.
37
- *
38
- * Unlike {@link withSpan}, which ends the span when `fn` resolves, this
39
- * helper extends the span's lifetime until the **consumer** of the returned
40
- * iterable reaches end-of-stream (success), throws (error), or aborts.
41
- *
42
- * Required for any operation whose result is a producer (`StreamResult`,
43
- * `AsyncIterable`-returning function, etc.) — ending the span on `fn`
44
- * resolution would capture only the setup phase and report zero tokens /
45
- * meaningless duration.
46
- *
47
- * @param options Span options (name, tracer, attributes, kind).
48
- * @param fn Callback that produces the result. Must NOT depend on
49
- * the iterable being consumed before returning.
50
- * @param getIterable Selector that picks the `AsyncIterable` out of the
51
- * result for wrapping.
52
- * @param setIterable Setter that returns a new result with the iterable
53
- * replaced by the wrapped (span-lifetime-extending)
54
- * iterable. Should be a pure function — clone the
55
- * result rather than mutating in place.
56
- *
58
+ * @description Wraps a streaming operation in a span that stays open until the consumer reaches end-of-stream, throws, or closes the iterable, so the span covers the whole stream rather than only its setup.
59
+ * @param options {SpanOptions} - Span name, tracer, kind and initial attributes.
60
+ * @param fn {(span: Span) => Promise<TResult>} - Produces the result; must not depend on the iterable being consumed before returning.
61
+ * @param getIterable {(result: TResult) => AsyncIterable<TChunk>} - Selects the iterable to wrap out of the result.
62
+ * @param setIterable {(result: TResult, wrapped: AsyncIterableIterator<TChunk>) => TResult} - Returns a new result carrying the wrapped iterable; must clone rather than mutate.
63
+ * @returns {Promise<TResult>} The result with its iterable replaced by the span-lifetime-extending one.
57
64
  * @example
58
65
  * ```ts
59
66
  * return withStreamSpan(
60
- * { name: "neurolink.provider.stream", tracer, attributes: {...} },
67
+ * { name: "provider.stream", tracer, attributes: {...} },
61
68
  * async () => this.executeStreamInner(options),
62
69
  * (r) => r.stream,
63
70
  * (r, wrapped) => ({ ...r, stream: wrapped }),
@@ -65,7 +72,7 @@ export async function withClientSpan(options, fn) {
65
72
  * ```
66
73
  */
67
74
  export async function withStreamSpan(options, fn, getIterable, setIterable) {
68
- const { name, tracer, kind = SpanKind.CLIENT, attributes } = options;
75
+ const { name, tracer, kind = SpanKind.CLIENT, attributes, detached, } = options;
69
76
  const span = tracer.startSpan(name, { kind });
70
77
  if (attributes) {
71
78
  for (const [key, value] of Object.entries(attributes)) {
@@ -74,25 +81,22 @@ export async function withStreamSpan(options, fn, getIterable, setIterable) {
74
81
  }
75
82
  }
76
83
  }
84
+ const spanContext = detached
85
+ ? context.active()
86
+ : trace.setSpan(context.active(), span);
77
87
  let result;
78
88
  try {
79
- result = await fn(span);
89
+ result = await context.with(spanContext, () => fn(span));
80
90
  }
81
91
  catch (error) {
82
- span.setStatus({
83
- code: SpanStatusCode.ERROR,
84
- message: error instanceof Error ? error.message : String(error),
85
- });
86
- if (error instanceof Error) {
87
- span.recordException(error);
88
- }
92
+ markFailure(span, error);
89
93
  span.end();
90
94
  throw error;
91
95
  }
92
- // Setup succeeded. Now wrap the iterable so the span ends with the stream.
93
- const original = getIterable(result);
96
+ const iterator = getIterable(result)[Symbol.asyncIterator]();
94
97
  let ended = false;
95
- const endOnce = (status) => {
98
+ let started = false;
99
+ function end(status) {
96
100
  if (ended) {
97
101
  return;
98
102
  }
@@ -100,38 +104,121 @@ export async function withStreamSpan(options, fn, getIterable, setIterable) {
100
104
  if (status) {
101
105
  span.setStatus(status);
102
106
  }
103
- else {
104
- span.setStatus({ code: SpanStatusCode.OK });
105
- }
106
107
  span.end();
107
- };
108
- // Build an AsyncGenerator (not just an AsyncIterable) so the wrapped
109
- // value is structurally assignable wherever the original stream type
110
- // already expected `AsyncGenerator<X>` (e.g. Ollama's executeStream
111
- // returns `AsyncGenerator<{ content: string }>`).
112
- const wrapped = (async function* () {
113
- try {
114
- for await (const chunk of original) {
115
- yield chunk;
116
- }
117
- endOnce();
108
+ }
109
+ async function close() {
110
+ if (ended) {
111
+ return;
118
112
  }
119
- catch (err) {
120
- // recordException must come BEFORE span.end() — OTel ignores events
121
- // on ended spans, so flipping the order silently drops the exception.
122
- if (err instanceof Error) {
123
- span.recordException(err);
113
+ span.setAttributes({
114
+ [LangfuseOtelSpanAttributes.OBSERVATION_LEVEL]: "WARNING",
115
+ [LangfuseOtelSpanAttributes.OBSERVATION_STATUS_MESSAGE]: started
116
+ ? "stream closed early"
117
+ : "stream closed before first chunk",
118
+ });
119
+ end();
120
+ await iterator.return?.();
121
+ }
122
+ const wrapped = {
123
+ async next() {
124
+ if (ended) {
125
+ return { done: true, value: undefined };
124
126
  }
125
- endOnce({
126
- code: SpanStatusCode.ERROR,
127
- message: err instanceof Error ? err.message : String(err),
128
- });
129
- throw err;
130
- }
131
- })();
127
+ started = true;
128
+ try {
129
+ const step = await context.with(spanContext, () => iterator.next());
130
+ if (step.done) {
131
+ end({ code: SpanStatusCode.OK });
132
+ return { done: true, value: undefined };
133
+ }
134
+ return step;
135
+ }
136
+ catch (err) {
137
+ markFailure(span, err);
138
+ end();
139
+ throw err;
140
+ }
141
+ },
142
+ async return() {
143
+ await close();
144
+ return { done: true, value: undefined };
145
+ },
146
+ async throw(error) {
147
+ await close();
148
+ throw error;
149
+ },
150
+ [Symbol.asyncIterator]() {
151
+ return wrapped;
152
+ },
153
+ };
132
154
  return setIterable(result, wrapped);
133
155
  }
156
+ /**
157
+ * @description Runs one model request inside a Langfuse generation span, recording the request half before `fn` runs. `fn` records the response half with `observeGeneration`; errors mark the span ERROR.
158
+ * @param request {GenerationRecord} - Provider, model, parameters and input sent.
159
+ * @param fn {(span: Span) => Promise<T>} - Issues the request and returns its result.
160
+ * @returns {Promise<T>} Whatever `fn` resolves to.
161
+ */
162
+ export async function withGeneration(request, fn) {
163
+ return withSpan({
164
+ name: generationName(request.operation),
165
+ tracer: tracers.generation,
166
+ kind: SpanKind.CLIENT,
167
+ }, async (span) => {
168
+ observeGeneration(span, request);
169
+ return fn(span);
170
+ });
171
+ }
134
172
  /** Convenience CLIENT-kind alias matching `withClientSpan`. */
135
173
  export async function withClientStreamSpan(options, fn, getIterable, setIterable) {
136
174
  return withStreamSpan({ ...options, kind: SpanKind.CLIENT }, fn, getIterable, setIterable);
137
175
  }
176
+ /**
177
+ * @description Runs `fn` inside a `step` span, for a single model call that belongs to a step outside the shared loop, such as a forced finalization request.
178
+ * @param stepNumber {number} - One-based step number within the turn.
179
+ * @param fn {() => Promise<T>} - The step's work.
180
+ * @returns {Promise<T>} Whatever `fn` resolves to.
181
+ */
182
+ export async function withStepSpan(stepNumber, fn) {
183
+ return withSpan({ name: stepSpanName(), tracer: tracers.sdk }, async (span) => {
184
+ observe(span, "span", { metadata: { step_index: stepNumber } });
185
+ return fn();
186
+ });
187
+ }
188
+ /**
189
+ * @description Groups the observations of each agentic loop step under one `step` span. Call `begin` at the top of a step, run the step's model call and tool dispatch through `run`, and `end` when the loop is over; `begin` closes the previous step.
190
+ */
191
+ export class StepSpans {
192
+ current;
193
+ /**
194
+ * @description Closes the previous step and opens `step` under the active span.
195
+ * @param stepNumber {number} - One-based step number within the turn.
196
+ * @returns {void} Nothing.
197
+ */
198
+ begin(stepNumber) {
199
+ this.end();
200
+ this.current = tracers.sdk.startSpan(stepSpanName(), {
201
+ kind: SpanKind.INTERNAL,
202
+ });
203
+ observe(this.current, "span", { metadata: { step_index: stepNumber } });
204
+ }
205
+ /**
206
+ * @description Runs `fn` with the open step as the active span, so the observations it creates become the step's children.
207
+ * @param fn {() => T} - Work belonging to the current step.
208
+ * @returns {T} Whatever `fn` returns.
209
+ */
210
+ run(fn) {
211
+ const step = this.current;
212
+ return step
213
+ ? context.with(trace.setSpan(context.active(), step), fn)
214
+ : fn();
215
+ }
216
+ /**
217
+ * @description Closes the open step, if any.
218
+ * @returns {void} Nothing.
219
+ */
220
+ end() {
221
+ this.current?.end();
222
+ this.current = undefined;
223
+ }
224
+ }
@@ -1042,13 +1042,6 @@ export type TelemetryCommandArgs = {
1042
1042
  };
1043
1043
  /** Telemetry status sub-command args */
1044
1044
  export type TelemetryStatusArgs = TelemetryCommandArgs;
1045
- /** Telemetry configure sub-command args */
1046
- export type TelemetryConfigureArgs = TelemetryCommandArgs & {
1047
- exporter: string;
1048
- config: string;
1049
- };
1050
- /** Telemetry list-exporters sub-command args */
1051
- export type TelemetryListExportersArgs = TelemetryCommandArgs;
1052
1045
  /** Telemetry flush sub-command args */
1053
1046
  export type TelemetryFlushArgs = TelemetryCommandArgs & {
1054
1047
  timeout?: number;
@@ -1077,8 +1070,6 @@ export type TelemetryNativeValidateArgs = {
1077
1070
  directory: string;
1078
1071
  collectorBin: string;
1079
1072
  };
1080
- /** Available exporter names */
1081
- export type ExporterName = "langfuse" | "langsmith" | "otel" | "datadog" | "sentry" | "braintrust" | "arize" | "posthog" | "laminar";
1082
1073
  /** Observability command arguments */
1083
1074
  export type ObservabilityCommandArgs = {
1084
1075
  format?: "text" | "json" | "table";
@@ -1090,8 +1081,6 @@ export type ObservabilityStatusArgs = ObservabilityCommandArgs;
1090
1081
  export type ObservabilityMetricsArgs = ObservabilityCommandArgs & {
1091
1082
  detailed?: boolean;
1092
1083
  };
1093
- /** Observability exporters sub-command args */
1094
- export type ObservabilityExportersArgs = ObservabilityCommandArgs;
1095
1084
  /** Observability costs sub-command args */
1096
1085
  export type ObservabilityCostsArgs = ObservabilityCommandArgs & {
1097
1086
  byModel?: boolean;
@@ -18,6 +18,7 @@
18
18
  * `src/lib/types/` (CLAUDE.md rule 9). Note `evaluate`/`Evaluation*` is a
19
19
  * different, unrelated feature — scoring a generated response with RAGAS.
20
20
  */
21
+ import type { NeurolinkTraceContext } from "./observability.js";
21
22
  import type { NeurolinkCredentials } from "./providers.js";
22
23
  /**
23
24
  * Anything a decision model accepts as free-form content. `state` may be a
@@ -336,6 +337,8 @@ export type DecisionOptions = DecisionRequest & {
336
337
  provider?: string;
337
338
  /** Per-call credential overrides, as `generate()` takes. */
338
339
  credentials?: NeurolinkCredentials;
340
+ /** Per-call trace attributes (user, session, tags, trace name, metadata) for this decision's spans. */
341
+ context?: NeurolinkTraceContext;
339
342
  };
340
343
  /**
341
344
  * The built-in consumers of the `decide` inference type — one name per place
@@ -1,3 +1,5 @@
1
+ import type { JsonValue } from "./common.js";
2
+ import type { SpanAttributes } from "./span.js";
1
3
  /**
2
4
  * Exporter configuration types
3
5
  * Following NeuroLink's type conventions
@@ -71,6 +73,13 @@ export type LangfuseExporterConfig = ExporterConfig & {
71
73
  * Defaults to false (input/output are exported).
72
74
  */
73
75
  redactIO?: boolean;
76
+ /**
77
+ * Applied to input, output and metadata before they are sent. When it
78
+ * throws, the value is replaced by `<fully masked>`.
79
+ */
80
+ mask?: (params: {
81
+ data: JsonValue;
82
+ }) => JsonValue;
74
83
  };
75
84
  /**
76
85
  * LangSmith exporter configuration
@@ -255,3 +264,48 @@ export type SamplingRule = {
255
264
  /** Optional priority (higher = evaluated first) */
256
265
  priority?: number;
257
266
  };
267
+ /** @description A span attribute value as `SpanData` carries it before OTLP encoding. */
268
+ export type SpanAttributeValue = SpanAttributes[string];
269
+ /** @description An OTLP/JSON attribute value. */
270
+ export type OtlpAnyValue = {
271
+ boolValue: boolean;
272
+ } | {
273
+ intValue: string;
274
+ } | {
275
+ doubleValue: number;
276
+ } | {
277
+ stringValue: string;
278
+ } | {
279
+ arrayValue: {
280
+ values: OtlpAnyValue[];
281
+ };
282
+ };
283
+ /** @description An OTLP/JSON attribute. */
284
+ export type OtlpKeyValue = {
285
+ key: string;
286
+ value: OtlpAnyValue;
287
+ };
288
+ /** @description An OTLP/JSON span as Langfuse's ingestion endpoint reads it. */
289
+ export type OtlpSpan = {
290
+ traceId: string;
291
+ spanId: string;
292
+ parentSpanId?: string;
293
+ name: string;
294
+ kind: number;
295
+ startTimeUnixNano: string;
296
+ endTimeUnixNano: string;
297
+ attributes: OtlpKeyValue[];
298
+ events: {
299
+ name: string;
300
+ timeUnixNano: string;
301
+ attributes: OtlpKeyValue[];
302
+ }[];
303
+ status: {
304
+ code: number;
305
+ message?: string;
306
+ };
307
+ };
308
+ /** @description What the Langfuse exporter adds to, masks in and removes from span attributes before sending. */
309
+ export type LangfuseAttributePolicy = Pick<LangfuseExporterConfig, "environment" | "release" | "mask"> & {
310
+ redactIO: boolean;
311
+ };
@@ -1,5 +1 @@
1
- /**
2
- * Exporter configuration types
3
- * Following NeuroLink's type conventions
4
- */
5
1
  export {};
@@ -6,6 +6,7 @@ import type { AnalyticsData, TokenUsage } from "./analytics.js";
6
6
  import type { ClaudeLimitSnapshot } from "./subscription.js";
7
7
  import type { JsonValue } from "./common.js";
8
8
  import type { TerminalAgentModeOption, TerminalAgentModeVersion } from "./agentMode.js";
9
+ import type { NeurolinkTraceContext } from "./observability.js";
9
10
  import type { Content, ImageWithAltText } from "./content.js";
10
11
  import type { ChatMessage, ConversationMemoryConfig, StreamEventSequence, ToolReplayMode } from "./conversation.js";
11
12
  import type { MemoryCallOptions } from "./memory.js";
@@ -647,7 +648,7 @@ export type GenerateOptions = {
647
648
  } | undefined>;
648
649
  enableEvaluation?: boolean;
649
650
  enableAnalytics?: boolean;
650
- context?: StandardRecord;
651
+ context?: StandardRecord & NeurolinkTraceContext;
651
652
  evaluationDomain?: string;
652
653
  toolUsageContext?: string;
653
654
  /**
@@ -1847,6 +1848,8 @@ export type NativeGenerateLoopArgs = {
1847
1848
  prepareStep?: NativeLoopPrepareStep;
1848
1849
  /** Resolved model id, handed to `prepareStep` as its `model` argument. */
1849
1850
  modelId: string;
1851
+ /** Provider name recorded on each step's generation and used to price it. */
1852
+ provider: string;
1850
1853
  responseFormat?: Record<string, unknown>;
1851
1854
  providerOptions?: Record<string, Record<string, unknown>>;
1852
1855
  maxSteps: number;
@@ -31,10 +31,11 @@ export type WorkerLogEvent = {
31
31
  * Options for `NeuroLink.createWorkerInstance()`.
32
32
  *
33
33
  * A worker instance is a sub-agent-grade NeuroLink: conversation memory off,
34
- * orchestration off, observability inherited from the creating instance with
35
- * `autoDetectExternalProvider: true` + `skipLangfuseSpanProcessor: true`
36
- * (so worker spans join the host's tracer without duplicate Langfuse
37
- * exports), and an internal log bridge attached with a caller-supplied tag.
34
+ * orchestration off, observability config inherited from the creating
35
+ * instance, and an internal log bridge attached with a caller-supplied tag.
36
+ * Telemetry is shared through the process-wide tracer provider, which is
37
+ * auto-detected when the host registered its own, so worker spans join the
38
+ * host's traces without duplicate Langfuse exports.
38
39
  */
39
40
  export type WorkerInstanceOptions = {
40
41
  /** Tag stamped on every forwarded log event (default "worker"). */
@@ -2,6 +2,8 @@ import type Anthropic from "@anthropic-ai/sdk";
2
2
  import type { Span } from "@opentelemetry/api";
3
3
  import type { Tool } from "./tools.js";
4
4
  import type { GenerateStopReason } from "./generate.js";
5
+ import type { UnknownRecord } from "./common.js";
6
+ import type { GenerationRecord } from "./observability.js";
5
7
  import type { CollectedChunkResult, NativeFunctionCall, NativeToolDeclarationsResult } from "./providers.js";
6
8
  import type { ExecutionControlDecision, ExecutionControlStepContext } from "./stream.js";
7
9
  /**
@@ -50,6 +52,8 @@ export type AgenticLoopStepResult<TRaw = unknown> = {
50
52
  usage: AgenticLoopUsage;
51
53
  /** Provider's own raw stop/finish-reason string, e.g. "tool_use", "MAX_TOKENS" */
52
54
  rawStopReason: string | undefined;
55
+ /** Model the response says served it, when the API reports one. */
56
+ model?: string;
53
57
  /** Adapter-private accumulated response data needed by buildToolResultMessages
54
58
  * (e.g. Anthropic's ordered content blocks, Gemini's rawResponseParts). */
55
59
  raw: TRaw;
@@ -61,6 +65,8 @@ export type AgenticLoopToolCallResult = AgenticLoopToolCall & {
61
65
  };
62
66
  export type AgenticLoopStepRequest = {
63
67
  raw: unknown;
68
+ /** The request half of this step's generation observation. */
69
+ generation?: GenerationRecord;
64
70
  /**
65
71
  * Tools that became callable while this step's request was being built —
66
72
  * mid-turn discovery hydrating a name the model had already tried.
@@ -248,6 +254,8 @@ export type AnthropicLoopAdapterConfig<TMessage = Anthropic.Messages.MessagePara
248
254
  client: {
249
255
  messages: Pick<Anthropic["messages"], "create">;
250
256
  };
257
+ /** Provider name recorded on each step's generation and used to price it. */
258
+ provider: string;
251
259
  maxSteps: number;
252
260
  /**
253
261
  * Build one step's request. A closure so the per-turn work the caller
@@ -346,13 +354,6 @@ export type AnthropicLoopAdapterConfig<TMessage = Anthropic.Messages.MessagePara
346
354
  * it, so this cannot quietly give a provider behaviour its hand-rolled loops
347
355
  * never had.
348
356
  */
349
- /** One model-requested tool call, as recorded on the tool's observability span. */
350
- export type ToolSpanCall = {
351
- /** Arguments the model passed to the tool. */
352
- args?: Record<string, unknown>;
353
- /** The model's id for this call, shared with the tool_use block that requested it. */
354
- toolCallId?: string;
355
- };
356
357
  export type ToolExecutionGuards = {
357
358
  /**
358
359
  * Upper bound on a single execute(). Omit, or pass `null`, for no bound.
@@ -374,25 +375,6 @@ export type ToolExecutionGuards = {
374
375
  * legitimately slow tool reads as a stalled turn without this.
375
376
  */
376
377
  onProgress?: () => void;
377
- /**
378
- * Wrap one tool call in the provider's own observability.
379
- *
380
- * A function rather than a pair of start/end callbacks, because the caller
381
- * needs the execution to happen INSIDE its span's context — spans opened by
382
- * the tool itself must nest under the tool call, not dangle as siblings of
383
- * the turn. Handing over the whole invocation is the only shape that lets a
384
- * caller do `context.with(span, run)`.
385
- *
386
- * The caller awaits `run()` itself, so it sees the settled result and can
387
- * mark a FAILURE THAT WAS RETURNED rather than thrown: MCP tools report
388
- * errors in their payload, and an observation that only watches for
389
- * exceptions records those calls as successful.
390
- *
391
- * `call` carries the arguments the model passed and the model's id for the
392
- * call, so the observation can show what was asked and be paired with the
393
- * request that asked it.
394
- */
395
- withToolSpan?: <T>(name: string, run: () => Promise<T>, call?: ToolSpanCall) => Promise<T>;
396
378
  };
397
379
  /**
398
380
  * @deprecated Renamed to `ToolExecutionGuards` — the guards were never
@@ -406,6 +388,12 @@ export type GeminiStepRaw = {
406
388
  rawResponseParts: unknown[];
407
389
  stepFunctionCalls: NativeFunctionCall[];
408
390
  };
391
+ /** One Gemini step's request: the model, the conversation and the generation config. */
392
+ export type GeminiStepRequest = {
393
+ model: string;
394
+ contents: GeminiTurnContent[];
395
+ config?: UnknownRecord;
396
+ };
409
397
  /** One turn entry in a Gemini conversation: a role plus its content parts. */
410
398
  export type GeminiTurnContent = {
411
399
  role: string;
@@ -419,9 +407,11 @@ export type GeminiTurnContent = {
419
407
  export type GeminiLoopAdapterCoreConfig = {
420
408
  /** Used in log lines and generated tool-call ids. */
421
409
  providerLabel: string;
410
+ /** Provider name recorded on each step's generation and used to price it. */
411
+ provider: string;
422
412
  maxSteps: number;
423
413
  /** Build one step's request object (model, contents, config). */
424
- buildRequest: (conversation: GeminiTurnContent[], step: number) => unknown;
414
+ buildRequest: (conversation: GeminiTurnContent[], step: number) => GeminiStepRequest;
425
415
  /** Issue the request. Kept injectable so each provider keeps its own client. */
426
416
  sendStep: (request: unknown, signal: AbortSignal) => Promise<AsyncIterable<{
427
417
  functionCalls?: NativeFunctionCall[];
@@ -541,7 +531,7 @@ export type AgenticLoopOptions = {
541
531
  /**
542
532
  * Span the per-step provider retry annotates, via
543
533
  * `withProviderRetry(..., span, ...)` — it records
544
- * `gen_ai.provider.total_attempts` on every completed step, retried or not.
534
+ * `provider.total_attempts` on every completed step, retried or not.
545
535
  *
546
536
  * Caller-supplied rather than read from the ambient context inside the
547
537
  * engine. Reading it here would hand the attribute to every provider on the