@introspection-sdk/introspection-node 0.4.2 → 0.4.3

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 (151) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-check-types.log +4 -0
  3. package/.turbo/turbo-lint.log +5 -0
  4. package/dist/client.d.ts +42 -132
  5. package/dist/client.d.ts.map +1 -1
  6. package/dist/client.js +67 -366
  7. package/dist/client.js.map +1 -0
  8. package/dist/converters/claude.d.ts +4 -0
  9. package/dist/converters/claude.d.ts.map +1 -1
  10. package/dist/converters/claude.js +7 -0
  11. package/dist/converters/claude.js.map +1 -0
  12. package/dist/converters/gemini.d.ts +90 -0
  13. package/dist/converters/gemini.d.ts.map +1 -0
  14. package/dist/converters/gemini.js +171 -0
  15. package/dist/converters/gemini.js.map +1 -0
  16. package/dist/converters/index.d.ts +1 -1
  17. package/dist/converters/index.d.ts.map +1 -1
  18. package/dist/converters/index.js +2 -1
  19. package/dist/converters/index.js.map +1 -0
  20. package/dist/converters/openai.js +1 -0
  21. package/dist/converters/openai.js.map +1 -0
  22. package/dist/converters/openclaw.d.ts +16 -0
  23. package/dist/converters/openclaw.d.ts.map +1 -0
  24. package/dist/converters/openclaw.js +49 -0
  25. package/dist/converters/openinference.js +1 -0
  26. package/dist/converters/openinference.js.map +1 -0
  27. package/dist/converters/vercel.d.ts.map +1 -1
  28. package/dist/converters/vercel.js +31 -3
  29. package/dist/converters/vercel.js.map +1 -0
  30. package/dist/http.d.ts +36 -0
  31. package/dist/http.d.ts.map +1 -0
  32. package/dist/http.js +145 -0
  33. package/dist/http.js.map +1 -0
  34. package/dist/index.d.ts +24 -27
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +22 -32
  37. package/dist/index.js.map +1 -0
  38. package/dist/otel/anthropic.d.ts +62 -0
  39. package/dist/otel/anthropic.d.ts.map +1 -0
  40. package/dist/otel/anthropic.js +425 -0
  41. package/dist/otel/anthropic.js.map +1 -0
  42. package/dist/otel/claude-hooks.d.ts +346 -0
  43. package/dist/otel/claude-hooks.d.ts.map +1 -0
  44. package/dist/otel/claude-hooks.js +645 -0
  45. package/dist/otel/claude-hooks.js.map +1 -0
  46. package/dist/otel/claude-wrapper.d.ts +93 -0
  47. package/dist/otel/claude-wrapper.d.ts.map +1 -0
  48. package/dist/otel/claude-wrapper.js +155 -0
  49. package/dist/otel/claude-wrapper.js.map +1 -0
  50. package/dist/otel/gemini.d.ts +48 -0
  51. package/dist/otel/gemini.d.ts.map +1 -0
  52. package/dist/otel/gemini.js +297 -0
  53. package/dist/otel/gemini.js.map +1 -0
  54. package/dist/otel/index.d.ts +27 -0
  55. package/dist/otel/index.d.ts.map +1 -0
  56. package/dist/otel/index.js +33 -0
  57. package/dist/otel/index.js.map +1 -0
  58. package/dist/otel/langchain-handler.d.ts +112 -0
  59. package/dist/otel/langchain-handler.d.ts.map +1 -0
  60. package/dist/otel/langchain-handler.js +555 -0
  61. package/dist/otel/langchain-handler.js.map +1 -0
  62. package/dist/otel/logs.d.ts +82 -0
  63. package/dist/otel/logs.d.ts.map +1 -0
  64. package/dist/otel/logs.js +271 -0
  65. package/dist/otel/logs.js.map +1 -0
  66. package/dist/otel/mastra-exporter.d.ts +87 -0
  67. package/dist/otel/mastra-exporter.d.ts.map +1 -0
  68. package/dist/otel/mastra-exporter.js +525 -0
  69. package/dist/otel/mastra-exporter.js.map +1 -0
  70. package/dist/otel/pi.d.ts +59 -0
  71. package/dist/otel/pi.d.ts.map +1 -0
  72. package/dist/otel/pi.js +67 -0
  73. package/dist/otel/pi.js.map +1 -0
  74. package/dist/otel/setup.d.ts +23 -0
  75. package/dist/otel/setup.d.ts.map +1 -0
  76. package/dist/otel/setup.js +127 -0
  77. package/dist/otel/setup.js.map +1 -0
  78. package/dist/otel/span-processor.d.ts +62 -0
  79. package/dist/otel/span-processor.d.ts.map +1 -0
  80. package/dist/otel/span-processor.js +319 -0
  81. package/dist/otel/span-processor.js.map +1 -0
  82. package/dist/otel/tracing-processor.d.ts +139 -0
  83. package/dist/otel/tracing-processor.d.ts.map +1 -0
  84. package/dist/otel/tracing-processor.js +418 -0
  85. package/dist/otel/tracing-processor.js.map +1 -0
  86. package/dist/resources/experiments.d.ts +31 -0
  87. package/dist/resources/experiments.d.ts.map +1 -0
  88. package/dist/resources/experiments.js +133 -0
  89. package/dist/resources/experiments.js.map +1 -0
  90. package/dist/resources/recipes.d.ts +22 -0
  91. package/dist/resources/recipes.d.ts.map +1 -0
  92. package/dist/resources/recipes.js +61 -0
  93. package/dist/resources/recipes.js.map +1 -0
  94. package/dist/resources/runtimes.d.ts +76 -0
  95. package/dist/resources/runtimes.d.ts.map +1 -0
  96. package/dist/resources/runtimes.js +179 -0
  97. package/dist/resources/runtimes.js.map +1 -0
  98. package/dist/runner-resources/files.d.ts +35 -0
  99. package/dist/runner-resources/files.d.ts.map +1 -0
  100. package/dist/runner-resources/files.js +131 -0
  101. package/dist/runner-resources/files.js.map +1 -0
  102. package/dist/runner-resources/tasks.d.ts +39 -0
  103. package/dist/runner-resources/tasks.d.ts.map +1 -0
  104. package/dist/runner-resources/tasks.js +129 -0
  105. package/dist/runner-resources/tasks.js.map +1 -0
  106. package/dist/runner.d.ts +80 -0
  107. package/dist/runner.d.ts.map +1 -0
  108. package/dist/runner.js +159 -0
  109. package/dist/runner.js.map +1 -0
  110. package/dist/streaming.d.ts +8 -0
  111. package/dist/streaming.d.ts.map +1 -0
  112. package/dist/streaming.js +67 -0
  113. package/dist/streaming.js.map +1 -0
  114. package/dist/types/genai.d.ts +165 -0
  115. package/dist/types/genai.d.ts.map +1 -0
  116. package/dist/types/genai.js +82 -0
  117. package/dist/types.js +1 -0
  118. package/dist/types.js.map +1 -0
  119. package/dist/utils.js +1 -0
  120. package/dist/utils.js.map +1 -0
  121. package/dist/version.js +1 -0
  122. package/dist/version.js.map +1 -0
  123. package/package.json +81 -24
  124. package/src/client.ts +76 -473
  125. package/src/converters/claude.ts +10 -0
  126. package/src/converters/gemini.ts +243 -0
  127. package/src/converters/index.ts +13 -7
  128. package/src/converters/vercel.ts +41 -3
  129. package/src/http.ts +163 -0
  130. package/src/index.ts +110 -75
  131. package/src/{anthropic.ts → otel/anthropic.ts} +95 -8
  132. package/src/{claude-hooks.ts → otel/claude-hooks.ts} +119 -20
  133. package/src/{claude-wrapper.ts → otel/claude-wrapper.ts} +1 -1
  134. package/src/otel/gemini.ts +397 -0
  135. package/src/otel/index.ts +80 -0
  136. package/src/{langchain-handler.ts → otel/langchain-handler.ts} +53 -6
  137. package/src/otel/logs.ts +394 -0
  138. package/src/{mastra-exporter.ts → otel/mastra-exporter.ts} +83 -22
  139. package/src/otel/pi.ts +88 -0
  140. package/src/otel/setup.ts +174 -0
  141. package/src/{span-processor.ts → otel/span-processor.ts} +11 -6
  142. package/src/{tracing-processor.ts → otel/tracing-processor.ts} +94 -19
  143. package/src/resources/experiments.ts +170 -0
  144. package/src/resources/recipes.ts +73 -0
  145. package/src/resources/runtimes.ts +231 -0
  146. package/src/runner-resources/files.ts +163 -0
  147. package/src/runner-resources/tasks.ts +156 -0
  148. package/src/runner.ts +194 -0
  149. package/src/streaming.ts +57 -0
  150. package/src/ai-sdk-integration.ts +0 -691
  151. package/src/converters/ai-sdk.ts +0 -323
package/src/index.ts CHANGED
@@ -1,36 +1,18 @@
1
- import { IntrospectionSpanProcessor } from "./span-processor.js";
2
- import type { IntrospectionSpanProcessorOptions } from "./span-processor.js";
3
-
4
1
  /**
5
- * Factory that creates an {@link IntrospectionSpanProcessor}.
6
- *
7
- * Provided as the default export for convenient use with logfire's
8
- * `additionalSpanProcessors` option.
2
+ * Root entry point — REST-only surface.
9
3
  *
10
- * @param options - Optional processor configuration (token, serviceName, etc.).
11
- * @returns A ready-to-use {@link IntrospectionSpanProcessor}.
4
+ * Exports `IntrospectionClient` (REST), Runner / RuntimeHandle /
5
+ * ExperimentHandle, REST wire types, and HTTP helpers. None of these
6
+ * touch the OpenTelemetry SDK.
12
7
  *
13
- * @example
14
- * ```ts
15
- * import introspectionSpanProcessor from "@introspection-sdk/introspection-node";
16
- *
17
- * logfire.configure({ additionalSpanProcessors: [introspectionSpanProcessor()] });
18
- * ```
8
+ * For the OTel surface (logs, span processors, instrumentors) import
9
+ * from `@introspection-sdk/introspection-node/otel`.
19
10
  */
20
- export default function introspectionSpanProcessor(
21
- options?: IntrospectionSpanProcessorOptions,
22
- ): IntrospectionSpanProcessor {
23
- return new IntrospectionSpanProcessor(options);
24
- }
25
-
26
- // Span processor exports (for OpenTelemetry integration)
27
- export { IntrospectionSpanProcessor } from "./span-processor.js";
28
- export type { IntrospectionSpanProcessorOptions } from "./span-processor.js";
29
11
 
30
- // Client exports
12
+ // REST client.
31
13
  export { IntrospectionClient } from "./client.js";
32
14
 
33
- // Type exports
15
+ // Configuration / event types shared with the OTel surface.
34
16
  export type {
35
17
  AdvancedOptions,
36
18
  IntrospectionClientOptions,
@@ -38,40 +20,98 @@ export type {
38
20
  UserTraits,
39
21
  } from "./types.js";
40
22
 
41
- // Tracing processor exports (for OpenAI Agents SDK integration)
42
- export { IntrospectionTracingProcessor } from "./tracing-processor.js";
43
- export type {
44
- IntrospectionTracingProcessorOptions,
45
- TracingProcessorAdvancedOptions,
46
- } from "./tracing-processor.js";
23
+ // Runner-bound REST namespaces (runner.tasks, runner.files) + HTTP/SSE.
24
+ export { TasksApi, TaskRunsApi, RunHandle } from "./runner-resources/tasks.js";
25
+ export type { StartParams } from "./runner-resources/tasks.js";
26
+ export { FilesApi, FileVersionsApi } from "./runner-resources/files.js";
27
+ export type { FileUploadBody } from "./runner-resources/files.js";
28
+ export { HttpClient } from "./http.js";
29
+ export type { ResolvedApiConfig } from "./http.js";
30
+ export { parseSse } from "./streaming.js";
47
31
 
48
- // Claude Agent SDK hooks exports
49
- export { IntrospectionClaudeHooks } from "./claude-hooks.js";
50
- export type {
51
- IntrospectionClaudeHooksOptions,
52
- ClaudeHooksAdvancedOptions,
53
- ClaudeHooksConfig,
54
- ClaudeHookCallbackMatcher,
55
- ClaudeHookEvent,
56
- ClaudeHookInput,
57
- ClaudeHookOutput,
58
- ClaudeHookCallback,
59
- ClaudeUsage,
60
- ClaudeModelUsage,
61
- ClaudeResultMessage,
62
- ClaudeAssistantMessage,
63
- ClaudeSDKMessage,
64
- } from "./claude-hooks.js";
32
+ // Runner + CP resources.
33
+ export { Runner } from "./runner.js";
34
+ export type { RunnerSource } from "./runner.js";
35
+ export {
36
+ RuntimesApi,
37
+ RuntimeHandle,
38
+ attachRuntimes,
39
+ isUuid,
40
+ } from "./resources/runtimes.js";
41
+ export type { RuntimeHandleFactory } from "./resources/runtimes.js";
42
+ export {
43
+ ExperimentsApi,
44
+ ExperimentHandle,
45
+ attachExperiments,
46
+ } from "./resources/experiments.js";
47
+ export type { ExperimentHandleFactory } from "./resources/experiments.js";
48
+ export { RecipesApi, attachRecipes } from "./resources/recipes.js";
65
49
 
66
- // Claude Agent SDK wrapper exports
67
- export { withIntrospection } from "./claude-wrapper.js";
50
+ // REST API wire types
68
51
  export type {
69
- WithIntrospectionOptions,
70
- InstrumentedClaudeAgentSDK,
71
- ClaudeAgentSDKModule,
72
- } from "./claude-wrapper.js";
52
+ Paginated,
53
+ ListParams,
54
+ Task,
55
+ TaskCreateParams,
56
+ TaskUpdateParams,
57
+ TaskListParams,
58
+ TaskMode,
59
+ TaskStatus,
60
+ TaskPrompt,
61
+ TaskRun,
62
+ TaskRunCreateParams,
63
+ TaskRunResponse,
64
+ TaskCreateResponse,
65
+ TaskCancelResponse,
66
+ AgentInfo,
67
+ File,
68
+ FileType,
69
+ FileListParams,
70
+ FileUpdateParams,
71
+ FileCreateTextParams,
72
+ SseEvent,
73
+ Runtime,
74
+ RuntimeCreate,
75
+ RuntimeUpdate,
76
+ RuntimeListParams,
77
+ Experiment,
78
+ ExperimentCreate,
79
+ ExperimentUpdate,
80
+ ExperimentListParams,
81
+ ExperimentStatus,
82
+ ExperimentEndParams,
83
+ Arm,
84
+ Recipe,
85
+ RecipeCreate,
86
+ RecipeUpdate,
87
+ RecipeListParams,
88
+ RunnerSpec,
89
+ RunnerDeployment,
90
+ RunnerContext,
91
+ RunnerRecipeSummary,
92
+ RunnerIdentity,
93
+ RunRequest,
94
+ RunCaller,
95
+ RunCallerLibrary,
96
+ RunCallerPage,
97
+ RunIdentityInput,
98
+ } from "@introspection-sdk/types";
99
+ export {
100
+ IntrospectionAPIError,
101
+ AuthenticationError,
102
+ InsufficientScopeError,
103
+ RunnerExpiredError,
104
+ NotFoundError,
105
+ ConflictError,
106
+ ValidationError,
107
+ RateLimitError,
108
+ SandboxUnavailableError,
109
+ StreamError,
110
+ NetworkError,
111
+ apiErrorFromResponse,
112
+ } from "@introspection-sdk/types";
73
113
 
74
- // GenAI types exports
114
+ // GenAI types (shared, no OTel SDK imports).
75
115
  export type {
76
116
  GenAiAttributes,
77
117
  InputMessage,
@@ -85,7 +125,7 @@ export type {
85
125
  } from "@introspection-sdk/types";
86
126
  export { toAttributes } from "@introspection-sdk/types";
87
127
 
88
- // OpenAI converter exports
128
+ // OpenAI converter exports (pure functions, no OTel SDK imports).
89
129
  export {
90
130
  convertResponsesInputsToSemconv,
91
131
  convertResponsesOutputsToSemconv,
@@ -100,22 +140,17 @@ export type {
100
140
  Response as OpenAIResponse,
101
141
  } from "./converters/openai.js";
102
142
 
103
- // AI SDK integration exports (for Vercel AI SDK)
104
- export { IntrospectionAISDKIntegration } from "./ai-sdk-integration.js";
105
- export type {
106
- IntrospectionAISDKIntegrationOptions,
107
- AISDKIntegrationAdvancedOptions,
108
- } from "./ai-sdk-integration.js";
109
-
110
- // OpenInference converter exports
111
- export {
112
- addOpenInferenceAttributes,
113
- OpenInferenceSpanExporter,
114
- } from "./converters/openinference.js";
115
-
116
- // Anthropic instrumentor exports
143
+ // Gemini converters (pure functions, no OTel SDK imports).
117
144
  export {
118
- AnthropicInstrumentor,
119
- tracedMessagesCreate,
120
- REDACTED_THINKING_CONTENT,
121
- } from "./anthropic.js";
145
+ convertGeminiContentsToInputMessages,
146
+ convertGeminiCandidatesToOutputMessages,
147
+ convertGeminiSystemInstructionToSemconv,
148
+ convertGeminiToolsToToolDefinitions,
149
+ } from "./converters/gemini.js";
150
+ export type {
151
+ GeminiCandidate,
152
+ GeminiContent,
153
+ GeminiFunctionDeclaration,
154
+ GeminiPart,
155
+ GeminiTool,
156
+ } from "./converters/gemini.js";
@@ -24,7 +24,13 @@ import type {
24
24
  ReasoningPart,
25
25
  } from "@introspection-sdk/types";
26
26
 
27
- import { trace, SpanKind, SpanStatusCode } from "@opentelemetry/api";
27
+ import {
28
+ trace,
29
+ context as otelContext,
30
+ propagation,
31
+ SpanKind,
32
+ SpanStatusCode,
33
+ } from "@opentelemetry/api";
28
34
  import type { Tracer, Span } from "@opentelemetry/api";
29
35
  import type { BasicTracerProvider } from "@opentelemetry/sdk-trace-base";
30
36
 
@@ -154,7 +160,11 @@ function serializeMessages(msgs: InputMessage[] | OutputMessage[]): string {
154
160
  );
155
161
  }
156
162
 
157
- function startSpan(tracer: Tracer, model: string): { span: Span; token: null } {
163
+ function startSpan(
164
+ tracer: Tracer,
165
+ model: string,
166
+ conversationId?: string,
167
+ ): { span: Span; token: null } {
158
168
  const span = tracer.startSpan("chat", {
159
169
  kind: SpanKind.CLIENT,
160
170
  attributes: {
@@ -163,6 +173,9 @@ function startSpan(tracer: Tracer, model: string): { span: Span; token: null } {
163
173
  "gen_ai.operation.name": "chat",
164
174
  "gen_ai.request.model": model,
165
175
  "openinference.span.kind": "LLM",
176
+ ...(conversationId
177
+ ? { "gen_ai.conversation.id": conversationId }
178
+ : undefined),
166
179
  },
167
180
  });
168
181
  return { span, token: null };
@@ -177,10 +190,26 @@ function setRequestAttrs(span: Span, kwargs: Record<string, unknown>): void {
177
190
 
178
191
  const system = kwargs.system;
179
192
  if (system) {
180
- const sysVal =
181
- typeof system === "string"
182
- ? JSON.stringify([{ type: "text", content: system }])
183
- : JSON.stringify(system);
193
+ let sysVal: string;
194
+ if (typeof system === "string") {
195
+ sysVal = JSON.stringify([{ type: "text", content: system }]);
196
+ } else if (Array.isArray(system)) {
197
+ // Anthropic block format: [{type:"text", text:"...", cache_control:{...}}]
198
+ // Normalize to gen_ai format: [{type:"text", content:"..."}]
199
+ sysVal = JSON.stringify(
200
+ (system as Array<Record<string, unknown>>).map((block) => ({
201
+ type: "text",
202
+ content:
203
+ typeof block.text === "string"
204
+ ? block.text
205
+ : typeof block.content === "string"
206
+ ? block.content
207
+ : "",
208
+ })),
209
+ );
210
+ } else {
211
+ sysVal = JSON.stringify(system);
212
+ }
184
213
  span.setAttribute("gen_ai.system_instructions", sysVal);
185
214
  }
186
215
 
@@ -219,6 +248,16 @@ function setResponseAttrs(span: Span, response: Record<string, unknown>): void {
219
248
  span.setAttribute("gen_ai.usage.input_tokens", usage.input_tokens);
220
249
  if (usage.output_tokens != null)
221
250
  span.setAttribute("gen_ai.usage.output_tokens", usage.output_tokens);
251
+ if (usage.cache_read_input_tokens != null)
252
+ span.setAttribute(
253
+ "gen_ai.usage.cache_read.input_tokens",
254
+ usage.cache_read_input_tokens,
255
+ );
256
+ if (usage.cache_creation_input_tokens != null)
257
+ span.setAttribute(
258
+ "gen_ai.usage.cache_creation.input_tokens",
259
+ usage.cache_creation_input_tokens,
260
+ );
222
261
  }
223
262
 
224
263
  span.setStatus({ code: SpanStatusCode.OK });
@@ -245,6 +284,8 @@ class TracedStream {
245
284
  private responseModel: string | null = null;
246
285
  private inputTokens = 0;
247
286
  private outputTokens = 0;
287
+ private cacheReadInputTokens = 0;
288
+ private cacheCreationInputTokens = 0;
248
289
  private finalized = false;
249
290
 
250
291
  constructor(inner: AsyncIterable<StreamEvent>, span: Span) {
@@ -268,7 +309,11 @@ class TracedStream {
268
309
  this.responseId = (event.message.id as string) || null;
269
310
  this.responseModel = (event.message.model as string) || null;
270
311
  const usage = event.message.usage as Record<string, number> | undefined;
271
- if (usage) this.inputTokens = usage.input_tokens || 0;
312
+ if (usage) {
313
+ this.inputTokens = usage.input_tokens || 0;
314
+ this.cacheReadInputTokens = usage.cache_read_input_tokens || 0;
315
+ this.cacheCreationInputTokens = usage.cache_creation_input_tokens || 0;
316
+ }
272
317
  } else if (event.type === "content_block_start" && event.content_block) {
273
318
  const bt = (event.content_block.type as string) || "";
274
319
  this.currentBlock = { type: bt };
@@ -328,6 +373,16 @@ class TracedStream {
328
373
  this.span.setAttribute("gen_ai.usage.input_tokens", this.inputTokens);
329
374
  if (this.outputTokens)
330
375
  this.span.setAttribute("gen_ai.usage.output_tokens", this.outputTokens);
376
+ if (this.cacheReadInputTokens)
377
+ this.span.setAttribute(
378
+ "gen_ai.usage.cache_read.input_tokens",
379
+ this.cacheReadInputTokens,
380
+ );
381
+ if (this.cacheCreationInputTokens)
382
+ this.span.setAttribute(
383
+ "gen_ai.usage.cache_creation.input_tokens",
384
+ this.cacheCreationInputTokens,
385
+ );
331
386
  this.span.setStatus({ code: SpanStatusCode.OK });
332
387
  this.span.end();
333
388
  }
@@ -345,8 +400,13 @@ export async function tracedMessagesCreate(
345
400
  tracer: Tracer,
346
401
  client: { messages: { create: (...args: unknown[]) => Promise<unknown> } },
347
402
  kwargs: Record<string, unknown>,
403
+ conversationId?: string,
348
404
  ): Promise<unknown> {
349
- const { span } = startSpan(tracer, (kwargs.model as string) || "unknown");
405
+ const { span } = startSpan(
406
+ tracer,
407
+ (kwargs.model as string) || "unknown",
408
+ conversationId,
409
+ );
350
410
  try {
351
411
  setRequestAttrs(span, kwargs);
352
412
  const response = (await client.messages.create(kwargs)) as Record<
@@ -382,12 +442,20 @@ export class AnthropicInstrumentor {
382
442
  private tracer: Tracer | null = null;
383
443
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
384
444
  private patchedClients: Array<{ client: any; originalCreate: any }> = [];
445
+ private conversationId: string | undefined;
385
446
 
386
447
  instrument(opts: {
387
448
  tracerProvider?: BasicTracerProvider;
388
449
  /** The Anthropic client instance to instrument. */
389
450
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
390
451
  client: any;
452
+ /**
453
+ * Conversation ID shared across all spans produced by this instrumentor.
454
+ * Enables the Introspection processor to reliably link multi-turn API calls
455
+ * into a single conversation without relying on history-hash matching.
456
+ * Defaults to a random UUID generated at instrument() time.
457
+ */
458
+ conversationId?: string;
391
459
  }): void {
392
460
  const { client } = opts;
393
461
  if (!client?.messages?.create) {
@@ -396,6 +464,8 @@ export class AnthropicInstrumentor {
396
464
  );
397
465
  }
398
466
 
467
+ this.conversationId = opts.conversationId ?? crypto.randomUUID();
468
+
399
469
  const provider = opts.tracerProvider ?? trace.getTracerProvider();
400
470
  this.tracer = (provider as BasicTracerProvider).getTracer(
401
471
  "introspection-anthropic",
@@ -404,16 +474,33 @@ export class AnthropicInstrumentor {
404
474
  const origCreate = client.messages.create.bind(client.messages);
405
475
  this.patchedClients.push({ client, originalCreate: origCreate });
406
476
  const tracer = this.tracer!;
477
+ const defaultConversationId = this.conversationId;
407
478
 
408
479
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
409
480
  client.messages.create = function (...args: any[]) {
410
481
  const kwargs = args[0] as Record<string, unknown>;
411
482
  const isStream = kwargs?.stream === true;
412
483
 
484
+ // Baggage on the active context overrides the conversation ID bound
485
+ // at instrument() time, so callers can wrap a request in
486
+ // IntrospectionClient.withConversation() to scope a single call to a
487
+ // distinct conversation without re-instrumenting the client.
488
+ const baggage = propagation.getBaggage(otelContext.active());
489
+ const baggageConvId = baggage?.getEntry("gen_ai.conversation.id")?.value;
490
+ const baggageAgentName = baggage?.getEntry("gen_ai.agent.name")?.value;
491
+ const baggageAgentId = baggage?.getEntry("gen_ai.agent.id")?.value;
492
+
413
493
  const { span } = startSpan(
414
494
  tracer,
415
495
  (kwargs?.model as string) || "unknown",
496
+ baggageConvId ?? defaultConversationId,
416
497
  );
498
+ if (baggageAgentName) {
499
+ span.setAttribute("gen_ai.agent.name", baggageAgentName);
500
+ }
501
+ if (baggageAgentId) {
502
+ span.setAttribute("gen_ai.agent.id", baggageAgentId);
503
+ }
417
504
  setRequestAttrs(span, kwargs || {});
418
505
 
419
506
  if (isStream) {