dd-trace 6.0.0 → 6.1.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.
Files changed (77) hide show
  1. package/LICENSE-3rdparty.csv +1 -2
  2. package/index.d.ts +21 -1
  3. package/package.json +32 -29
  4. package/packages/datadog-instrumentations/src/ai.js +45 -0
  5. package/packages/datadog-instrumentations/src/apollo-server.js +5 -13
  6. package/packages/datadog-instrumentations/src/child_process.js +3 -3
  7. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +587 -0
  8. package/packages/datadog-instrumentations/src/cucumber.js +102 -43
  9. package/packages/datadog-instrumentations/src/cypress-config.js +11 -3
  10. package/packages/datadog-instrumentations/src/fastify.js +27 -8
  11. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  12. package/packages/datadog-instrumentations/src/helpers/instrument.js +7 -0
  13. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +6 -6
  14. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/claude-agent-sdk.js +17 -0
  15. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  16. package/packages/datadog-instrumentations/src/jest.js +713 -63
  17. package/packages/datadog-instrumentations/src/mocha/main.js +24 -3
  18. package/packages/datadog-instrumentations/src/mocha/utils.js +128 -22
  19. package/packages/datadog-instrumentations/src/mocha/worker.js +13 -0
  20. package/packages/datadog-instrumentations/src/mongodb.js +74 -0
  21. package/packages/datadog-instrumentations/src/mongoose.js +4 -5
  22. package/packages/datadog-instrumentations/src/playwright.js +13 -9
  23. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +4 -0
  24. package/packages/datadog-instrumentations/src/vitest-main.js +28 -1
  25. package/packages/datadog-instrumentations/src/vitest-util.js +5 -0
  26. package/packages/datadog-instrumentations/src/vitest-worker.js +104 -34
  27. package/packages/datadog-plugin-ai/src/index.js +1 -1
  28. package/packages/datadog-plugin-ai/src/tracing.js +66 -3
  29. package/packages/datadog-plugin-ai/src/utils.js +17 -4
  30. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +1 -0
  31. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -0
  32. package/packages/datadog-plugin-child_process/src/index.js +13 -2
  33. package/packages/datadog-plugin-claude-agent-sdk/src/index.js +31 -0
  34. package/packages/datadog-plugin-claude-agent-sdk/src/tracing.js +107 -0
  35. package/packages/datadog-plugin-claude-agent-sdk/src/util.js +15 -0
  36. package/packages/datadog-plugin-cucumber/src/index.js +14 -23
  37. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +351 -11
  38. package/packages/datadog-plugin-cypress/src/index.js +47 -2
  39. package/packages/datadog-plugin-cypress/src/plugin.js +1 -0
  40. package/packages/datadog-plugin-graphql/src/execute.js +21 -10
  41. package/packages/datadog-plugin-graphql/src/index.js +17 -1
  42. package/packages/datadog-plugin-graphql/src/utils.js +9 -2
  43. package/packages/datadog-plugin-graphql/src/validate.js +1 -1
  44. package/packages/datadog-plugin-jest/src/index.js +30 -14
  45. package/packages/datadog-plugin-mocha/src/index.js +39 -14
  46. package/packages/datadog-plugin-mongodb-core/src/bulk-write.js +43 -0
  47. package/packages/datadog-plugin-mongodb-core/src/index.js +8 -443
  48. package/packages/datadog-plugin-mongodb-core/src/query.js +452 -0
  49. package/packages/datadog-plugin-playwright/src/index.js +3 -2
  50. package/packages/datadog-plugin-vitest/src/index.js +51 -9
  51. package/packages/dd-trace/src/aiguard/index.js +9 -14
  52. package/packages/dd-trace/src/aiguard/integrations/index.js +34 -0
  53. package/packages/dd-trace/src/aiguard/integrations/openai.js +47 -33
  54. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +42 -28
  55. package/packages/dd-trace/src/appsec/graphql.js +9 -6
  56. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +3 -10
  57. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +26 -1
  58. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +115 -24
  59. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +3 -0
  60. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -0
  61. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +75 -7
  62. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +156 -0
  63. package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
  64. package/packages/dd-trace/src/config/supported-configurations.json +37 -0
  65. package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
  66. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +403 -0
  67. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +8 -395
  68. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +27 -0
  69. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +363 -0
  70. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +270 -0
  71. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +10 -0
  72. package/packages/dd-trace/src/opentelemetry/span.js +5 -0
  73. package/packages/dd-trace/src/plugin_manager.js +16 -0
  74. package/packages/dd-trace/src/plugins/ci_plugin.js +147 -7
  75. package/packages/dd-trace/src/plugins/index.js +2 -0
  76. package/packages/dd-trace/src/plugins/util/test.js +4 -0
  77. package/packages/dd-trace/src/profiling/exporters/agent.js +28 -3
@@ -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
+ }