@introspection-sdk/introspection-node 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-check-types.log +5 -0
- package/.turbo/turbo-lint.log +5 -0
- package/dist/ai-sdk-integration.d.ts +123 -0
- package/dist/ai-sdk-integration.d.ts.map +1 -0
- package/dist/ai-sdk-integration.js +475 -0
- package/dist/anthropic.d.ts +54 -0
- package/dist/anthropic.d.ts.map +1 -0
- package/dist/anthropic.js +375 -0
- package/dist/claude-hooks.d.ts +328 -0
- package/dist/claude-hooks.d.ts.map +1 -0
- package/dist/claude-hooks.js +571 -0
- package/dist/claude-wrapper.d.ts +93 -0
- package/dist/claude-wrapper.d.ts.map +1 -0
- package/dist/claude-wrapper.js +154 -0
- package/dist/client.d.ts +157 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +394 -0
- package/dist/converters/ai-sdk.d.ts +65 -0
- package/dist/converters/ai-sdk.d.ts.map +1 -0
- package/dist/converters/ai-sdk.js +238 -0
- package/dist/converters/claude.d.ts +146 -0
- package/dist/converters/claude.d.ts.map +1 -0
- package/dist/converters/claude.js +202 -0
- package/dist/converters/index.d.ts +9 -0
- package/dist/converters/index.d.ts.map +1 -0
- package/dist/converters/index.js +8 -0
- package/dist/converters/openai.d.ts +51 -0
- package/dist/converters/openai.d.ts.map +1 -0
- package/dist/converters/openai.js +268 -0
- package/dist/converters/openclaw.d.ts +16 -0
- package/dist/converters/openclaw.d.ts.map +1 -0
- package/dist/converters/openclaw.js +49 -0
- package/dist/converters/openinference.d.ts +107 -0
- package/dist/converters/openinference.d.ts.map +1 -0
- package/dist/converters/openinference.js +552 -0
- package/dist/converters/vercel.d.ts +21 -0
- package/dist/converters/vercel.d.ts.map +1 -0
- package/dist/converters/vercel.js +208 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/langchain-handler.d.ts +101 -0
- package/dist/langchain-handler.d.ts.map +1 -0
- package/dist/langchain-handler.js +489 -0
- package/dist/mastra-exporter.d.ts +85 -0
- package/dist/mastra-exporter.d.ts.map +1 -0
- package/dist/mastra-exporter.js +471 -0
- package/dist/span-processor.d.ts +62 -0
- package/dist/span-processor.d.ts.map +1 -0
- package/dist/span-processor.js +310 -0
- package/dist/tracing-processor.d.ts +136 -0
- package/dist/tracing-processor.d.ts.map +1 -0
- package/dist/tracing-processor.js +368 -0
- package/dist/types/genai.d.ts +165 -0
- package/dist/types/genai.d.ts.map +1 -0
- package/dist/types/genai.js +82 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +19 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +57 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/eslint.config.mjs +4 -0
- package/package.json +78 -0
- package/src/ai-sdk-integration.ts +689 -0
- package/src/anthropic.ts +465 -0
- package/src/claude-hooks.ts +1010 -0
- package/src/claude-wrapper.ts +240 -0
- package/src/client.ts +513 -0
- package/src/converters/ai-sdk.ts +298 -0
- package/src/converters/claude.ts +334 -0
- package/src/converters/index.ts +44 -0
- package/src/converters/openai.ts +302 -0
- package/src/converters/openinference.ts +673 -0
- package/src/converters/vercel.ts +264 -0
- package/src/index.ts +121 -0
- package/src/langchain-handler.ts +722 -0
- package/src/mastra-exporter.ts +650 -0
- package/src/span-processor.ts +418 -0
- package/src/tracing-processor.ts +548 -0
- package/src/types/genai.ts +264 -0
- package/src/types.ts +13 -0
- package/src/utils.ts +69 -0
- package/src/version.ts +2 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight Anthropic instrumentor for Introspection SDK.
|
|
3
|
+
*
|
|
4
|
+
* Captures the full Anthropic response including thinking blocks (extended
|
|
5
|
+
* thinking) with signatures, which third-party instrumentors drop.
|
|
6
|
+
*
|
|
7
|
+
* Supports both non-streaming (`messages.create`) and streaming
|
|
8
|
+
* (`messages.create({ stream: true })`) calls.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { AnthropicInstrumentor } from "@introspection-sdk/introspection-node";
|
|
13
|
+
*
|
|
14
|
+
* const instrumentor = new AnthropicInstrumentor();
|
|
15
|
+
* instrumentor.instrument({ tracerProvider: provider });
|
|
16
|
+
* // All client.messages.create calls are now traced
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
import { trace, SpanKind, SpanStatusCode } from "@opentelemetry/api";
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Converters
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/** Sentinel value for redacted thinking blocks — content was encrypted by safety systems. */
|
|
24
|
+
export const REDACTED_THINKING_CONTENT = "[redacted]";
|
|
25
|
+
function blockToParts(block) {
|
|
26
|
+
const bt = block.type || "";
|
|
27
|
+
if (bt === "text") {
|
|
28
|
+
return [{ type: "text", content: block.text || "" }];
|
|
29
|
+
}
|
|
30
|
+
if (bt === "thinking") {
|
|
31
|
+
const part = {
|
|
32
|
+
type: "thinking",
|
|
33
|
+
content: block.thinking || undefined,
|
|
34
|
+
signature: block.signature || undefined,
|
|
35
|
+
provider_name: "anthropic",
|
|
36
|
+
};
|
|
37
|
+
return [part];
|
|
38
|
+
}
|
|
39
|
+
if (bt === "redacted_thinking") {
|
|
40
|
+
return [
|
|
41
|
+
{
|
|
42
|
+
type: "thinking",
|
|
43
|
+
content: REDACTED_THINKING_CONTENT,
|
|
44
|
+
signature: block.data || undefined,
|
|
45
|
+
provider_name: "anthropic",
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
if (bt === "tool_use") {
|
|
50
|
+
return [
|
|
51
|
+
{
|
|
52
|
+
type: "tool_call",
|
|
53
|
+
id: block.id || "",
|
|
54
|
+
name: block.name || "",
|
|
55
|
+
arguments: block.input,
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
if (bt === "tool_result") {
|
|
60
|
+
return [
|
|
61
|
+
{
|
|
62
|
+
type: "tool_call_response",
|
|
63
|
+
id: block.tool_use_id || "",
|
|
64
|
+
response: block.content != null ? String(block.content) : "",
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
function convertAnthropicInput(messages) {
|
|
71
|
+
const result = [];
|
|
72
|
+
for (const msg of messages) {
|
|
73
|
+
const role = msg.role || "user";
|
|
74
|
+
const content = msg.content;
|
|
75
|
+
if (typeof content === "string") {
|
|
76
|
+
result.push({
|
|
77
|
+
role: role,
|
|
78
|
+
parts: [{ type: "text", content }],
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
else if (Array.isArray(content)) {
|
|
82
|
+
const parts = [];
|
|
83
|
+
for (const block of content) {
|
|
84
|
+
const rec = typeof block === "object" && block !== null
|
|
85
|
+
? block
|
|
86
|
+
: { type: "text", text: String(block) };
|
|
87
|
+
parts.push(...blockToParts(rec));
|
|
88
|
+
}
|
|
89
|
+
if (parts.length > 0) {
|
|
90
|
+
result.push({ role: role, parts });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
function convertAnthropicOutput(content) {
|
|
97
|
+
const parts = [];
|
|
98
|
+
let hasToolCalls = false;
|
|
99
|
+
for (const block of content) {
|
|
100
|
+
const newParts = blockToParts(block);
|
|
101
|
+
for (const p of newParts) {
|
|
102
|
+
if (p.type === "tool_call")
|
|
103
|
+
hasToolCalls = true;
|
|
104
|
+
}
|
|
105
|
+
parts.push(...newParts);
|
|
106
|
+
}
|
|
107
|
+
if (parts.length === 0)
|
|
108
|
+
return [];
|
|
109
|
+
return [
|
|
110
|
+
{
|
|
111
|
+
role: "assistant",
|
|
112
|
+
parts,
|
|
113
|
+
finish_reason: hasToolCalls ? "tool-calls" : "stop",
|
|
114
|
+
},
|
|
115
|
+
];
|
|
116
|
+
}
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Span helpers
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
function serializeMessages(msgs) {
|
|
121
|
+
return JSON.stringify(msgs.map((m) => {
|
|
122
|
+
const obj = { role: m.role, parts: m.parts };
|
|
123
|
+
if ("finish_reason" in m && m.finish_reason) {
|
|
124
|
+
obj.finish_reason = m.finish_reason;
|
|
125
|
+
}
|
|
126
|
+
return obj;
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
function startSpan(tracer, model) {
|
|
130
|
+
const span = tracer.startSpan("chat", {
|
|
131
|
+
kind: SpanKind.CLIENT,
|
|
132
|
+
attributes: {
|
|
133
|
+
"gen_ai.system": "anthropic",
|
|
134
|
+
"gen_ai.provider.name": "anthropic",
|
|
135
|
+
"gen_ai.operation.name": "chat",
|
|
136
|
+
"gen_ai.request.model": model,
|
|
137
|
+
"openinference.span.kind": "LLM",
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
return { span, token: null };
|
|
141
|
+
}
|
|
142
|
+
function setRequestAttrs(span, kwargs) {
|
|
143
|
+
const messages = kwargs.messages || [];
|
|
144
|
+
const inputMsgs = convertAnthropicInput(messages);
|
|
145
|
+
if (inputMsgs.length > 0) {
|
|
146
|
+
span.setAttribute("gen_ai.input.messages", serializeMessages(inputMsgs));
|
|
147
|
+
}
|
|
148
|
+
const system = kwargs.system;
|
|
149
|
+
if (system) {
|
|
150
|
+
const sysVal = typeof system === "string"
|
|
151
|
+
? JSON.stringify([{ type: "text", content: system }])
|
|
152
|
+
: JSON.stringify(system);
|
|
153
|
+
span.setAttribute("gen_ai.system_instructions", sysVal);
|
|
154
|
+
}
|
|
155
|
+
const tools = kwargs.tools;
|
|
156
|
+
if (tools && tools.length > 0) {
|
|
157
|
+
const defs = tools.map((t) => ({
|
|
158
|
+
name: t.name || "",
|
|
159
|
+
description: t.description,
|
|
160
|
+
parameters: t.input_schema,
|
|
161
|
+
}));
|
|
162
|
+
span.setAttribute("gen_ai.tool.definitions", JSON.stringify(defs));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function setResponseAttrs(span, response) {
|
|
166
|
+
const content = response.content;
|
|
167
|
+
if (content) {
|
|
168
|
+
const outputMsgs = convertAnthropicOutput(content);
|
|
169
|
+
if (outputMsgs.length > 0) {
|
|
170
|
+
span.setAttribute("gen_ai.output.messages", serializeMessages(outputMsgs));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (response.id)
|
|
174
|
+
span.setAttribute("gen_ai.response.id", String(response.id));
|
|
175
|
+
if (response.model)
|
|
176
|
+
span.setAttribute("gen_ai.response.model", String(response.model));
|
|
177
|
+
const usage = response.usage;
|
|
178
|
+
if (usage) {
|
|
179
|
+
if (usage.input_tokens != null)
|
|
180
|
+
span.setAttribute("gen_ai.usage.input_tokens", usage.input_tokens);
|
|
181
|
+
if (usage.output_tokens != null)
|
|
182
|
+
span.setAttribute("gen_ai.usage.output_tokens", usage.output_tokens);
|
|
183
|
+
}
|
|
184
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
185
|
+
}
|
|
186
|
+
class TracedStream {
|
|
187
|
+
inner;
|
|
188
|
+
span;
|
|
189
|
+
blocks = [];
|
|
190
|
+
currentBlock = null;
|
|
191
|
+
responseId = null;
|
|
192
|
+
responseModel = null;
|
|
193
|
+
inputTokens = 0;
|
|
194
|
+
outputTokens = 0;
|
|
195
|
+
finalized = false;
|
|
196
|
+
constructor(inner, span) {
|
|
197
|
+
this.inner = inner;
|
|
198
|
+
this.span = span;
|
|
199
|
+
}
|
|
200
|
+
async *[Symbol.asyncIterator]() {
|
|
201
|
+
try {
|
|
202
|
+
for await (const event of this.inner) {
|
|
203
|
+
this.processEvent(event);
|
|
204
|
+
yield event;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
this.finalize();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
processEvent(event) {
|
|
212
|
+
if (event.type === "message_start" && event.message) {
|
|
213
|
+
this.responseId = event.message.id || null;
|
|
214
|
+
this.responseModel = event.message.model || null;
|
|
215
|
+
const usage = event.message.usage;
|
|
216
|
+
if (usage)
|
|
217
|
+
this.inputTokens = usage.input_tokens || 0;
|
|
218
|
+
}
|
|
219
|
+
else if (event.type === "content_block_start" && event.content_block) {
|
|
220
|
+
const bt = event.content_block.type || "";
|
|
221
|
+
this.currentBlock = { type: bt };
|
|
222
|
+
if (bt === "thinking") {
|
|
223
|
+
this.currentBlock.thinking = "";
|
|
224
|
+
this.currentBlock.signature = "";
|
|
225
|
+
}
|
|
226
|
+
else if (bt === "text") {
|
|
227
|
+
this.currentBlock.text = "";
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
else if (event.type === "content_block_delta" && event.delta) {
|
|
231
|
+
if (!this.currentBlock)
|
|
232
|
+
return;
|
|
233
|
+
const dt = event.delta.type || "";
|
|
234
|
+
if (dt === "thinking_delta") {
|
|
235
|
+
this.currentBlock.thinking =
|
|
236
|
+
(this.currentBlock.thinking || "") +
|
|
237
|
+
(event.delta.thinking || "");
|
|
238
|
+
}
|
|
239
|
+
else if (dt === "text_delta") {
|
|
240
|
+
this.currentBlock.text =
|
|
241
|
+
(this.currentBlock.text || "") +
|
|
242
|
+
(event.delta.text || "");
|
|
243
|
+
}
|
|
244
|
+
else if (dt === "signature_delta") {
|
|
245
|
+
this.currentBlock.signature =
|
|
246
|
+
(this.currentBlock.signature || "") +
|
|
247
|
+
(event.delta.signature || "");
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
else if (event.type === "content_block_stop") {
|
|
251
|
+
if (this.currentBlock) {
|
|
252
|
+
this.blocks.push(this.currentBlock);
|
|
253
|
+
this.currentBlock = null;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
else if (event.type === "message_delta") {
|
|
257
|
+
const usage = event.usage;
|
|
258
|
+
if (usage)
|
|
259
|
+
this.outputTokens = usage.output_tokens || 0;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
finalize() {
|
|
263
|
+
if (this.finalized)
|
|
264
|
+
return;
|
|
265
|
+
this.finalized = true;
|
|
266
|
+
if (this.blocks.length > 0) {
|
|
267
|
+
const outputMsgs = convertAnthropicOutput(this.blocks);
|
|
268
|
+
if (outputMsgs.length > 0) {
|
|
269
|
+
this.span.setAttribute("gen_ai.output.messages", serializeMessages(outputMsgs));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (this.responseId)
|
|
273
|
+
this.span.setAttribute("gen_ai.response.id", this.responseId);
|
|
274
|
+
if (this.responseModel)
|
|
275
|
+
this.span.setAttribute("gen_ai.response.model", this.responseModel);
|
|
276
|
+
if (this.inputTokens)
|
|
277
|
+
this.span.setAttribute("gen_ai.usage.input_tokens", this.inputTokens);
|
|
278
|
+
if (this.outputTokens)
|
|
279
|
+
this.span.setAttribute("gen_ai.usage.output_tokens", this.outputTokens);
|
|
280
|
+
this.span.setStatus({ code: SpanStatusCode.OK });
|
|
281
|
+
this.span.end();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// ---------------------------------------------------------------------------
|
|
285
|
+
// Public API
|
|
286
|
+
// ---------------------------------------------------------------------------
|
|
287
|
+
/**
|
|
288
|
+
* Traced wrapper around `client.messages.create(kwargs)`.
|
|
289
|
+
* Creates a gen_ai span with full Anthropic response including thinking blocks.
|
|
290
|
+
*/
|
|
291
|
+
export async function tracedMessagesCreate(tracer, client, kwargs) {
|
|
292
|
+
const { span } = startSpan(tracer, kwargs.model || "unknown");
|
|
293
|
+
try {
|
|
294
|
+
setRequestAttrs(span, kwargs);
|
|
295
|
+
const response = (await client.messages.create(kwargs));
|
|
296
|
+
setResponseAttrs(span, response);
|
|
297
|
+
return response;
|
|
298
|
+
}
|
|
299
|
+
catch (e) {
|
|
300
|
+
span.setStatus({
|
|
301
|
+
code: SpanStatusCode.ERROR,
|
|
302
|
+
message: String(e),
|
|
303
|
+
});
|
|
304
|
+
throw e;
|
|
305
|
+
}
|
|
306
|
+
finally {
|
|
307
|
+
span.end();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Auto-instrumentor that patches `Anthropic.messages.create` to add tracing.
|
|
312
|
+
*
|
|
313
|
+
* Captures all content blocks including thinking (extended thinking) with
|
|
314
|
+
* signatures. Supports both non-streaming and streaming calls.
|
|
315
|
+
*/
|
|
316
|
+
/**
|
|
317
|
+
* Auto-instrumentor that wraps an Anthropic client instance to add tracing.
|
|
318
|
+
*
|
|
319
|
+
* Captures all content blocks including thinking (extended thinking) with
|
|
320
|
+
* signatures. Supports both non-streaming and streaming calls.
|
|
321
|
+
*/
|
|
322
|
+
export class AnthropicInstrumentor {
|
|
323
|
+
tracer = null;
|
|
324
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
325
|
+
patchedClients = [];
|
|
326
|
+
instrument(opts) {
|
|
327
|
+
const { client } = opts;
|
|
328
|
+
if (!client?.messages?.create) {
|
|
329
|
+
throw new Error("Invalid client — pass an Anthropic client instance (new Anthropic())");
|
|
330
|
+
}
|
|
331
|
+
const provider = opts.tracerProvider ?? trace.getTracerProvider();
|
|
332
|
+
this.tracer = provider.getTracer("introspection-anthropic");
|
|
333
|
+
const origCreate = client.messages.create.bind(client.messages);
|
|
334
|
+
this.patchedClients.push({ client, originalCreate: origCreate });
|
|
335
|
+
const tracer = this.tracer;
|
|
336
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
337
|
+
client.messages.create = function (...args) {
|
|
338
|
+
const kwargs = args[0];
|
|
339
|
+
const isStream = kwargs?.stream === true;
|
|
340
|
+
const { span } = startSpan(tracer, kwargs?.model || "unknown");
|
|
341
|
+
setRequestAttrs(span, kwargs || {});
|
|
342
|
+
if (isStream) {
|
|
343
|
+
const streamPromise = origCreate(...args);
|
|
344
|
+
return streamPromise.then((stream) => new TracedStream(stream, span), (err) => {
|
|
345
|
+
span.setStatus({
|
|
346
|
+
code: SpanStatusCode.ERROR,
|
|
347
|
+
message: String(err),
|
|
348
|
+
});
|
|
349
|
+
span.end();
|
|
350
|
+
throw err;
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
const resultPromise = origCreate(...args);
|
|
354
|
+
return resultPromise.then((response) => {
|
|
355
|
+
setResponseAttrs(span, response);
|
|
356
|
+
span.end();
|
|
357
|
+
return response;
|
|
358
|
+
}, (err) => {
|
|
359
|
+
span.setStatus({
|
|
360
|
+
code: SpanStatusCode.ERROR,
|
|
361
|
+
message: String(err),
|
|
362
|
+
});
|
|
363
|
+
span.end();
|
|
364
|
+
throw err;
|
|
365
|
+
});
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
uninstrument() {
|
|
369
|
+
for (const { client, originalCreate } of this.patchedClients) {
|
|
370
|
+
client.messages.create = originalCreate;
|
|
371
|
+
}
|
|
372
|
+
this.patchedClients = [];
|
|
373
|
+
this.tracer = null;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Agent SDK Instrumentation Hooks
|
|
3
|
+
*
|
|
4
|
+
* Provides OpenTelemetry instrumentation for the Claude Agent SDK via its hooks system.
|
|
5
|
+
* Creates spans for sessions, tool uses, and subagent executions with gen_ai.* attributes.
|
|
6
|
+
*
|
|
7
|
+
* @see https://platform.claude.com/docs/en/agent-sdk/typescript
|
|
8
|
+
*/
|
|
9
|
+
import { type SpanExporter, type IdGenerator } from "@opentelemetry/sdk-trace-base";
|
|
10
|
+
/** Common fields present on every hook input payload. */
|
|
11
|
+
export interface BaseHookInput {
|
|
12
|
+
/** Active session identifier. */
|
|
13
|
+
session_id: string;
|
|
14
|
+
/** Path to the session transcript file. */
|
|
15
|
+
transcript_path: string;
|
|
16
|
+
/** Working directory of the agent process. */
|
|
17
|
+
cwd: string;
|
|
18
|
+
/** Permission mode the session is running in. */
|
|
19
|
+
permission_mode?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Input payload for the `SessionStart` hook. */
|
|
22
|
+
export interface SessionStartHookInput extends BaseHookInput {
|
|
23
|
+
hook_event_name: "SessionStart";
|
|
24
|
+
/** How the session was initiated. */
|
|
25
|
+
source: "startup" | "resume" | "clear" | "compact";
|
|
26
|
+
/** Agent type (e.g. `"main"`, `"subagent"`). */
|
|
27
|
+
agent_type?: string;
|
|
28
|
+
/** Model used for this session. */
|
|
29
|
+
model?: string;
|
|
30
|
+
}
|
|
31
|
+
/** Input payload for the `SessionEnd` hook. */
|
|
32
|
+
export interface SessionEndHookInput extends BaseHookInput {
|
|
33
|
+
hook_event_name: "SessionEnd";
|
|
34
|
+
/** Reason the session ended. */
|
|
35
|
+
reason: string;
|
|
36
|
+
}
|
|
37
|
+
/** Input payload for the `PreToolUse` hook. */
|
|
38
|
+
export interface PreToolUseHookInput extends BaseHookInput {
|
|
39
|
+
hook_event_name: "PreToolUse";
|
|
40
|
+
/** Name of the tool about to be invoked. */
|
|
41
|
+
tool_name: string;
|
|
42
|
+
/** Input data passed to the tool. */
|
|
43
|
+
tool_input: unknown;
|
|
44
|
+
/** Unique identifier for this tool invocation. */
|
|
45
|
+
tool_use_id: string;
|
|
46
|
+
}
|
|
47
|
+
/** Input payload for the `PostToolUse` hook. */
|
|
48
|
+
export interface PostToolUseHookInput extends BaseHookInput {
|
|
49
|
+
hook_event_name: "PostToolUse";
|
|
50
|
+
/** Name of the tool that was invoked. */
|
|
51
|
+
tool_name: string;
|
|
52
|
+
/** Input data that was passed to the tool. */
|
|
53
|
+
tool_input: unknown;
|
|
54
|
+
/** Response returned by the tool. */
|
|
55
|
+
tool_response: unknown;
|
|
56
|
+
/** Unique identifier for this tool invocation. */
|
|
57
|
+
tool_use_id: string;
|
|
58
|
+
}
|
|
59
|
+
/** Input payload for the `SubagentStart` hook. */
|
|
60
|
+
export interface SubagentStartHookInput extends BaseHookInput {
|
|
61
|
+
hook_event_name: "SubagentStart";
|
|
62
|
+
/** Identifier of the sub-agent. */
|
|
63
|
+
agent_id: string;
|
|
64
|
+
/** Type / name of the sub-agent. */
|
|
65
|
+
agent_type: string;
|
|
66
|
+
}
|
|
67
|
+
/** Input payload for the `SubagentStop` hook. */
|
|
68
|
+
export interface SubagentStopHookInput extends BaseHookInput {
|
|
69
|
+
hook_event_name: "SubagentStop";
|
|
70
|
+
/** Identifier of the sub-agent. */
|
|
71
|
+
agent_id: string;
|
|
72
|
+
/** Type / name of the sub-agent. */
|
|
73
|
+
agent_type: string;
|
|
74
|
+
/** Path to the sub-agent's transcript. */
|
|
75
|
+
agent_transcript_path: string;
|
|
76
|
+
}
|
|
77
|
+
/** Discriminated union of all possible hook input payloads. */
|
|
78
|
+
export type ClaudeHookInput = SessionStartHookInput | SessionEndHookInput | PreToolUseHookInput | PostToolUseHookInput | SubagentStartHookInput | SubagentStopHookInput;
|
|
79
|
+
/**
|
|
80
|
+
* JSON-serialisable output returned by a hook callback
|
|
81
|
+
* (`SyncHookJSONOutput` in the SDK).
|
|
82
|
+
*/
|
|
83
|
+
export type ClaudeHookOutput = {
|
|
84
|
+
/** Whether the SDK should continue processing. */
|
|
85
|
+
continue?: boolean;
|
|
86
|
+
/** Suppress the default output for this hook event. */
|
|
87
|
+
suppressOutput?: boolean;
|
|
88
|
+
/** Reason to stop the session. */
|
|
89
|
+
stopReason?: string;
|
|
90
|
+
/** Approval decision for `PreToolUse` hooks. */
|
|
91
|
+
decision?: "approve" | "block";
|
|
92
|
+
/** System message to inject into the conversation. */
|
|
93
|
+
systemMessage?: string;
|
|
94
|
+
/** Human-readable reason for the decision. */
|
|
95
|
+
reason?: string;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Async callback signature for a Claude Agent SDK hook.
|
|
99
|
+
*
|
|
100
|
+
* @param input - The hook input payload.
|
|
101
|
+
* @param toolUseID - The tool-use ID (set for `PreToolUse` / `PostToolUse`).
|
|
102
|
+
* @param options - Abort signal for cancellation.
|
|
103
|
+
* @returns A {@link ClaudeHookOutput} controlling the SDK's behaviour.
|
|
104
|
+
*/
|
|
105
|
+
export type ClaudeHookCallback = (input: ClaudeHookInput, toolUseID: string | undefined, options: {
|
|
106
|
+
signal: AbortSignal;
|
|
107
|
+
}) => Promise<ClaudeHookOutput>;
|
|
108
|
+
/** Pairs an optional glob matcher with one or more hook callbacks. */
|
|
109
|
+
export interface ClaudeHookCallbackMatcher {
|
|
110
|
+
/** Glob pattern to match tool names (only relevant for tool hooks). */
|
|
111
|
+
matcher?: string;
|
|
112
|
+
/** Callbacks to invoke when the matcher (or all events) match. */
|
|
113
|
+
hooks: ClaudeHookCallback[];
|
|
114
|
+
/** Timeout in milliseconds for hook execution. */
|
|
115
|
+
timeout?: number;
|
|
116
|
+
}
|
|
117
|
+
/** String literal union of all Claude Agent SDK hook event names. */
|
|
118
|
+
export type ClaudeHookEvent = "PreToolUse" | "PostToolUse" | "SessionStart" | "SessionEnd" | "SubagentStart" | "SubagentStop";
|
|
119
|
+
/**
|
|
120
|
+
* Full hooks configuration object accepted by the Claude Agent SDK
|
|
121
|
+
* `query()` options. Maps each event name to an array of
|
|
122
|
+
* {@link ClaudeHookCallbackMatcher} entries.
|
|
123
|
+
*/
|
|
124
|
+
export type ClaudeHooksConfig = Partial<Record<ClaudeHookEvent, ClaudeHookCallbackMatcher[]>>;
|
|
125
|
+
/** Token-level usage counters reported by the Claude API. */
|
|
126
|
+
export interface ClaudeUsage {
|
|
127
|
+
/** Number of input tokens consumed. */
|
|
128
|
+
input_tokens: number;
|
|
129
|
+
/** Number of output tokens generated. */
|
|
130
|
+
output_tokens: number;
|
|
131
|
+
/** Tokens used to create new cache entries. */
|
|
132
|
+
cache_creation_input_tokens?: number;
|
|
133
|
+
/** Tokens read from cache. */
|
|
134
|
+
cache_read_input_tokens?: number;
|
|
135
|
+
}
|
|
136
|
+
/** Per-model usage breakdown reported in a result message. */
|
|
137
|
+
export interface ClaudeModelUsage {
|
|
138
|
+
/** Input tokens for this model. */
|
|
139
|
+
inputTokens: number;
|
|
140
|
+
/** Output tokens for this model. */
|
|
141
|
+
outputTokens: number;
|
|
142
|
+
/** Tokens read from cache for this model. */
|
|
143
|
+
cacheReadInputTokens: number;
|
|
144
|
+
/** Tokens used to create cache for this model. */
|
|
145
|
+
cacheCreationInputTokens: number;
|
|
146
|
+
/** Estimated cost in USD for this model. */
|
|
147
|
+
costUSD: number;
|
|
148
|
+
}
|
|
149
|
+
/** A `"result"` message yielded at the end of a `query()` stream. */
|
|
150
|
+
export interface ClaudeResultMessage {
|
|
151
|
+
type: "result";
|
|
152
|
+
/** Aggregate token usage for the session. */
|
|
153
|
+
usage?: ClaudeUsage;
|
|
154
|
+
/** Total estimated cost in USD. */
|
|
155
|
+
total_cost_usd?: number;
|
|
156
|
+
/** Per-model usage breakdown. */
|
|
157
|
+
modelUsage?: Record<string, ClaudeModelUsage>;
|
|
158
|
+
/** Final text result (if any). */
|
|
159
|
+
result?: string;
|
|
160
|
+
/** Whether the session ended with an error. */
|
|
161
|
+
is_error?: boolean;
|
|
162
|
+
/** Session identifier. */
|
|
163
|
+
session_id: string;
|
|
164
|
+
}
|
|
165
|
+
/** An `"assistant"` message yielded during a `query()` stream. */
|
|
166
|
+
export interface ClaudeAssistantMessage {
|
|
167
|
+
type: "assistant";
|
|
168
|
+
/** The underlying API message with optional usage and content. */
|
|
169
|
+
message?: {
|
|
170
|
+
id?: string;
|
|
171
|
+
model?: string;
|
|
172
|
+
usage?: {
|
|
173
|
+
input_tokens?: number;
|
|
174
|
+
output_tokens?: number;
|
|
175
|
+
};
|
|
176
|
+
content?: Array<{
|
|
177
|
+
type: string;
|
|
178
|
+
text?: string;
|
|
179
|
+
}>;
|
|
180
|
+
};
|
|
181
|
+
/** Session identifier. */
|
|
182
|
+
session_id: string;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Discriminated union of all messages that can be yielded by a Claude Agent
|
|
186
|
+
* SDK `query()` stream.
|
|
187
|
+
*/
|
|
188
|
+
export type ClaudeSDKMessage = ClaudeResultMessage | ClaudeAssistantMessage | {
|
|
189
|
+
type: string;
|
|
190
|
+
session_id?: string;
|
|
191
|
+
};
|
|
192
|
+
/** Advanced options for testing and customization of {@link IntrospectionClaudeHooks}. */
|
|
193
|
+
export interface ClaudeHooksAdvancedOptions {
|
|
194
|
+
/** Custom span exporter (for testing) */
|
|
195
|
+
spanExporter?: SpanExporter;
|
|
196
|
+
/** Custom ID generator (for testing) */
|
|
197
|
+
idGenerator?: IdGenerator;
|
|
198
|
+
/** Use SimpleSpanProcessor instead of BatchSpanProcessor (for testing) */
|
|
199
|
+
useSimpleSpanProcessor?: boolean;
|
|
200
|
+
}
|
|
201
|
+
/** Configuration for {@link IntrospectionClaudeHooks}. */
|
|
202
|
+
export interface IntrospectionClaudeHooksOptions {
|
|
203
|
+
/** Authentication token (env: INTROSPECTION_TOKEN) */
|
|
204
|
+
token?: string;
|
|
205
|
+
/** Base URL for the API (env: INTROSPECTION_BASE_URL) */
|
|
206
|
+
baseUrl?: string;
|
|
207
|
+
/** Service name for the OTel resource (defaults to "claude-agent") */
|
|
208
|
+
serviceName?: string;
|
|
209
|
+
/** Agent name set on session spans as gen_ai.agent.name (defaults to serviceName) */
|
|
210
|
+
agentName?: string;
|
|
211
|
+
/** Additional headers to include in requests */
|
|
212
|
+
additionalHeaders?: Record<string, string>;
|
|
213
|
+
/** Advanced options for testing and customization */
|
|
214
|
+
advanced?: ClaudeHooksAdvancedOptions;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Provides instrumented hooks for Claude Agent SDK.
|
|
218
|
+
*
|
|
219
|
+
* Creates OpenTelemetry spans with gen_ai.* semantic convention attributes:
|
|
220
|
+
* - Session spans: gen_ai.system, gen_ai.request.model
|
|
221
|
+
* - Tool spans: gen_ai.tool.name, gen_ai.tool.input, gen_ai.tool.output
|
|
222
|
+
* - Subagent spans: gen_ai.agent.name
|
|
223
|
+
*
|
|
224
|
+
* Usage tracking is captured from streamed messages via recordUsage().
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```typescript
|
|
228
|
+
* import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
229
|
+
* import { IntrospectionClaudeHooks } from "@introspection-sdk/introspection-node";
|
|
230
|
+
*
|
|
231
|
+
* const hooks = new IntrospectionClaudeHooks();
|
|
232
|
+
*
|
|
233
|
+
* const stream = query({
|
|
234
|
+
* prompt: "Hello",
|
|
235
|
+
* options: {
|
|
236
|
+
* hooks: hooks.getHooks(),
|
|
237
|
+
* },
|
|
238
|
+
* });
|
|
239
|
+
*
|
|
240
|
+
* for await (const message of stream) {
|
|
241
|
+
* hooks.recordUsage(message);
|
|
242
|
+
* // handle message...
|
|
243
|
+
* }
|
|
244
|
+
*
|
|
245
|
+
* await hooks.shutdown();
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
248
|
+
export declare class IntrospectionClaudeHooks {
|
|
249
|
+
private _tracerProvider;
|
|
250
|
+
private _tracer;
|
|
251
|
+
private _agentName;
|
|
252
|
+
private _sessionSpans;
|
|
253
|
+
private _toolSpans;
|
|
254
|
+
private _subagentSpans;
|
|
255
|
+
private _sessionUsage;
|
|
256
|
+
constructor(options?: IntrospectionClaudeHooksOptions);
|
|
257
|
+
/**
|
|
258
|
+
* Build the hooks configuration object to pass to Claude Agent SDK
|
|
259
|
+
* `query()` options.
|
|
260
|
+
*
|
|
261
|
+
* @returns A {@link ClaudeHooksConfig} wired to this instance's tracing.
|
|
262
|
+
*/
|
|
263
|
+
getHooks(): ClaudeHooksConfig;
|
|
264
|
+
/**
|
|
265
|
+
* Set the input prompt for a session so it appears as
|
|
266
|
+
* `gen_ai.input.messages` on the session span.
|
|
267
|
+
*
|
|
268
|
+
* If `sessionId` is not provided the prompt is buffered and associated
|
|
269
|
+
* with the next session that starts via an `init` message.
|
|
270
|
+
*
|
|
271
|
+
* @param prompt - The user prompt string passed to `query()`.
|
|
272
|
+
* @param sessionId - Optional session ID if already known.
|
|
273
|
+
*/
|
|
274
|
+
setInputPrompt(prompt: string, sessionId?: string): void;
|
|
275
|
+
private _pendingInputPrompt?;
|
|
276
|
+
/**
|
|
277
|
+
* Set system instructions for a session so they appear as
|
|
278
|
+
* `gen_ai.system_instructions` on the session span.
|
|
279
|
+
*
|
|
280
|
+
* If `sessionId` is not provided the value is buffered and associated
|
|
281
|
+
* with the next session that starts via an `init` message.
|
|
282
|
+
*
|
|
283
|
+
* @param instructions - The system prompt / instructions string.
|
|
284
|
+
* @param sessionId - Optional session ID if already known.
|
|
285
|
+
*/
|
|
286
|
+
setSystemInstructions(instructions: string, sessionId?: string): void;
|
|
287
|
+
private _pendingSystemInstructions?;
|
|
288
|
+
/**
|
|
289
|
+
* Record usage from a streamed SDK message.
|
|
290
|
+
*
|
|
291
|
+
* Call this for **every** message yielded by `query()` to capture token
|
|
292
|
+
* usage. Session spans are automatically created on `init` messages and
|
|
293
|
+
* ended on `result` messages.
|
|
294
|
+
*
|
|
295
|
+
* @param message - A {@link ClaudeSDKMessage} yielded by the `query()` stream.
|
|
296
|
+
*/
|
|
297
|
+
recordUsage(message: ClaudeSDKMessage): void;
|
|
298
|
+
/**
|
|
299
|
+
* Start a session span from an init system message.
|
|
300
|
+
* This provides an alternative to the SessionStart hook which has timing issues.
|
|
301
|
+
*/
|
|
302
|
+
private _startSessionFromMessage;
|
|
303
|
+
/**
|
|
304
|
+
* End a session span from a result message.
|
|
305
|
+
* This provides an alternative to the SessionEnd hook which has timing issues.
|
|
306
|
+
*/
|
|
307
|
+
private _endSessionFromMessage;
|
|
308
|
+
private _onSessionStart;
|
|
309
|
+
private _onSessionEnd;
|
|
310
|
+
private _onPreToolUse;
|
|
311
|
+
private _onPostToolUse;
|
|
312
|
+
private _onSubagentStart;
|
|
313
|
+
private _onSubagentStop;
|
|
314
|
+
/**
|
|
315
|
+
* Force-flush any buffered spans to the Introspection backend.
|
|
316
|
+
*
|
|
317
|
+
* @returns A promise that resolves once the flush completes.
|
|
318
|
+
*/
|
|
319
|
+
forceFlush(): Promise<void>;
|
|
320
|
+
/**
|
|
321
|
+
* Shut down the hooks, ending any in-flight spans and flushing all
|
|
322
|
+
* pending data.
|
|
323
|
+
*
|
|
324
|
+
* @returns A promise that resolves once shutdown is complete.
|
|
325
|
+
*/
|
|
326
|
+
shutdown(): Promise<void>;
|
|
327
|
+
}
|
|
328
|
+
//# sourceMappingURL=claude-hooks.d.ts.map
|