@faapi/agent 6.16.0 → 6.18.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/dist/index.js CHANGED
@@ -296,21 +296,12 @@ function stripReasoningFromMessages(messages) {
296
296
  return rest;
297
297
  });
298
298
  }
299
- function normalizeToolCalls(toolCalls, bodyText, status) {
299
+ function normalizeToolCalls(toolCalls, _bodyText, _status) {
300
300
  if (!toolCalls || toolCalls.length === 0) return void 0;
301
301
  const result = [];
302
302
  for (let i = 0; i < toolCalls.length; i++) {
303
303
  const tc = toolCalls[i];
304
304
  const argsStr = tc?.function?.arguments ?? "{}";
305
- try {
306
- JSON.parse(argsStr);
307
- } catch {
308
- const excerpt = argsStr.slice(0, 500);
309
- throw new LLMProviderError(`Invalid tool arguments JSON: ${excerpt}`, {
310
- status,
311
- body: bodyText.slice(0, 500)
312
- });
313
- }
314
305
  result.push({
315
306
  id: tc?.id ?? `call_${i}`,
316
307
  type: "function",
@@ -394,14 +385,6 @@ function finalizeStreamChunk(accumulators, finishReason, usage) {
394
385
  const acc = accumulators.get(idx);
395
386
  if (!acc.id || !acc.name) continue;
396
387
  const argsStr = acc.argsString || "{}";
397
- try {
398
- JSON.parse(argsStr);
399
- } catch {
400
- const excerpt = argsStr.slice(0, 500);
401
- throw new LLMProviderError(`Invalid tool arguments JSON: ${excerpt}`, {
402
- body: excerpt
403
- });
404
- }
405
388
  toolCalls.push({
406
389
  id: acc.id,
407
390
  type: "function",
@@ -441,6 +424,9 @@ function isTracingToolResult(value) {
441
424
  }
442
425
 
443
426
  // src/reactLoop.ts
427
+ function isSubAgentToolResult(value) {
428
+ return typeof value === "object" && value !== null && value.__subAgent === true;
429
+ }
444
430
  var ReactLoopError = class extends Error {
445
431
  /** 配置的 maxTurns 值 */
446
432
  maxTurns;
@@ -565,14 +551,15 @@ async function reactLoop(input, config) {
565
551
  const extras = buildRequestExtras(config);
566
552
  let totalUsage;
567
553
  let finalReasoning;
568
- let turns = 0;
554
+ let loopTurns = 0;
555
+ let subTurns = 0;
569
556
  const traceStartedAt = enableTracing ? nowMs() : 0;
570
557
  const traceEvents = enableTracing ? [] : void 0;
571
- while (turns < maxTurns) {
558
+ while (loopTurns < maxTurns) {
572
559
  if (config.signal?.aborted) {
573
560
  throw new AgentAbortError(void 0, messages);
574
561
  }
575
- turns++;
562
+ loopTurns++;
576
563
  const llmStartedAt = enableTracing ? nowMs() : 0;
577
564
  const outgoing = config.maxHistoryTokens ? trimHistory(messages, config.maxHistoryTokens) : messages;
578
565
  const inputSnapshot = enableTracing ? [...outgoing] : void 0;
@@ -596,7 +583,7 @@ async function reactLoop(input, config) {
596
583
  const llmEndedAt = nowMs();
597
584
  traceEvents.push({
598
585
  type: "llm_call",
599
- turn: turns,
586
+ turn: loopTurns,
600
587
  startedAt: llmStartedAt,
601
588
  durationMs: llmEndedAt - llmStartedAt,
602
589
  model: config.model ?? "",
@@ -612,6 +599,7 @@ async function reactLoop(input, config) {
612
599
  messages.push(stripReasoning(response.message));
613
600
  if (response.stopReason !== "tool_calls" || !response.message.tool_calls) {
614
601
  const traceEndedAt = enableTracing ? nowMs() : 0;
602
+ const turns = loopTurns + subTurns;
615
603
  const result = {
616
604
  content: response.message.content,
617
605
  messages,
@@ -646,7 +634,9 @@ async function reactLoop(input, config) {
646
634
  try {
647
635
  args = parseToolCallArguments(toolCall);
648
636
  rawResult = await config.executeTool(toolName, args);
649
- if (isTracingToolResult(rawResult)) {
637
+ if (isSubAgentToolResult(rawResult)) {
638
+ resultStr = stringifyResult(rawResult.result);
639
+ } else if (isTracingToolResult(rawResult)) {
650
640
  resultStr = stringifyResult(rawResult.result);
651
641
  } else {
652
642
  resultStr = stringifyResult(rawResult);
@@ -682,11 +672,31 @@ async function reactLoop(input, config) {
682
672
  toolErr,
683
673
  hasError
684
674
  } of settled) {
675
+ if (isSubAgentToolResult(rawResult)) {
676
+ if (rawResult.usage) {
677
+ totalUsage = accumulateUsage(totalUsage, rawResult.usage);
678
+ }
679
+ if (typeof rawResult.turns === "number") {
680
+ subTurns += rawResult.turns;
681
+ }
682
+ }
685
683
  if (enableTracing) {
686
- if (isTracingToolResult(rawResult)) {
684
+ if (isSubAgentToolResult(rawResult) && rawResult.trace) {
685
+ traceEvents.push({
686
+ type: "subagent_call",
687
+ turn: loopTurns,
688
+ startedAt: toolStartedAt,
689
+ durationMs: toolEndedAt - toolStartedAt,
690
+ toolCallId: toolCall.id,
691
+ agentName: extractSubAgentName(toolName),
692
+ input: JSON.stringify(args),
693
+ trace: rawResult.trace,
694
+ result: resultStr
695
+ });
696
+ } else if (isTracingToolResult(rawResult)) {
687
697
  traceEvents.push({
688
698
  type: "subagent_call",
689
- turn: turns,
699
+ turn: loopTurns,
690
700
  startedAt: toolStartedAt,
691
701
  durationMs: toolEndedAt - toolStartedAt,
692
702
  toolCallId: toolCall.id,
@@ -698,7 +708,7 @@ async function reactLoop(input, config) {
698
708
  } else {
699
709
  traceEvents.push({
700
710
  type: "tool_call",
701
- turn: turns,
711
+ turn: loopTurns,
702
712
  startedAt: toolStartedAt,
703
713
  durationMs: toolEndedAt - toolStartedAt,
704
714
  toolCallId: toolCall.id,
@@ -728,12 +738,13 @@ async function* reactLoopStream(input, config) {
728
738
  const maxTurns = config.maxTurns ?? DEFAULT_MAX_TURNS;
729
739
  const extras = buildRequestExtras(config);
730
740
  let totalUsage;
731
- let turns = 0;
732
- while (turns < maxTurns) {
741
+ let loopTurns = 0;
742
+ let subTurns = 0;
743
+ while (loopTurns < maxTurns) {
733
744
  if (config.signal?.aborted) {
734
745
  throw new AgentAbortError(void 0, messages);
735
746
  }
736
- turns++;
747
+ loopTurns++;
737
748
  const llmStartedAt = enableTracing ? nowMs() : 0;
738
749
  const outgoing = config.maxHistoryTokens ? trimHistory(messages, config.maxHistoryTokens) : messages;
739
750
  const inputSnapshot = enableTracing ? [...outgoing] : void 0;
@@ -763,7 +774,6 @@ async function* reactLoopStream(input, config) {
763
774
  finishReason = chunk.finishReason;
764
775
  }
765
776
  if (chunk.usage) {
766
- totalUsage = accumulateUsage(totalUsage, chunk.usage);
767
777
  turnUsage = chunk.usage;
768
778
  }
769
779
  }
@@ -773,6 +783,9 @@ async function* reactLoopStream(input, config) {
773
783
  }
774
784
  throw err;
775
785
  }
786
+ if (turnUsage) {
787
+ totalUsage = accumulateUsage(totalUsage, turnUsage);
788
+ }
776
789
  const assistantMessage = {
777
790
  role: "assistant",
778
791
  content: turnContent
@@ -787,7 +800,7 @@ async function* reactLoopStream(input, config) {
787
800
  yield {
788
801
  traceEvent: {
789
802
  type: "llm_call",
790
- turn: turns,
803
+ turn: loopTurns,
791
804
  startedAt: llmStartedAt,
792
805
  durationMs: llmEndedAt - llmStartedAt,
793
806
  model: config.model ?? "",
@@ -801,7 +814,7 @@ async function* reactLoopStream(input, config) {
801
814
  if (finishReason !== "tool_calls" || !toolCalls) {
802
815
  const donePayload = {
803
816
  content: turnContent,
804
- turns,
817
+ turns: loopTurns + subTurns,
805
818
  stopReason: finishReason ?? "other",
806
819
  usage: totalUsage
807
820
  };
@@ -819,11 +832,37 @@ async function* reactLoopStream(input, config) {
819
832
  let rawResult;
820
833
  let toolErr;
821
834
  let hasError = false;
835
+ const pendingDeltas = [];
836
+ let wakeDrain = null;
837
+ const deltaEmitter = {
838
+ onSubAgentDelta: (delta) => {
839
+ pendingDeltas.push(delta);
840
+ const wake = wakeDrain;
841
+ wakeDrain = null;
842
+ wake?.();
843
+ }
844
+ };
822
845
  try {
823
846
  args = parseToolCallArguments(toolCall);
824
847
  yield { toolCall: { name: toolName, arguments: args } };
825
- rawResult = await config.executeTool(toolName, args);
826
- if (isTracingToolResult(rawResult)) {
848
+ const toolPromise = config.executeTool(toolName, args, deltaEmitter);
849
+ const settled = toolPromise.then(
850
+ () => true,
851
+ () => true
852
+ // 错误延后重抛,此处只关心执行结束以收尾 drain
853
+ );
854
+ drain: for (; ; ) {
855
+ while (pendingDeltas.length > 0) {
856
+ yield { subagentDelta: pendingDeltas.shift() };
857
+ }
858
+ if (await Promise.race([settled, new Promise((r) => wakeDrain = r)])) {
859
+ break drain;
860
+ }
861
+ }
862
+ rawResult = await toolPromise;
863
+ if (isSubAgentToolResult(rawResult)) {
864
+ resultStr = stringifyResult(rawResult.result);
865
+ } else if (isTracingToolResult(rawResult)) {
827
866
  resultStr = stringifyResult(rawResult.result);
828
867
  } else {
829
868
  resultStr = stringifyResult(rawResult);
@@ -834,14 +873,36 @@ async function* reactLoopStream(input, config) {
834
873
  resultStr = stringifyError(err);
835
874
  rawResult = void 0;
836
875
  }
876
+ if (isSubAgentToolResult(rawResult)) {
877
+ if (rawResult.usage) {
878
+ totalUsage = accumulateUsage(totalUsage, rawResult.usage);
879
+ }
880
+ if (typeof rawResult.turns === "number") {
881
+ subTurns += rawResult.turns;
882
+ }
883
+ }
837
884
  yield { toolResult: { name: toolName, result: resultStr } };
838
885
  if (enableTracing) {
839
886
  const toolEndedAt = nowMs();
840
- if (isTracingToolResult(rawResult)) {
887
+ if (isSubAgentToolResult(rawResult) && rawResult.trace) {
888
+ yield {
889
+ traceEvent: {
890
+ type: "subagent_call",
891
+ turn: loopTurns,
892
+ startedAt: toolStartedAt,
893
+ durationMs: toolEndedAt - toolStartedAt,
894
+ toolCallId: toolCall.id,
895
+ agentName: extractSubAgentName(toolName),
896
+ input: JSON.stringify(args),
897
+ trace: rawResult.trace,
898
+ result: resultStr
899
+ }
900
+ };
901
+ } else if (isTracingToolResult(rawResult)) {
841
902
  yield {
842
903
  traceEvent: {
843
904
  type: "subagent_call",
844
- turn: turns,
905
+ turn: loopTurns,
845
906
  startedAt: toolStartedAt,
846
907
  durationMs: toolEndedAt - toolStartedAt,
847
908
  toolCallId: toolCall.id,
@@ -855,7 +916,7 @@ async function* reactLoopStream(input, config) {
855
916
  yield {
856
917
  traceEvent: {
857
918
  type: "tool_call",
858
- turn: turns,
919
+ turn: loopTurns,
859
920
  startedAt: toolStartedAt,
860
921
  durationMs: toolEndedAt - toolStartedAt,
861
922
  toolCallId: toolCall.id,
@@ -933,6 +994,18 @@ var AgentError = class extends Error {
933
994
  this.name = "AgentError";
934
995
  }
935
996
  };
997
+ var AgentToolTimeoutError = class extends AgentError {
998
+ /** 超时的 tool 名 */
999
+ toolName;
1000
+ /** 配置的超时毫秒数 */
1001
+ timeoutMs;
1002
+ constructor(toolName, timeoutMs) {
1003
+ super(`Tool "${toolName}" timed out after ${timeoutMs}ms`);
1004
+ this.name = "AgentToolTimeoutError";
1005
+ this.toolName = toolName;
1006
+ this.timeoutMs = timeoutMs;
1007
+ }
1008
+ };
936
1009
  var AgentRecursionError = class extends AgentError {
937
1010
  /** 配置的 maxAgentDepth 值 */
938
1011
  maxDepth;
@@ -1075,6 +1148,22 @@ var Agent = class _Agent {
1075
1148
  * `options.messages` 提供时先经 `validateResumeHistory` 结构校验,非法抛
1076
1149
  * `AgentError`,不发起 LLM 请求。
1077
1150
  */
1151
+ /**
1152
+ * 构建执行白名单:agent 声明的 tools + sub-agents(`agent.` 前缀)
1153
+ *
1154
+ * 每次 run 构建一次存入 callCtx(executeTool 复用)——reload 场景注册表换代后
1155
+ * 新 run 重新构建,声明变化自然生效
1156
+ */
1157
+ buildDeclaredTools(agentName) {
1158
+ const declared = /* @__PURE__ */ new Set();
1159
+ for (const tool of this.deps.resolveAgentTools(agentName)) {
1160
+ declared.add(tool.name);
1161
+ }
1162
+ for (const sub of this.deps.resolveSubAgents(agentName)) {
1163
+ declared.add(`agent.${sub.name}`);
1164
+ }
1165
+ return declared;
1166
+ }
1078
1167
  async buildLoopConfig(input, options) {
1079
1168
  if (!input && !options?.messages?.length) {
1080
1169
  throw new AgentError(
@@ -1097,7 +1186,13 @@ var Agent = class _Agent {
1097
1186
  const tools = await this.buildToolDefinitions(agentName);
1098
1187
  const { provider, model } = options?.provider !== void 0 ? this.resolveExternalProvider(options.provider, options?.model) : this.resolveModelKey(options?.model, meta);
1099
1188
  const enableTracing = options?.enableTracing ?? this.deps.config?.enableTracing ?? false;
1100
- const callCtx = { agentName, enableTracing, provider, model };
1189
+ const callCtx = {
1190
+ agentName,
1191
+ enableTracing,
1192
+ provider,
1193
+ model,
1194
+ declaredTools: this.buildDeclaredTools(agentName)
1195
+ };
1101
1196
  return {
1102
1197
  provider,
1103
1198
  systemPrompt: meta.systemPrompt,
@@ -1105,11 +1200,12 @@ var Agent = class _Agent {
1105
1200
  temperature: options?.temperature,
1106
1201
  maxTokens: options?.maxTokens,
1107
1202
  maxTurns: meta.maxTurns ?? this.deps.config?.maxTurns,
1203
+ maxHistoryTokens: this.deps.config?.maxHistoryTokens,
1108
1204
  tools,
1109
1205
  signal: options?.signal,
1110
1206
  messages: options?.messages,
1111
1207
  enableTracing,
1112
- executeTool: async (name, args) => this.executeTool(name, args, callCtx)
1208
+ executeTool: async (name, args, deltaEmitter) => this.executeTool(name, args, callCtx, deltaEmitter)
1113
1209
  };
1114
1210
  }
1115
1211
  /**
@@ -1265,7 +1361,7 @@ var Agent = class _Agent {
1265
1361
  /**
1266
1362
  * tool 执行路由(由 reactLoop 调用)
1267
1363
  *
1268
- * - `agent.` 前缀 → {@link executeSubAgent} 递归(含 enableTracing + TracingToolResult 包装)
1364
+ * - `agent.` 前缀 → {@link executeSubAgent} 递归(含 usage/turns 上卷 + tracing 包装)
1269
1365
  * - 常规 tool → `loadToolModule` 加载 handler + 可选 input 校验 → 调用
1270
1366
  *
1271
1367
  * `callCtx` 由 [buildLoopConfig](#buildLoopConfig) 闭包捕获传入——本次调用的有效
@@ -1277,7 +1373,7 @@ var Agent = class _Agent {
1277
1373
  *
1278
1374
  * **tool 未找到 / 加载失败**:抛错,被 reactLoop catch 后同样回传 LLM。
1279
1375
  */
1280
- async executeTool(rawName, rawArgs, callCtx) {
1376
+ async executeTool(rawName, rawArgs, callCtx, deltaEmitter) {
1281
1377
  const name = rawName;
1282
1378
  let args = rawArgs;
1283
1379
  const guard = this.deps.config?.beforeToolCall?.(name, args, this.deps.ctx);
@@ -1285,20 +1381,13 @@ var Agent = class _Agent {
1285
1381
  if ("error" in guard) return { error: guard.error };
1286
1382
  if ("args" in guard) args = guard.args;
1287
1383
  }
1288
- const declared = /* @__PURE__ */ new Set();
1289
- for (const tool2 of this.deps.resolveAgentTools(callCtx.agentName)) {
1290
- declared.add(tool2.name);
1291
- }
1292
- for (const sub of this.deps.resolveSubAgents(callCtx.agentName)) {
1293
- declared.add(`agent.${sub.name}`);
1294
- }
1295
- if (!declared.has(name)) {
1384
+ if (!callCtx.declaredTools.has(name)) {
1296
1385
  return {
1297
1386
  error: `Tool "${name}" is not declared by agent "${callCtx.agentName}" (add it to the agent's tools/agents declaration)`
1298
1387
  };
1299
1388
  }
1300
1389
  if (name.startsWith("agent.")) {
1301
- return await this.executeSubAgent(name.slice(6), args, callCtx);
1390
+ return await this.executeSubAgent(name.slice(6), args, callCtx, deltaEmitter);
1302
1391
  }
1303
1392
  const tool = this.deps.getTool(name);
1304
1393
  if (!tool) {
@@ -1314,27 +1403,49 @@ var Agent = class _Agent {
1314
1403
  callArgs = result2.value ?? args;
1315
1404
  }
1316
1405
  const mod = await this.deps.loadToolModule(tool.filePath, tool.functionName);
1317
- const result = await mod.handler(callArgs, this.deps.ctx);
1318
- this.deps.config?.afterToolCall?.(name, args, result, this.deps.ctx);
1406
+ const toolTimeoutMs = this.deps.config?.toolTimeoutMs;
1407
+ let result;
1408
+ if (toolTimeoutMs && toolTimeoutMs > 0) {
1409
+ result = await Promise.race([
1410
+ mod.handler(callArgs, this.deps.ctx),
1411
+ new Promise((_, reject) => {
1412
+ const timer = setTimeout(
1413
+ () => reject(new AgentToolTimeoutError(name, toolTimeoutMs)),
1414
+ toolTimeoutMs
1415
+ );
1416
+ timer.unref?.();
1417
+ })
1418
+ ]);
1419
+ } else {
1420
+ result = await mod.handler(callArgs, this.deps.ctx);
1421
+ }
1422
+ try {
1423
+ this.deps.config?.afterToolCall?.(name, args, result, this.deps.ctx);
1424
+ } catch (hookErr) {
1425
+ console.error(`[faapi] afterToolCall hook error for tool "${name}":`, hookErr);
1426
+ }
1319
1427
  return result;
1320
1428
  }
1321
1429
  /**
1322
1430
  * sub-agent 递归执行
1323
1431
  *
1324
1432
  * 1. `maxAgentDepth` 防护——超限抛 {@link AgentRecursionError}
1325
- * 2. sub-agent handler 导出 `run` 时调自定义 `mod.run(args)`(无 trace,与常规 tool 一致)
1433
+ * 2. sub-agent handler 导出 `run` 时调自定义 `mod.run(args)`(无 trace、无结构化
1434
+ * usage 可卷——直接返回业务结果,其 token 不进入父 run 台账)
1326
1435
  * 3. 无 `run` 时调 `subAgent.run(stringify(args), { agent, provider, model, enableTracing })`
1327
1436
  * 走默认 reactLoop——继承父调用的 provider,sub 元数据声明 `model` 时优先用自身的,
1328
1437
  * 未声明时沿用父 model
1329
1438
  *
1330
- * **tracing 路径**:`enableTracing=true` 时,subAgent.run 返回的 `result.trace`(agentName
1331
- * 已被 `Agent.run` 填为 subName)被包装为 [TracingToolResult](./trace.md) 返回给 reactLoop。
1332
- * reactLoop 通过 `isTracingToolResult` 识别后发出 `subagent_call` 事件,嵌入 sub-trace
1333
- * (递归结构,业务方可还原完整调用树)。`enableTracing=false` 时返回 `result.content`
1334
- * (unknown,与常规 tool 一致,零开销)。
1439
+ * **返回值统一包装为 [SubAgentToolResult](./reactLoop.md)**(无论 tracing 开关——
1440
+ * 用量上卷不依赖 tracing):`usage` / `turns` 是子循环整树口径(sub-sub 已在子循环
1441
+ * 上卷),reactLoop 识别后累加进父循环,父 run 的 usage 台账 = 全部 `llm_call` 之和。
1442
+ * `enableTracing=true` 时再附 `trace`(agentName 已被 `Agent.run` 填为 subName),
1443
+ * reactLoop 据此发出 `subagent_call` 事件,嵌入 sub-trace(递归结构,业务方可还原
1444
+ * 完整调用树)。详见 reactLoop.md「usage 与 turns 的整树口径」。
1335
1445
  *
1336
- * **自定义 run 无 trace**:业务方导出 `run` 函数时直接返回业务结果,无法采集 sub-agent
1337
- * 内部明细——需 trace 时应让 sub-agent 走默认 reactLoop(不导出 `run`)。
1446
+ * **自定义 run 无 trace、无用量上卷**:业务方导出 `run` 函数时直接返回业务结果,
1447
+ * 无法采集 sub-agent 内部明细——需 trace / 用量统计时让 sub-agent 走默认 reactLoop
1448
+ * (不导出 `run`)。
1338
1449
  *
1339
1450
  * 自定义 run 接收原始 args 对象;默认 reactLoop 的 user 消息:args 恰为单字段
1340
1451
  * `{ input: <string> }`(与显式入参 schema 形状一致)时直传字符串,其余形状
@@ -1344,7 +1455,7 @@ var Agent = class _Agent {
1344
1455
  * 而非 `getAgent`(返回 AgentCore,无代码加载细节)。DB skill 无文件,
1345
1456
  * `getAgentEntry` 返回 `undefined`,走默认 reactLoop。
1346
1457
  */
1347
- async executeSubAgent(subName, args, callCtx) {
1458
+ async executeSubAgent(subName, args, callCtx, deltaEmitter) {
1348
1459
  const newDepth = this.depth + 1;
1349
1460
  const maxDepth = this.deps.config?.maxAgentDepth ?? DEFAULT_MAX_AGENT_DEPTH;
1350
1461
  if (newDepth > maxDepth) {
@@ -1361,21 +1472,68 @@ var Agent = class _Agent {
1361
1472
  }
1362
1473
  }
1363
1474
  const subMeta = this.deps.getAgent(subName);
1364
- const result = await subAgent.run(extractSubAgentUserInput(args), {
1475
+ const subOptions = {
1365
1476
  agent: subName,
1366
1477
  provider: callCtx.provider,
1367
1478
  model: subMeta?.model ?? callCtx.model,
1368
1479
  enableTracing: callCtx.enableTracing
1369
- });
1480
+ };
1481
+ if (deltaEmitter) {
1482
+ const toolName = `agent.${subName}`;
1483
+ const startedAt = performance.now();
1484
+ const traceEvents = callCtx.enableTracing ? [] : void 0;
1485
+ let done;
1486
+ for await (const chunk of subAgent.stream(extractSubAgentUserInput(args), subOptions)) {
1487
+ if (chunk.subagentDelta) {
1488
+ deltaEmitter.onSubAgentDelta(chunk.subagentDelta);
1489
+ } else if (typeof chunk.deltaContent === "string" && chunk.deltaContent.length > 0 || typeof chunk.deltaReasoning === "string" && chunk.deltaReasoning.length > 0) {
1490
+ deltaEmitter.onSubAgentDelta({
1491
+ name: toolName,
1492
+ depth: newDepth,
1493
+ deltaContent: chunk.deltaContent,
1494
+ deltaReasoning: chunk.deltaReasoning
1495
+ });
1496
+ }
1497
+ if (chunk.traceEvent) {
1498
+ traceEvents.push(chunk.traceEvent);
1499
+ }
1500
+ if (chunk.done) {
1501
+ done = chunk.done;
1502
+ }
1503
+ }
1504
+ this.deps.config?.afterToolCall?.(toolName, args, done?.content, this.deps.ctx);
1505
+ const wrapped2 = {
1506
+ __subAgent: true,
1507
+ result: done?.content ?? "",
1508
+ usage: done?.usage,
1509
+ turns: done?.turns ?? 0
1510
+ };
1511
+ if (traceEvents) {
1512
+ wrapped2.trace = {
1513
+ agentName: "",
1514
+ startedAt,
1515
+ durationMs: performance.now() - startedAt,
1516
+ turns: done?.turns ?? 0,
1517
+ usage: done?.usage,
1518
+ stopReason: done?.stopReason,
1519
+ content: done?.content,
1520
+ events: traceEvents
1521
+ };
1522
+ }
1523
+ return wrapped2;
1524
+ }
1525
+ const result = await subAgent.run(extractSubAgentUserInput(args), subOptions);
1370
1526
  this.deps.config?.afterToolCall?.(`agent.${subName}`, args, result.content, this.deps.ctx);
1527
+ const wrapped = {
1528
+ __subAgent: true,
1529
+ result: result.content,
1530
+ usage: result.usage,
1531
+ turns: result.turns
1532
+ };
1371
1533
  if (callCtx.enableTracing && result.trace) {
1372
- return {
1373
- __trace: true,
1374
- result: result.content,
1375
- trace: result.trace
1376
- };
1534
+ wrapped.trace = result.trace;
1377
1535
  }
1378
- return result.content;
1536
+ return wrapped;
1379
1537
  }
1380
1538
  };
1381
1539
 
@@ -1494,12 +1652,14 @@ export {
1494
1652
  AgentAbortError,
1495
1653
  AgentError,
1496
1654
  AgentRecursionError,
1655
+ AgentToolTimeoutError,
1497
1656
  LLMProviderError,
1498
1657
  ReactLoopError,
1499
1658
  createOpenAIProvider,
1500
1659
  createProvider,
1501
1660
  createToolSchemaResolver,
1502
1661
  plugin_default as default,
1662
+ isSubAgentToolResult,
1503
1663
  isTracingToolResult,
1504
1664
  reactLoop,
1505
1665
  reactLoopStream