langsmith 0.5.25 → 0.6.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 (63) hide show
  1. package/dist/client.cjs +50 -20
  2. package/dist/client.d.ts +40 -3
  3. package/dist/client.js +50 -20
  4. package/dist/experimental/anthropic/context.cjs +419 -49
  5. package/dist/experimental/anthropic/context.js +420 -50
  6. package/dist/experimental/anthropic/index.cjs +78 -10
  7. package/dist/experimental/anthropic/index.js +80 -12
  8. package/dist/experimental/anthropic/messages.cjs +53 -0
  9. package/dist/experimental/anthropic/messages.d.ts +6 -0
  10. package/dist/experimental/anthropic/messages.js +52 -0
  11. package/dist/experimental/anthropic/transcripts.cjs +144 -0
  12. package/dist/experimental/anthropic/transcripts.d.ts +22 -0
  13. package/dist/experimental/anthropic/transcripts.js +141 -0
  14. package/dist/experimental/anthropic/types.d.ts +1 -0
  15. package/dist/experimental/anthropic/usage.cjs +19 -20
  16. package/dist/experimental/anthropic/usage.d.ts +2 -1
  17. package/dist/experimental/anthropic/usage.js +18 -20
  18. package/dist/experimental/opencode/index.cjs +36 -0
  19. package/dist/experimental/opencode/index.d.ts +3 -0
  20. package/dist/experimental/opencode/index.js +32 -0
  21. package/dist/experimental/opencode/tracer.cjs +389 -0
  22. package/dist/experimental/opencode/tracer.d.ts +30 -0
  23. package/dist/experimental/opencode/tracer.js +385 -0
  24. package/dist/experimental/otel/setup.cjs +1 -1
  25. package/dist/experimental/otel/setup.js +1 -1
  26. package/dist/experimental/sandbox/sandbox.cjs +6 -2
  27. package/dist/experimental/sandbox/sandbox.d.ts +5 -1
  28. package/dist/experimental/sandbox/sandbox.js +6 -2
  29. package/dist/experimental/sandbox/types.d.ts +3 -1
  30. package/dist/experimental/vercel/index.cjs +1 -1
  31. package/dist/experimental/vercel/index.js +1 -1
  32. package/dist/experimental/vercel/middleware.cjs +2 -1
  33. package/dist/experimental/vercel/middleware.js +2 -1
  34. package/dist/index.cjs +1 -1
  35. package/dist/index.d.ts +1 -1
  36. package/dist/index.js +1 -1
  37. package/dist/singletons/traceable.cjs +1 -3
  38. package/dist/singletons/traceable.js +1 -3
  39. package/dist/traceable.cjs +1 -3
  40. package/dist/traceable.js +1 -3
  41. package/dist/utils/_git.cjs +2 -2
  42. package/dist/utils/_git.js +2 -2
  43. package/dist/utils/env.cjs +2 -2
  44. package/dist/utils/env.js +2 -2
  45. package/dist/utils/error.cjs +2 -2
  46. package/dist/utils/error.js +2 -2
  47. package/dist/utils/jestlike/reporter.cjs +1 -1
  48. package/dist/utils/jestlike/reporter.js +1 -1
  49. package/dist/utils/jestlike/vendor/chain.cjs +2 -3
  50. package/dist/utils/jestlike/vendor/chain.js +2 -3
  51. package/dist/utils/serialize_worker.cjs +1 -2
  52. package/dist/utils/serialize_worker.d.ts +1 -2
  53. package/dist/utils/serialize_worker.js +1 -2
  54. package/dist/vitest/utils/esm.mjs +4 -4
  55. package/dist/wrappers/gemini.cjs +1 -1
  56. package/dist/wrappers/gemini.js +1 -1
  57. package/dist/wrappers/openai.cjs +2 -6
  58. package/dist/wrappers/openai.js +2 -6
  59. package/experimental/opencode.cjs +1 -0
  60. package/experimental/opencode.d.cts +1 -0
  61. package/experimental/opencode.d.ts +1 -0
  62. package/experimental/opencode.js +1 -0
  63. package/package.json +24 -18
@@ -4,11 +4,27 @@ exports.StreamManager = void 0;
4
4
  const messages_js_1 = require("./messages.cjs");
5
5
  const traceable_js_1 = require("../../traceable.cjs");
6
6
  const usage_js_1 = require("./usage.cjs");
7
+ const transcripts_js_1 = require("./transcripts.cjs");
8
+ function isRecord(value) {
9
+ return typeof value === "object" && value != null && !Array.isArray(value);
10
+ }
11
+ function isToolResultError(value) {
12
+ return isRecord(value) && (value.is_error === true || value.isError === true);
13
+ }
14
+ function makeSubagentTranscriptPathKey(path, toolUseId, agentType) {
15
+ return JSON.stringify([path, toolUseId ?? null, agentType ?? null]);
16
+ }
7
17
  /**
8
18
  * @internal
9
19
  */
10
20
  class StreamManager {
11
21
  constructor() {
22
+ Object.defineProperty(this, "rootRun", {
23
+ enumerable: true,
24
+ configurable: true,
25
+ writable: true,
26
+ value: void 0
27
+ });
12
28
  Object.defineProperty(this, "namespaces", {
13
29
  enumerable: true,
14
30
  configurable: true,
@@ -33,6 +49,48 @@ class StreamManager {
33
49
  writable: true,
34
50
  value: {}
35
51
  });
52
+ Object.defineProperty(this, "subagents", {
53
+ enumerable: true,
54
+ configurable: true,
55
+ writable: true,
56
+ value: {}
57
+ });
58
+ Object.defineProperty(this, "mainTranscriptPath", {
59
+ enumerable: true,
60
+ configurable: true,
61
+ writable: true,
62
+ value: void 0
63
+ });
64
+ Object.defineProperty(this, "subagentTranscriptPaths", {
65
+ enumerable: true,
66
+ configurable: true,
67
+ writable: true,
68
+ value: []
69
+ });
70
+ Object.defineProperty(this, "pendingAgentTools", {
71
+ enumerable: true,
72
+ configurable: true,
73
+ writable: true,
74
+ value: new Map()
75
+ });
76
+ Object.defineProperty(this, "agentToToolUseId", {
77
+ enumerable: true,
78
+ configurable: true,
79
+ writable: true,
80
+ value: new Map()
81
+ });
82
+ Object.defineProperty(this, "transcriptPathKeys", {
83
+ enumerable: true,
84
+ configurable: true,
85
+ writable: true,
86
+ value: new Set()
87
+ });
88
+ Object.defineProperty(this, "resultModelUsage", {
89
+ enumerable: true,
90
+ configurable: true,
91
+ writable: true,
92
+ value: void 0
93
+ });
36
94
  Object.defineProperty(this, "postRunQueue", {
37
95
  enumerable: true,
38
96
  configurable: true,
@@ -46,10 +104,21 @@ class StreamManager {
46
104
  value: []
47
105
  });
48
106
  const rootRun = (0, traceable_js_1.getCurrentRunTree)(true);
107
+ this.rootRun = rootRun;
49
108
  this.namespaces = rootRun?.createChild ? { root: rootRun } : {};
50
109
  this.history = { root: [] };
110
+ if (rootRun != null) {
111
+ StreamManager.managersByRootRun.set(rootRun, this);
112
+ }
113
+ StreamManager.liveManagers.add(this);
51
114
  }
52
- addMessage(message) {
115
+ dispose() {
116
+ StreamManager.liveManagers.delete(this);
117
+ if (this.rootRun != null) {
118
+ StreamManager.managersByRootRun.delete(this.rootRun);
119
+ }
120
+ }
121
+ async addMessage(message) {
53
122
  const eventTime = Date.now();
54
123
  // Short-circuit if no root run found
55
124
  // This can happen if tracing is disabled globally
@@ -57,7 +126,7 @@ class StreamManager {
57
126
  return;
58
127
  if (message.type === "result") {
59
128
  if (message.modelUsage) {
60
- (0, usage_js_1.correctUsageFromResults)(message.modelUsage, Object.values(this.assistant).filter((runTree) => runTree != null));
129
+ this.resultModelUsage = message.modelUsage;
61
130
  }
62
131
  const usage = message.modelUsage
63
132
  ? (0, usage_js_1.aggregateUsageFromModelUsage)(message.modelUsage)
@@ -105,7 +174,11 @@ class StreamManager {
105
174
  this.assistant[messageId].outputs = (() => {
106
175
  const prevMessages = this.assistant[messageId].outputs?.output.messages ?? [];
107
176
  const newMessages = (0, messages_js_1.convertFromAnthropicMessage)([message]);
108
- return { output: { messages: [...prevMessages, ...newMessages] } };
177
+ return {
178
+ output: {
179
+ messages: (0, messages_js_1.mergeMessagesById)(prevMessages, newMessages),
180
+ },
181
+ };
109
182
  })();
110
183
  this.assistant[messageId].end_time = eventTime;
111
184
  this.assistant[messageId].extra ??= {};
@@ -121,35 +194,10 @@ class StreamManager {
121
194
  : [];
122
195
  for (const block of tools) {
123
196
  if ((0, messages_js_1.isTaskTool)(block)) {
124
- const name = block.input.subagent_type ||
125
- block.input.agent_type ||
126
- (block.input.description
127
- ? block.input.description.split(" ")[0]
128
- : null) ||
129
- "unknown-agent";
130
- this.tools[block.id] ??=
131
- this.createChild("root", {
132
- name,
133
- run_type: "chain",
134
- inputs: block.input,
135
- start_time: eventTime,
136
- extra: {
137
- metadata: {
138
- ls_agent_type: "subagent",
139
- },
140
- },
141
- }) ?? this.tools[block.id];
142
- this.namespaces[block.id] ??= this.tools[block.id];
197
+ this.createAgentToolRun(namespace, block, eventTime);
143
198
  }
144
199
  else {
145
- const name = block.name || "unknown-tool";
146
- this.tools[block.id] ??=
147
- this.createChild(namespace, {
148
- name,
149
- run_type: "tool",
150
- inputs: block.input ? { input: block.input } : {},
151
- start_time: eventTime,
152
- }) ?? this.tools[block.id];
200
+ this.createToolRun(namespace, block, eventTime);
153
201
  }
154
202
  }
155
203
  }
@@ -158,9 +206,7 @@ class StreamManager {
158
206
  ? message.message.content.filter((block) => "tool_use_id" in block)
159
207
  : [];
160
208
  const getToolOutput = (result) => {
161
- if (typeof result === "object" &&
162
- result != null &&
163
- !Array.isArray(result)) {
209
+ if (isRecord(result)) {
164
210
  return result;
165
211
  }
166
212
  return { content: result };
@@ -169,10 +215,23 @@ class StreamManager {
169
215
  if (["string", "number", "boolean"].includes(typeof result)) {
170
216
  return String(result);
171
217
  }
218
+ if (Array.isArray(result)) {
219
+ return result.map(getToolError).join("\n");
220
+ }
221
+ if (isRecord(result)) {
222
+ if (typeof result.error === "string")
223
+ return result.error;
224
+ if (typeof result.text === "string")
225
+ return result.text;
226
+ if ("content" in result)
227
+ return getToolError(result.content);
228
+ }
172
229
  return JSON.stringify(result);
173
230
  };
174
231
  for (const block of toolResultBlocks) {
175
- if (this.tools[block.tool_use_id] != null) {
232
+ const tool = this.tools[block.tool_use_id];
233
+ const subagent = this.subagents[block.tool_use_id];
234
+ if (tool != null || subagent != null) {
176
235
  // Previous versions of @anthropic-ai/claude-agent-sdk did provide
177
236
  // tool result in `message.tool_use_result`, but at least since 0.2.50 it disappeared,
178
237
  // so we rely on the last tool result block instead.
@@ -180,36 +239,347 @@ class StreamManager {
180
239
  ? message.tool_use_result
181
240
  : block.content;
182
241
  const toolOutput = getToolOutput(result);
183
- const toolError = "is_error" in block && block.is_error === true
184
- ? getToolError(result)
185
- : undefined;
186
- void this.tools[block.tool_use_id]?.end(toolOutput, toolError);
242
+ const isError = isToolResultError(block) || isToolResultError(result);
243
+ const toolError = isError ? getToolError(result) : undefined;
244
+ await tool?.end(toolOutput, toolError);
245
+ // Match Python's lifecycle: PostToolUse sets outputs on the
246
+ // subagent chain, but the subagent itself is not ended until after
247
+ // transcript reconciliation. Hidden transcript LLM/tool children can
248
+ // arrive after the Agent/Task tool result, so ending the chain here
249
+ // can make reconciled children appear outside their parent bounds.
250
+ if (subagent != null) {
251
+ subagent.outputs ??= toolOutput;
252
+ subagent.error ??= toolError;
253
+ }
187
254
  }
188
255
  }
189
256
  }
190
257
  this.history[namespace].push(message);
191
258
  }
259
+ addHookEvent(input, toolUseId) {
260
+ if (typeof input !== "object" || input == null)
261
+ return;
262
+ const data = input;
263
+ if (this.mainTranscriptPath == null &&
264
+ typeof data.transcript_path === "string") {
265
+ this.mainTranscriptPath = data.transcript_path;
266
+ }
267
+ if (data.hook_event_name === "PreToolUse" &&
268
+ typeof toolUseId === "string" &&
269
+ (data.tool_name === "Agent" || data.tool_name === "Task")) {
270
+ this.pendingAgentTools.set(toolUseId, typeof data.tool_input === "object" && data.tool_input != null
271
+ ? data.tool_input
272
+ : {});
273
+ return;
274
+ }
275
+ if (data.hook_event_name === "SubagentStart") {
276
+ const agentId = typeof data.agent_id === "string" ? data.agent_id : undefined;
277
+ if (agentId == null)
278
+ return;
279
+ const agentType = typeof data.agent_type === "string" ? data.agent_type : undefined;
280
+ let matchedToolUseId;
281
+ for (const [pendingToolUseId, toolInput] of this.pendingAgentTools) {
282
+ const pendingAgentType = typeof toolInput.subagent_type === "string"
283
+ ? toolInput.subagent_type
284
+ : typeof toolInput.agent_type === "string"
285
+ ? toolInput.agent_type
286
+ : undefined;
287
+ if (agentType == null ||
288
+ pendingAgentType == null ||
289
+ pendingAgentType === agentType) {
290
+ matchedToolUseId = pendingToolUseId;
291
+ break;
292
+ }
293
+ }
294
+ matchedToolUseId ??= this.pendingAgentTools.keys().next().value;
295
+ if (matchedToolUseId != null) {
296
+ this.agentToToolUseId.set(agentId, matchedToolUseId);
297
+ this.pendingAgentTools.delete(matchedToolUseId);
298
+ }
299
+ return;
300
+ }
301
+ if (data.hook_event_name === "SubagentStop") {
302
+ const transcriptPath = typeof data.agent_transcript_path === "string"
303
+ ? data.agent_transcript_path
304
+ : undefined;
305
+ if (!transcriptPath)
306
+ return;
307
+ const agentId = typeof data.agent_id === "string" ? data.agent_id : undefined;
308
+ const agentType = typeof data.agent_type === "string" ? data.agent_type : undefined;
309
+ const mappedToolUseId = agentId != null ? this.agentToToolUseId.get(agentId) : undefined;
310
+ if (agentId != null)
311
+ this.agentToToolUseId.delete(agentId);
312
+ this.addSubagentTranscriptPath(transcriptPath, mappedToolUseId, agentType);
313
+ }
314
+ }
315
+ addSubagentTranscriptPath(path, toolUseId, agentType) {
316
+ const key = makeSubagentTranscriptPathKey(path, toolUseId, agentType);
317
+ if (this.transcriptPathKeys.has(key))
318
+ return;
319
+ this.transcriptPathKeys.add(key);
320
+ this.subagentTranscriptPaths.push({ path, toolUseId, agentType });
321
+ }
322
+ static getActiveToolRun(toolName, input) {
323
+ const currentRun = (0, traceable_js_1.getCurrentRunTree)(true);
324
+ const currentManager = currentRun != null
325
+ ? StreamManager.managersByRootRun.get(currentRun)
326
+ : undefined;
327
+ const currentRunTree = currentManager?.getActiveToolRun(toolName, input);
328
+ if (currentRunTree != null)
329
+ return currentRunTree;
330
+ // Last resort: the SDK invoked an MCP handler from a detached async context
331
+ // that did not inherit the existing LangSmith AsyncLocalStorage. Require
332
+ // both tool name and input to match before scanning live managers to avoid
333
+ // cross-query attribution.
334
+ if (toolName == null || input === undefined)
335
+ return undefined;
336
+ for (const manager of Array.from(StreamManager.liveManagers).reverse()) {
337
+ if (manager === currentManager)
338
+ continue;
339
+ const runTree = manager.getActiveToolRun(toolName, input);
340
+ if (runTree != null)
341
+ return runTree;
342
+ }
343
+ return undefined;
344
+ }
345
+ getActiveToolRun(toolName, input) {
346
+ const toolEntries = Object.values(this.tools).filter((runTree) => runTree != null && runTree.end_time == null && runTree.error == null);
347
+ return toolEntries.find((runTree) => {
348
+ if (toolName != null) {
349
+ const runName = String(runTree.name);
350
+ const nameMatches = runName === toolName ||
351
+ runName.includes(toolName) ||
352
+ toolName.includes(runName);
353
+ if (!nameMatches)
354
+ return false;
355
+ }
356
+ if (input !== undefined) {
357
+ const recorded = runTree.inputs?.input ?? {};
358
+ try {
359
+ return JSON.stringify(recorded) === JSON.stringify(input ?? {});
360
+ }
361
+ catch {
362
+ return false;
363
+ }
364
+ }
365
+ return true;
366
+ });
367
+ }
192
368
  createChild(namespace, args) {
193
- const runTree = this.namespaces[namespace]?.createChild(args);
369
+ const parentRunTree = this.namespaces[namespace];
370
+ if (parentRunTree == null)
371
+ return undefined;
372
+ return this.createChildRun(parentRunTree, args);
373
+ }
374
+ createChildRun(parentRunTree, args) {
375
+ const runTree = parentRunTree.createChild(args);
194
376
  if (runTree == null)
195
377
  return undefined;
196
378
  this.postRunQueue.push(runTree.postRun());
197
379
  this.runTrees.push(runTree);
198
380
  return runTree;
199
381
  }
200
- async finish() {
201
- // Clean up incomplete tools and subagent calls
202
- for (const tool of Object.values(this.tools)) {
203
- if (tool == null)
382
+ getAgentName(block) {
383
+ const subagentType = block.input.subagent_type;
384
+ if (typeof subagentType === "string" && subagentType.length > 0) {
385
+ return subagentType;
386
+ }
387
+ const agentType = block.input.agent_type;
388
+ if (typeof agentType === "string" && agentType.length > 0) {
389
+ return agentType;
390
+ }
391
+ const description = block.input.description;
392
+ if (typeof description === "string" && description.length > 0) {
393
+ return description.split(" ")[0] || "unknown-agent";
394
+ }
395
+ return "unknown-agent";
396
+ }
397
+ createToolRun(namespace, block, startTime) {
398
+ if (typeof block.id !== "string")
399
+ return undefined;
400
+ const name = typeof block.name === "string" ? block.name : "unknown-tool";
401
+ this.tools[block.id] ??=
402
+ this.createChild(namespace, {
403
+ name,
404
+ run_type: "tool",
405
+ inputs: block.input ? { input: block.input } : {},
406
+ start_time: startTime,
407
+ }) ?? this.tools[block.id];
408
+ return this.tools[block.id];
409
+ }
410
+ createAgentToolRun(namespace, block, startTime) {
411
+ if (typeof block.id !== "string")
412
+ return;
413
+ if (typeof block.input !== "object" ||
414
+ block.input == null ||
415
+ Array.isArray(block.input)) {
416
+ return;
417
+ }
418
+ const input = block.input;
419
+ const agentToolRun = this.createToolRun(namespace, block, startTime);
420
+ if (agentToolRun == null)
421
+ return;
422
+ this.subagents[block.id] ??=
423
+ this.createChildRun(agentToolRun, {
424
+ name: this.getAgentName({ input }),
425
+ run_type: "chain",
426
+ inputs: input,
427
+ start_time: startTime,
428
+ extra: {
429
+ metadata: {
430
+ ls_agent_type: "subagent",
431
+ },
432
+ },
433
+ }) ?? this.subagents[block.id];
434
+ this.namespaces[block.id] ??= this.subagents[block.id];
435
+ }
436
+ resolveSubagentNamespace(agentType) {
437
+ const entries = Object.entries(this.namespaces);
438
+ if (agentType == null)
439
+ return undefined;
440
+ return entries.find(([, runTree]) => runTree?.name === agentType)?.[0];
441
+ }
442
+ createSyntheticAssistantRun(namespace, turn) {
443
+ let runTree = this.assistant[turn.messageId];
444
+ if (runTree == null) {
445
+ runTree = this.createChild(namespace, {
446
+ name: "claude.assistant.turn",
447
+ run_type: "llm",
448
+ start_time: turn.timestamp,
449
+ inputs: turn.inputMessages.length > 0 ? { messages: turn.inputMessages } : {},
450
+ outputs: {
451
+ output: { messages: (0, messages_js_1.convertFromAnthropicMessage)(turn.message) },
452
+ },
453
+ extra: {
454
+ metadata: {
455
+ ls_provider: "anthropic",
456
+ ...(turn.model != null ? { ls_model_name: turn.model } : {}),
457
+ ...(turn.usageMetadata != null
458
+ ? { usage_metadata: turn.usageMetadata }
459
+ : {}),
460
+ },
461
+ },
462
+ });
463
+ if (runTree == null)
464
+ return;
465
+ this.assistant[turn.messageId] = runTree;
466
+ }
467
+ else {
468
+ runTree.outputs = {
469
+ output: { messages: (0, messages_js_1.convertFromAnthropicMessage)(turn.message) },
470
+ };
471
+ runTree.extra ??= {};
472
+ runTree.extra.metadata ??= {};
473
+ if (turn.model != null)
474
+ runTree.extra.metadata.ls_model_name = turn.model;
475
+ if (turn.usageMetadata != null) {
476
+ runTree.extra.metadata.usage_metadata = turn.usageMetadata;
477
+ }
478
+ }
479
+ runTree.end_time = turn.timestamp;
480
+ const tools = Array.isArray(turn.message.message.content)
481
+ ? turn.message.message.content.filter((block) => (0, messages_js_1.isToolBlock)(block))
482
+ : [];
483
+ for (const block of tools) {
484
+ if ((0, messages_js_1.isTaskTool)(block)) {
485
+ this.createAgentToolRun(namespace, block, turn.timestamp);
486
+ }
487
+ else {
488
+ this.createToolRun(namespace, block, turn.timestamp);
489
+ }
490
+ }
491
+ }
492
+ async reconcileTranscripts() {
493
+ const usageByMessageId = {};
494
+ if (this.mainTranscriptPath != null) {
495
+ const transcript = await (0, transcripts_js_1.readTranscript)(this.mainTranscriptPath);
496
+ Object.assign(usageByMessageId, transcript.usageByMessageId);
497
+ }
498
+ for (const transcriptPath of this.subagentTranscriptPaths) {
499
+ const namespace = transcriptPath.toolUseId ??
500
+ this.resolveSubagentNamespace(transcriptPath.agentType);
501
+ if (namespace == null || this.namespaces[namespace] == null)
204
502
  continue;
205
- if (tool.outputs == null && tool.error == null) {
206
- void tool.end(undefined, "Run not completed (conversation ended)");
503
+ const transcript = await (0, transcripts_js_1.readTranscript)(transcriptPath.path);
504
+ Object.assign(usageByMessageId, transcript.usageByMessageId);
505
+ for (const turn of transcript.turns) {
506
+ this.createSyntheticAssistantRun(namespace, turn);
507
+ }
508
+ for (const toolResult of transcript.toolResults) {
509
+ const tool = this.tools[toolResult.toolUseId];
510
+ if (tool == null)
511
+ continue;
512
+ const output = isRecord(toolResult.content)
513
+ ? toolResult.content
514
+ : { content: toolResult.content };
515
+ const error = toolResult.isError
516
+ ? typeof toolResult.content === "string" ||
517
+ typeof toolResult.content === "number" ||
518
+ typeof toolResult.content === "boolean"
519
+ ? String(toolResult.content)
520
+ : JSON.stringify(toolResult.content)
521
+ : undefined;
522
+ await tool.end(output, error);
523
+ }
524
+ }
525
+ for (const [messageId, usage] of Object.entries(usageByMessageId)) {
526
+ const runTree = this.assistant[messageId];
527
+ if (runTree == null)
528
+ continue;
529
+ runTree.extra ??= {};
530
+ runTree.extra.metadata ??= {};
531
+ runTree.extra.metadata.usage_metadata = usage;
532
+ }
533
+ }
534
+ async finish() {
535
+ try {
536
+ await this.reconcileTranscripts();
537
+ if (this.resultModelUsage != null) {
538
+ (0, usage_js_1.correctUsageFromResults)(this.resultModelUsage, Object.values(this.assistant).filter((runTree) => runTree != null));
539
+ }
540
+ // Clean up incomplete tools and finalise subagent calls. This mirrors the
541
+ // Python integration: Agent/Task tool runs are ended when their tool result
542
+ // arrives, while subagent chain runs are finalised only after transcript
543
+ // reconciliation so hidden child LLM/tool runs are created first.
544
+ for (const tool of Object.values(this.tools)) {
545
+ if (tool == null)
546
+ continue;
547
+ if (tool.outputs == null && tool.error == null) {
548
+ await tool.end(undefined, "Run not completed (conversation ended)");
549
+ }
207
550
  }
551
+ for (const subagent of Object.values(this.subagents)) {
552
+ if (subagent == null)
553
+ continue;
554
+ if (subagent.end_time == null) {
555
+ if (subagent.outputs == null && subagent.error == null) {
556
+ await subagent.end(undefined, "Run not completed (conversation ended)");
557
+ }
558
+ else {
559
+ await subagent.end();
560
+ }
561
+ }
562
+ }
563
+ // First make sure all the runs are created
564
+ await Promise.allSettled(this.postRunQueue);
565
+ // Then patch the runs
566
+ await Promise.allSettled(this.runTrees.map((runTree) => runTree.patchRun()));
567
+ }
568
+ finally {
569
+ this.dispose();
208
570
  }
209
- // First make sure all the runs are created
210
- await Promise.allSettled(this.postRunQueue);
211
- // Then patch the runs
212
- await Promise.allSettled(this.runTrees.map((runTree) => runTree.patchRun()));
213
571
  }
214
572
  }
215
573
  exports.StreamManager = StreamManager;
574
+ Object.defineProperty(StreamManager, "liveManagers", {
575
+ enumerable: true,
576
+ configurable: true,
577
+ writable: true,
578
+ value: new Set()
579
+ });
580
+ Object.defineProperty(StreamManager, "managersByRootRun", {
581
+ enumerable: true,
582
+ configurable: true,
583
+ writable: true,
584
+ value: new WeakMap()
585
+ });