dd-trace 6.0.0 → 6.2.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/LICENSE-3rdparty.csv +1 -2
- package/index.d.ts +28 -1
- package/initialize.mjs +4 -2
- package/package.json +34 -30
- package/packages/datadog-instrumentations/src/ai.js +45 -0
- package/packages/datadog-instrumentations/src/apollo-server.js +5 -13
- package/packages/datadog-instrumentations/src/child_process.js +3 -3
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +587 -0
- package/packages/datadog-instrumentations/src/cucumber.js +102 -43
- package/packages/datadog-instrumentations/src/cypress-config.js +11 -3
- package/packages/datadog-instrumentations/src/fastify.js +27 -8
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +7 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +5 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +6 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/claude-agent-sdk.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +31 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +19 -5
- package/packages/datadog-instrumentations/src/jest.js +713 -63
- package/packages/datadog-instrumentations/src/mercurius.js +11 -0
- package/packages/datadog-instrumentations/src/mocha/main.js +24 -3
- package/packages/datadog-instrumentations/src/mocha/utils.js +128 -22
- package/packages/datadog-instrumentations/src/mocha/worker.js +13 -0
- package/packages/datadog-instrumentations/src/mongodb.js +74 -0
- package/packages/datadog-instrumentations/src/mongoose.js +4 -5
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +15 -0
- package/packages/datadog-instrumentations/src/playwright.js +13 -9
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +4 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +26 -1
- package/packages/datadog-instrumentations/src/vitest-util.js +2 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +103 -34
- package/packages/datadog-plugin-ai/src/index.js +1 -1
- package/packages/datadog-plugin-ai/src/tracing.js +66 -3
- package/packages/datadog-plugin-ai/src/utils.js +17 -4
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +1 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -0
- package/packages/datadog-plugin-child_process/src/index.js +13 -2
- package/packages/datadog-plugin-claude-agent-sdk/src/index.js +31 -0
- package/packages/datadog-plugin-claude-agent-sdk/src/tracing.js +107 -0
- package/packages/datadog-plugin-claude-agent-sdk/src/util.js +15 -0
- package/packages/datadog-plugin-cucumber/src/index.js +14 -23
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +351 -11
- package/packages/datadog-plugin-cypress/src/index.js +47 -2
- package/packages/datadog-plugin-cypress/src/plugin.js +1 -0
- package/packages/datadog-plugin-graphql/src/execute.js +25 -25
- package/packages/datadog-plugin-graphql/src/index.js +23 -1
- package/packages/datadog-plugin-graphql/src/request.js +104 -0
- package/packages/datadog-plugin-graphql/src/utils.js +186 -2
- package/packages/datadog-plugin-graphql/src/validate.js +25 -2
- package/packages/datadog-plugin-jest/src/index.js +30 -14
- package/packages/datadog-plugin-mocha/src/index.js +39 -14
- package/packages/datadog-plugin-mongodb-core/src/bulk-write.js +43 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +8 -443
- package/packages/datadog-plugin-mongodb-core/src/query.js +452 -0
- package/packages/datadog-plugin-playwright/src/index.js +3 -2
- package/packages/datadog-plugin-vitest/src/index.js +43 -9
- package/packages/dd-trace/src/aiguard/index.js +9 -14
- package/packages/dd-trace/src/aiguard/integrations/index.js +34 -0
- package/packages/dd-trace/src/aiguard/integrations/openai.js +47 -33
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +42 -28
- package/packages/dd-trace/src/appsec/graphql.js +9 -6
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +3 -10
- package/packages/dd-trace/src/appsec/lambda.js +8 -8
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +26 -1
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +115 -24
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +3 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +75 -7
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +156 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
- package/packages/dd-trace/src/config/supported-configurations.json +37 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
- package/packages/dd-trace/src/datastreams/writer.js +14 -2
- package/packages/dd-trace/src/encode/0.4.js +23 -2
- package/packages/dd-trace/src/encode/0.5.js +12 -1
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +11 -1
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +20 -2
- package/packages/dd-trace/src/exporters/common/writer.js +17 -1
- package/packages/dd-trace/src/guardrails/index.js +3 -1
- package/packages/dd-trace/src/guardrails/telemetry.js +40 -3
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +403 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +8 -395
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +27 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +363 -0
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +270 -0
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +10 -0
- package/packages/dd-trace/src/msgpack/chunk.js +33 -1
- package/packages/dd-trace/src/msgpack/index.js +6 -1
- package/packages/dd-trace/src/opentelemetry/span.js +5 -0
- package/packages/dd-trace/src/plugin_manager.js +16 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +147 -7
- package/packages/dd-trace/src/plugins/index.js +5 -0
- package/packages/dd-trace/src/plugins/util/test.js +4 -0
- package/packages/dd-trace/src/profiling/exporters/agent.js +28 -3
- package/packages/dd-trace/src/service-naming/schemas/v0/graphql.js +6 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/graphql.js +8 -0
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +5 -6
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { tracingChannel } = require('dc-polyfill')
|
|
4
|
+
const shimmer = require('../../datadog-shimmer')
|
|
5
|
+
const { addHook, getHooks } = require('./helpers/instrument')
|
|
6
|
+
const queryChannel = tracingChannel('orchestrion:@anthropic-ai/claude-agent-sdk:query')
|
|
7
|
+
|
|
8
|
+
const stepCh = tracingChannel('apm:claude-agent-sdk:step')
|
|
9
|
+
const llmCh = tracingChannel('apm:claude-agent-sdk:llm')
|
|
10
|
+
const toolCh = tracingChannel('apm:claude-agent-sdk:tool')
|
|
11
|
+
|
|
12
|
+
const LOCAL_LIFECYCLE_LOOKAHEAD = 4
|
|
13
|
+
|
|
14
|
+
const chunkEmitTimes = new WeakMap()
|
|
15
|
+
|
|
16
|
+
function hasDownstreamSubscribers () {
|
|
17
|
+
return queryChannel.asyncEnd.hasSubscribers ||
|
|
18
|
+
queryChannel.error.hasSubscribers ||
|
|
19
|
+
stepCh.start.hasSubscribers ||
|
|
20
|
+
llmCh.start.hasSubscribers ||
|
|
21
|
+
toolCh.start.hasSubscribers
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function mergeHooks (userHooks, tracerHooks) {
|
|
25
|
+
const merged = {}
|
|
26
|
+
|
|
27
|
+
for (const event of Object.keys(tracerHooks)) {
|
|
28
|
+
const userMatchers = userHooks?.[event] || []
|
|
29
|
+
merged[event] = [...userMatchers, ...tracerHooks[event]]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (userHooks) {
|
|
33
|
+
for (const event of Object.keys(userHooks)) {
|
|
34
|
+
if (!merged[event]) merged[event] = userHooks[event]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return merged
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getTool (sessionCtx, id) {
|
|
42
|
+
let tool = sessionCtx.tools.get(id)
|
|
43
|
+
if (!tool) {
|
|
44
|
+
tool = { id }
|
|
45
|
+
sessionCtx.tools.set(id, tool)
|
|
46
|
+
}
|
|
47
|
+
return tool
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function buildTracerHooks (sessionCtx) {
|
|
51
|
+
function onSessionStart (input) {
|
|
52
|
+
sessionCtx.sessionId = input.session_id
|
|
53
|
+
sessionCtx.source = input.source
|
|
54
|
+
sessionCtx.cwd = input.cwd
|
|
55
|
+
sessionCtx.transcriptPath = input.transcript_path
|
|
56
|
+
sessionCtx.agentType = input.agent_type
|
|
57
|
+
sessionCtx.permissionMode = sessionCtx.permissionMode || input.permission_mode
|
|
58
|
+
return {}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function onSessionEnd (input) {
|
|
62
|
+
sessionCtx.endReason = input.reason
|
|
63
|
+
return {}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function onUserPromptSubmit (input) {
|
|
67
|
+
sessionCtx.sessionId = sessionCtx.sessionId || input.session_id
|
|
68
|
+
sessionCtx.prompt = sessionCtx.prompt || input.prompt
|
|
69
|
+
return {}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function onStop (input) {
|
|
73
|
+
sessionCtx.stopReason = input.stop_reason
|
|
74
|
+
sessionCtx.lastAssistantMessage = input.last_assistant_message
|
|
75
|
+
return {}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function onPreToolUse (input, toolUseId) {
|
|
79
|
+
const id = toolUseId || input.tool_use_id
|
|
80
|
+
if (!id) return {}
|
|
81
|
+
|
|
82
|
+
Object.assign(getTool(sessionCtx, id), {
|
|
83
|
+
id,
|
|
84
|
+
name: input.tool_name,
|
|
85
|
+
input: input.tool_input,
|
|
86
|
+
sessionId: input.session_id,
|
|
87
|
+
hookStartTime: Date.now(),
|
|
88
|
+
})
|
|
89
|
+
return {}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function onPostToolUse (input, toolUseId) {
|
|
93
|
+
const id = toolUseId || input.tool_use_id
|
|
94
|
+
if (!id) return {}
|
|
95
|
+
|
|
96
|
+
const tool = getTool(sessionCtx, id)
|
|
97
|
+
Object.assign(tool, {
|
|
98
|
+
id,
|
|
99
|
+
name: input.tool_name || tool.name,
|
|
100
|
+
input: input.tool_input === undefined ? tool.input : input.tool_input,
|
|
101
|
+
output: input.tool_response,
|
|
102
|
+
sessionId: input.session_id,
|
|
103
|
+
hookFinishTime: Date.now(),
|
|
104
|
+
})
|
|
105
|
+
return {}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function onPostToolUseFailure (input, toolUseId) {
|
|
109
|
+
const id = toolUseId || input.tool_use_id
|
|
110
|
+
if (!id) return {}
|
|
111
|
+
|
|
112
|
+
const tool = getTool(sessionCtx, id)
|
|
113
|
+
Object.assign(tool, {
|
|
114
|
+
id,
|
|
115
|
+
name: input.tool_name || tool.name,
|
|
116
|
+
input: input.tool_input === undefined ? tool.input : input.tool_input,
|
|
117
|
+
error: input.error,
|
|
118
|
+
isInterrupt: input.is_interrupt,
|
|
119
|
+
sessionId: input.session_id,
|
|
120
|
+
hookFinishTime: Date.now(),
|
|
121
|
+
})
|
|
122
|
+
return {}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function onSubagentStart (input) {
|
|
126
|
+
const id = input.agent_id
|
|
127
|
+
if (!id) return {}
|
|
128
|
+
|
|
129
|
+
sessionCtx.subagents.set(id, {
|
|
130
|
+
id,
|
|
131
|
+
sessionId: input.session_id,
|
|
132
|
+
agentType: input.agent_type,
|
|
133
|
+
hookStartTime: Date.now(),
|
|
134
|
+
})
|
|
135
|
+
return {}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function onSubagentStop (input) {
|
|
139
|
+
const id = input.agent_id
|
|
140
|
+
if (!id) return {}
|
|
141
|
+
|
|
142
|
+
const subagent = sessionCtx.subagents.get(id) || { id }
|
|
143
|
+
Object.assign(subagent, {
|
|
144
|
+
sessionId: input.session_id,
|
|
145
|
+
agentType: input.agent_type || subagent.agentType,
|
|
146
|
+
transcriptPath: input.agent_transcript_path,
|
|
147
|
+
output: input.last_assistant_message,
|
|
148
|
+
hookFinishTime: Date.now(),
|
|
149
|
+
})
|
|
150
|
+
sessionCtx.subagents.set(id, subagent)
|
|
151
|
+
return {}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
SessionStart: [{ hooks: [onSessionStart] }],
|
|
156
|
+
SessionEnd: [{ hooks: [onSessionEnd] }],
|
|
157
|
+
UserPromptSubmit: [{ hooks: [onUserPromptSubmit] }],
|
|
158
|
+
Stop: [{ hooks: [onStop] }],
|
|
159
|
+
PreToolUse: [{ hooks: [onPreToolUse] }],
|
|
160
|
+
PostToolUse: [{ hooks: [onPostToolUse] }],
|
|
161
|
+
PostToolUseFailure: [{ hooks: [onPostToolUseFailure] }],
|
|
162
|
+
SubagentStart: [{ hooks: [onSubagentStart] }],
|
|
163
|
+
SubagentStop: [{ hooks: [onSubagentStop] }],
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function onQueryStart (ctx) {
|
|
168
|
+
if (!hasDownstreamSubscribers()) return
|
|
169
|
+
|
|
170
|
+
const { arguments: args } = ctx
|
|
171
|
+
const queryArg = args?.[0]
|
|
172
|
+
if (!queryArg) return
|
|
173
|
+
|
|
174
|
+
const options = queryArg.options || {}
|
|
175
|
+
const prompt = queryArg.prompt
|
|
176
|
+
const sessionCtx = {
|
|
177
|
+
prompt: typeof prompt === 'string' ? prompt : undefined,
|
|
178
|
+
model: options.model,
|
|
179
|
+
resume: options.resume,
|
|
180
|
+
maxTurns: options.maxTurns,
|
|
181
|
+
permissionMode: options.permissionMode,
|
|
182
|
+
tools: new Map(),
|
|
183
|
+
subagents: new Map(),
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
args[0] = {
|
|
187
|
+
...queryArg,
|
|
188
|
+
options: {
|
|
189
|
+
...options,
|
|
190
|
+
hooks: mergeHooks(options.hooks, buildTracerHooks(sessionCtx)),
|
|
191
|
+
},
|
|
192
|
+
}
|
|
193
|
+
ctx.sessionCtx = sessionCtx
|
|
194
|
+
ctx.claudeAgentSdkTracing = true
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function buildStreamIndex (chunks) {
|
|
198
|
+
const lifecycleByToolId = new Map()
|
|
199
|
+
|
|
200
|
+
for (let idx = 0; idx < chunks.length; idx++) {
|
|
201
|
+
const chunk = chunks[idx]
|
|
202
|
+
if (chunk.type === 'system' && chunk.subtype === 'task_started' && chunk.tool_use_id) {
|
|
203
|
+
const lifecycle = lifecycleByToolId.get(chunk.tool_use_id) || {}
|
|
204
|
+
lifecycle.taskStartedChunk = chunk
|
|
205
|
+
lifecycleByToolId.set(chunk.tool_use_id, lifecycle)
|
|
206
|
+
} else if (chunk.type === 'user') {
|
|
207
|
+
const content = chunk.message?.content
|
|
208
|
+
if (!Array.isArray(content)) continue
|
|
209
|
+
for (const block of content) {
|
|
210
|
+
if (block.type === 'tool_result' && block.tool_use_id) {
|
|
211
|
+
const lifecycle = lifecycleByToolId.get(block.tool_use_id) || {}
|
|
212
|
+
if (lifecycle.toolResultIndex === undefined) lifecycle.toolResultIndex = idx
|
|
213
|
+
lifecycleByToolId.set(block.tool_use_id, lifecycle)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return lifecycleByToolId
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function scanLocalLifecycle (chunks, startIndex, toolUseId, lifecycle) {
|
|
223
|
+
lifecycle.taskStartedChunk = undefined
|
|
224
|
+
lifecycle.toolResultIndex = undefined
|
|
225
|
+
|
|
226
|
+
const scanEnd = Math.min(chunks.length, startIndex + LOCAL_LIFECYCLE_LOOKAHEAD)
|
|
227
|
+
|
|
228
|
+
for (let idx = startIndex; idx < scanEnd; idx++) {
|
|
229
|
+
const chunk = chunks[idx]
|
|
230
|
+
if (chunk.type === 'system' && chunk.subtype === 'task_started' && chunk.tool_use_id === toolUseId) {
|
|
231
|
+
lifecycle.taskStartedChunk = chunk
|
|
232
|
+
} else if (chunk.type === 'user') {
|
|
233
|
+
const content = chunk.message?.content
|
|
234
|
+
if (!Array.isArray(content)) continue
|
|
235
|
+
for (const block of content) {
|
|
236
|
+
if (block.type === 'tool_result' && block.tool_use_id === toolUseId) {
|
|
237
|
+
lifecycle.toolResultIndex = idx
|
|
238
|
+
return lifecycle
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return lifecycle
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function createStreamLookup (chunks) {
|
|
248
|
+
let streamIndex
|
|
249
|
+
const localLifecycle = {}
|
|
250
|
+
|
|
251
|
+
return function getLifecycle (startIndex, toolUseId) {
|
|
252
|
+
if (streamIndex) return streamIndex.get(toolUseId) || {}
|
|
253
|
+
|
|
254
|
+
scanLocalLifecycle(chunks, startIndex, toolUseId, localLifecycle)
|
|
255
|
+
if (localLifecycle.toolResultIndex !== undefined) return localLifecycle
|
|
256
|
+
|
|
257
|
+
streamIndex = streamIndex || buildStreamIndex(chunks)
|
|
258
|
+
const indexedLifecycle = streamIndex.get(toolUseId)
|
|
259
|
+
return {
|
|
260
|
+
taskStartedChunk: localLifecycle.taskStartedChunk || indexedLifecycle?.taskStartedChunk,
|
|
261
|
+
toolResultIndex: indexedLifecycle?.toolResultIndex,
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function getToolScanEnd (chunks, startIndex, toolUseId, toolResultIndex) {
|
|
267
|
+
if (toolResultIndex !== undefined) return toolResultIndex + 1
|
|
268
|
+
|
|
269
|
+
for (let idx = startIndex; idx < chunks.length; idx++) {
|
|
270
|
+
const chunk = chunks[idx]
|
|
271
|
+
if (chunk.type === 'result') return idx
|
|
272
|
+
if (chunk.type === 'assistant' && chunk.parent_tool_use_id !== toolUseId) return idx
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return chunks.length
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function getToolResultContent (chunk, toolUseId) {
|
|
279
|
+
if (chunk?.type !== 'user') return
|
|
280
|
+
|
|
281
|
+
const content = chunk.message?.content
|
|
282
|
+
if (!Array.isArray(content)) return
|
|
283
|
+
|
|
284
|
+
for (const block of content) {
|
|
285
|
+
if (block.type === 'tool_result' && block.tool_use_id === toolUseId) return [block]
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
*
|
|
291
|
+
* @param {Array<Record<string, unknown>>} chunks
|
|
292
|
+
* @param {number} startIndex
|
|
293
|
+
* @param {string} toolUseId
|
|
294
|
+
* @returns {number} index in chunks where the next step should start iteration
|
|
295
|
+
*/
|
|
296
|
+
function processTool (chunks, startIndex, toolUseId, sessionCtx, getLifecycle, lifecycle) {
|
|
297
|
+
let chunkIndex = startIndex
|
|
298
|
+
let stepIndex = 0
|
|
299
|
+
const tool = sessionCtx?.tools.get(toolUseId)
|
|
300
|
+
const toolResultIndex = lifecycle.toolResultIndex
|
|
301
|
+
const scanEnd = getToolScanEnd(chunks, startIndex, toolUseId, toolResultIndex)
|
|
302
|
+
|
|
303
|
+
while (chunkIndex < scanEnd) {
|
|
304
|
+
const chunk = chunks[chunkIndex]
|
|
305
|
+
|
|
306
|
+
if (chunkIndex === toolResultIndex) return chunkIndex + 1
|
|
307
|
+
|
|
308
|
+
// only process steps for assistant chunks that belong to this subagent invocation
|
|
309
|
+
if (chunk.type === 'assistant' && chunk.parent_tool_use_id === toolUseId) {
|
|
310
|
+
let prevIdx = chunkIndex - 1
|
|
311
|
+
while (prevIdx >= 0 && chunks[prevIdx].type === 'system') prevIdx--
|
|
312
|
+
const prevChunk = prevIdx >= 0 ? chunks[prevIdx] : chunk
|
|
313
|
+
const stepCtx = {
|
|
314
|
+
stepIndex,
|
|
315
|
+
startTime: chunkEmitTimes.get(prevChunk),
|
|
316
|
+
parentToolUseId: toolUseId,
|
|
317
|
+
sessionId: chunks[0]?.session_id,
|
|
318
|
+
}
|
|
319
|
+
chunkIndex = stepCh.traceSync(() => {
|
|
320
|
+
const nextIdx = processStep(
|
|
321
|
+
chunks,
|
|
322
|
+
chunkIndex,
|
|
323
|
+
stepCtx.startTime,
|
|
324
|
+
toolUseId,
|
|
325
|
+
undefined,
|
|
326
|
+
stepCtx,
|
|
327
|
+
sessionCtx,
|
|
328
|
+
getLifecycle
|
|
329
|
+
)
|
|
330
|
+
stepCtx.finishTime = chunkEmitTimes.get(chunks[Math.min(nextIdx - 1, chunks.length - 1)])
|
|
331
|
+
return nextIdx
|
|
332
|
+
}, stepCtx)
|
|
333
|
+
stepIndex++
|
|
334
|
+
} else {
|
|
335
|
+
chunkIndex++
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (tool?.hookFinishTime) return scanEnd
|
|
340
|
+
|
|
341
|
+
return chunks.length + 1
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
*
|
|
346
|
+
* @param {Array<Record<string, unknown>>} chunks
|
|
347
|
+
* @param {number} startIndex
|
|
348
|
+
* @returns {number} index in chunks where the next step should start iteration
|
|
349
|
+
*/
|
|
350
|
+
function processStep (
|
|
351
|
+
chunks,
|
|
352
|
+
startIndex,
|
|
353
|
+
stepStartTime,
|
|
354
|
+
parentToolUseId = null,
|
|
355
|
+
initialPrompt,
|
|
356
|
+
stepCtx = null,
|
|
357
|
+
sessionCtx = null,
|
|
358
|
+
getLifecycle
|
|
359
|
+
) {
|
|
360
|
+
for (let idx = startIndex; idx < chunks.length; idx++) {
|
|
361
|
+
const chunk = chunks[idx]
|
|
362
|
+
|
|
363
|
+
if (chunk.type !== 'assistant') continue
|
|
364
|
+
|
|
365
|
+
const { id: messageId, model, usage } = chunk.message
|
|
366
|
+
|
|
367
|
+
// collect all chunks belonging to the same message (parallel tool_uses arrive as separate chunks)
|
|
368
|
+
const toolUses = []
|
|
369
|
+
let messageEndIdx = idx
|
|
370
|
+
for (let j = idx; j < chunks.length; j++) {
|
|
371
|
+
const c = chunks[j]
|
|
372
|
+
if (c.type !== 'assistant' || c.message.id !== messageId) break
|
|
373
|
+
if (c.message.content[0]?.type === 'tool_use') {
|
|
374
|
+
toolUses.push({ toolUse: c.message.content[0], startTime: chunkEmitTimes.get(c) })
|
|
375
|
+
}
|
|
376
|
+
messageEndIdx = j + 1
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (stepCtx) {
|
|
380
|
+
stepCtx.chunks = chunks
|
|
381
|
+
stepCtx.llmStartIdx = idx
|
|
382
|
+
stepCtx.llmEndIdx = messageEndIdx
|
|
383
|
+
stepCtx.toolOutputs = []
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
llmCh.traceSync(() => {}, {
|
|
387
|
+
model,
|
|
388
|
+
usage,
|
|
389
|
+
startTime: stepStartTime,
|
|
390
|
+
finishTime: chunkEmitTimes.get(chunks[messageEndIdx - 1]),
|
|
391
|
+
chunks,
|
|
392
|
+
llmStartIdx: idx,
|
|
393
|
+
llmEndIdx: messageEndIdx,
|
|
394
|
+
parentToolUseId,
|
|
395
|
+
initialPrompt,
|
|
396
|
+
sessionId: chunks[0]?.session_id,
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
if (toolUses.length === 0) {
|
|
400
|
+
return messageEndIdx
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// for parallel tool calls, each processTool starts from the same messageEndIdx
|
|
404
|
+
// and independently scans for its own tool_result; take the max to advance past all results
|
|
405
|
+
let nextIdx = messageEndIdx
|
|
406
|
+
for (const { toolUse: { id, name, input }, startTime: toolUseStartTime } of toolUses) {
|
|
407
|
+
const hookTool = sessionCtx?.tools.get(id)
|
|
408
|
+
const lifecycle = getLifecycle(messageEndIdx, id)
|
|
409
|
+
// Agent tools emit a system:task_started chunk with a more precise start time
|
|
410
|
+
const taskStartedChunk = lifecycle.taskStartedChunk
|
|
411
|
+
const toolCtx = {
|
|
412
|
+
id,
|
|
413
|
+
name: hookTool?.name || name,
|
|
414
|
+
input: hookTool?.input || input,
|
|
415
|
+
startTime: hookTool?.hookStartTime ||
|
|
416
|
+
(taskStartedChunk ? chunkEmitTimes.get(taskStartedChunk) : toolUseStartTime),
|
|
417
|
+
sessionId: chunks[0]?.session_id,
|
|
418
|
+
}
|
|
419
|
+
const toolEndIdx = toolCh.traceSync(() => {
|
|
420
|
+
const endIdx = processTool(chunks, messageEndIdx, id, sessionCtx, getLifecycle, lifecycle)
|
|
421
|
+
const resultChunk = chunks[endIdx - 1]
|
|
422
|
+
if (hookTool?.isInterrupt) toolCtx.isInterrupt = hookTool.isInterrupt
|
|
423
|
+
if (hookTool?.error) {
|
|
424
|
+
toolCtx.error = hookTool.error
|
|
425
|
+
toolCh.error.publish(toolCtx)
|
|
426
|
+
}
|
|
427
|
+
const resultOutput = getToolResultContent(resultChunk, id)
|
|
428
|
+
if (resultOutput) {
|
|
429
|
+
toolCtx.output = resultOutput
|
|
430
|
+
} else if (hookTool?.output) {
|
|
431
|
+
toolCtx.output = hookTool.output
|
|
432
|
+
}
|
|
433
|
+
toolCtx.finishTime = hookTool?.hookFinishTime ||
|
|
434
|
+
chunkEmitTimes.get(chunks[Math.min(endIdx - 1, chunks.length - 1)])
|
|
435
|
+
return endIdx
|
|
436
|
+
}, toolCtx)
|
|
437
|
+
|
|
438
|
+
if (stepCtx && toolCtx.output) stepCtx.toolOutputs.push(toolCtx.output)
|
|
439
|
+
|
|
440
|
+
nextIdx = Math.max(nextIdx, toolEndIdx)
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return nextIdx
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return chunks.length + 1
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* @param {Array<Record<string, unknown>>} chunks
|
|
451
|
+
*/
|
|
452
|
+
function processChunks (chunks, agentCtx) {
|
|
453
|
+
let chunkIndex = 0
|
|
454
|
+
let stepIndex = 0
|
|
455
|
+
const sessionCtx = agentCtx.sessionCtx
|
|
456
|
+
const getLifecycle = createStreamLookup(chunks)
|
|
457
|
+
|
|
458
|
+
const { type, subtype, ...rest } = chunks[0]
|
|
459
|
+
Object.assign(agentCtx, rest)
|
|
460
|
+
if (sessionCtx) {
|
|
461
|
+
if (sessionCtx.sessionId) agentCtx.session_id = sessionCtx.sessionId
|
|
462
|
+
if (sessionCtx.cwd) agentCtx.cwd = sessionCtx.cwd
|
|
463
|
+
if (sessionCtx.permissionMode) agentCtx.permissionMode = sessionCtx.permissionMode
|
|
464
|
+
if (sessionCtx.lastAssistantMessage && !agentCtx.output) agentCtx.output = sessionCtx.lastAssistantMessage
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
while (chunkIndex < chunks.length) {
|
|
468
|
+
if (chunks[chunkIndex].type === 'result') break
|
|
469
|
+
|
|
470
|
+
const prevChunk = chunkIndex > 0 ? chunks[chunkIndex - 1] : chunks[chunkIndex]
|
|
471
|
+
const stepCtx = { stepIndex, startTime: chunkEmitTimes.get(prevChunk), sessionId: agentCtx.session_id }
|
|
472
|
+
|
|
473
|
+
const run = agentCtx.runInContext ?? (fn => fn())
|
|
474
|
+
chunkIndex = run(() => stepCh.traceSync(() => {
|
|
475
|
+
const initialPrompt = sessionCtx?.prompt || agentCtx.arguments?.[0]?.prompt
|
|
476
|
+
const nextIdx = processStep(
|
|
477
|
+
chunks,
|
|
478
|
+
chunkIndex,
|
|
479
|
+
stepCtx.startTime,
|
|
480
|
+
null,
|
|
481
|
+
initialPrompt,
|
|
482
|
+
stepCtx,
|
|
483
|
+
sessionCtx,
|
|
484
|
+
getLifecycle
|
|
485
|
+
)
|
|
486
|
+
stepCtx.finishTime = chunkEmitTimes.get(chunks[Math.min(nextIdx - 1, chunks.length - 1)])
|
|
487
|
+
return nextIdx
|
|
488
|
+
}, stepCtx))
|
|
489
|
+
|
|
490
|
+
stepIndex++
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function finishStream (chunks, ctx, error) {
|
|
495
|
+
if (ctx.streamResolved) return
|
|
496
|
+
|
|
497
|
+
let processError
|
|
498
|
+
if (chunks.length > 0) {
|
|
499
|
+
const lastChunk = chunks[chunks.length - 1]
|
|
500
|
+
if (lastChunk?.type === 'result') ctx.output = lastChunk.result
|
|
501
|
+
|
|
502
|
+
try {
|
|
503
|
+
processChunks(chunks, ctx)
|
|
504
|
+
} catch (e) {
|
|
505
|
+
processError = e
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const finalError = error || processError
|
|
510
|
+
ctx.finishTime = Date.now()
|
|
511
|
+
|
|
512
|
+
if (finalError) {
|
|
513
|
+
ctx.error = finalError
|
|
514
|
+
queryChannel.error.publish(ctx)
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
ctx.streamResolved = true
|
|
518
|
+
queryChannel.asyncEnd.publish(ctx)
|
|
519
|
+
|
|
520
|
+
if (processError && !error) throw processError
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function wrapQueryAsyncIterator (asyncIterator, ctx) {
|
|
524
|
+
const chunks = []
|
|
525
|
+
|
|
526
|
+
return function () {
|
|
527
|
+
const iterator = asyncIterator.apply(this, arguments)
|
|
528
|
+
iterator.next = shimmer.wrapCallback(iterator.next, next => function () {
|
|
529
|
+
return next.apply(this, arguments).then(result => {
|
|
530
|
+
const { done, value } = result
|
|
531
|
+
|
|
532
|
+
if (!done && value) {
|
|
533
|
+
const chunkEmitTime = Date.now()
|
|
534
|
+
chunks.push(value)
|
|
535
|
+
chunkEmitTimes.set(value, chunkEmitTime)
|
|
536
|
+
} else {
|
|
537
|
+
finishStream(chunks, ctx)
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
return result
|
|
541
|
+
}).catch(error => {
|
|
542
|
+
finishStream(chunks, ctx, error)
|
|
543
|
+
throw error
|
|
544
|
+
})
|
|
545
|
+
})
|
|
546
|
+
|
|
547
|
+
if (typeof iterator.return === 'function') {
|
|
548
|
+
iterator.return = shimmer.wrapCallback(iterator.return, iteratorReturn => function () {
|
|
549
|
+
return iteratorReturn.apply(this, arguments).then(result => {
|
|
550
|
+
finishStream(chunks, ctx)
|
|
551
|
+
return result
|
|
552
|
+
}).catch(error => {
|
|
553
|
+
finishStream(chunks, ctx, error)
|
|
554
|
+
throw error
|
|
555
|
+
})
|
|
556
|
+
})
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return iterator
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
let querySubscribed = false
|
|
564
|
+
|
|
565
|
+
for (const hook of getHooks('@anthropic-ai/claude-agent-sdk')) {
|
|
566
|
+
hook.file = null
|
|
567
|
+
|
|
568
|
+
addHook(hook, exports => {
|
|
569
|
+
if (!querySubscribed) {
|
|
570
|
+
querySubscribed = true
|
|
571
|
+
queryChannel.subscribe({
|
|
572
|
+
start: onQueryStart,
|
|
573
|
+
end (ctx) {
|
|
574
|
+
if (!ctx.claudeAgentSdkTracing) return
|
|
575
|
+
|
|
576
|
+
const { result } = ctx
|
|
577
|
+
|
|
578
|
+
ctx.streamResolved = false
|
|
579
|
+
|
|
580
|
+
shimmer.wrap(result, Symbol.asyncIterator, asyncIterator => wrapQueryAsyncIterator(asyncIterator, ctx))
|
|
581
|
+
},
|
|
582
|
+
})
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return exports
|
|
586
|
+
})
|
|
587
|
+
}
|