@cicctencent/agent-core 0.1.99 → 0.1.101

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 (57) hide show
  1. package/dist/cjs/agent/engine.d.ts +11 -0
  2. package/dist/cjs/agent/engine.d.ts.map +1 -1
  3. package/dist/cjs/agent/engine.js +191 -29
  4. package/dist/cjs/agent/engine.js.map +1 -1
  5. package/dist/cjs/agent/history-manager.d.ts +21 -0
  6. package/dist/cjs/agent/history-manager.d.ts.map +1 -1
  7. package/dist/cjs/agent/history-manager.js +72 -0
  8. package/dist/cjs/agent/history-manager.js.map +1 -1
  9. package/dist/cjs/agent/llm-caller.d.ts +5 -7
  10. package/dist/cjs/agent/llm-caller.d.ts.map +1 -1
  11. package/dist/cjs/agent/llm-caller.js +110 -20
  12. package/dist/cjs/agent/llm-caller.js.map +1 -1
  13. package/dist/cjs/agent/tool-executor.d.ts +6 -3
  14. package/dist/cjs/agent/tool-executor.d.ts.map +1 -1
  15. package/dist/cjs/agent/tool-executor.js +95 -26
  16. package/dist/cjs/agent/tool-executor.js.map +1 -1
  17. package/dist/cjs/agent/types.d.ts +5 -0
  18. package/dist/cjs/agent/types.d.ts.map +1 -1
  19. package/dist/cjs/agent/types.js.map +1 -1
  20. package/dist/cjs/hooks/types.d.ts +6 -1
  21. package/dist/cjs/hooks/types.d.ts.map +1 -1
  22. package/dist/cjs/observability/middleware.d.ts.map +1 -1
  23. package/dist/cjs/observability/middleware.js +63 -1
  24. package/dist/cjs/observability/middleware.js.map +1 -1
  25. package/dist/cjs/observability/sink.d.ts +4 -0
  26. package/dist/cjs/observability/sink.d.ts.map +1 -1
  27. package/dist/cjs/observability/sink.js +26 -3
  28. package/dist/cjs/observability/sink.js.map +1 -1
  29. package/dist/esm/agent/engine.d.ts +11 -0
  30. package/dist/esm/agent/engine.d.ts.map +1 -1
  31. package/dist/esm/agent/engine.js +191 -29
  32. package/dist/esm/agent/engine.js.map +1 -1
  33. package/dist/esm/agent/history-manager.d.ts +21 -0
  34. package/dist/esm/agent/history-manager.d.ts.map +1 -1
  35. package/dist/esm/agent/history-manager.js +72 -0
  36. package/dist/esm/agent/history-manager.js.map +1 -1
  37. package/dist/esm/agent/llm-caller.d.ts +5 -7
  38. package/dist/esm/agent/llm-caller.d.ts.map +1 -1
  39. package/dist/esm/agent/llm-caller.js +110 -20
  40. package/dist/esm/agent/llm-caller.js.map +1 -1
  41. package/dist/esm/agent/tool-executor.d.ts +6 -3
  42. package/dist/esm/agent/tool-executor.d.ts.map +1 -1
  43. package/dist/esm/agent/tool-executor.js +95 -26
  44. package/dist/esm/agent/tool-executor.js.map +1 -1
  45. package/dist/esm/agent/types.d.ts +5 -0
  46. package/dist/esm/agent/types.d.ts.map +1 -1
  47. package/dist/esm/agent/types.js.map +1 -1
  48. package/dist/esm/hooks/types.d.ts +6 -1
  49. package/dist/esm/hooks/types.d.ts.map +1 -1
  50. package/dist/esm/observability/middleware.d.ts.map +1 -1
  51. package/dist/esm/observability/middleware.js +63 -1
  52. package/dist/esm/observability/middleware.js.map +1 -1
  53. package/dist/esm/observability/sink.d.ts +4 -0
  54. package/dist/esm/observability/sink.d.ts.map +1 -1
  55. package/dist/esm/observability/sink.js +26 -3
  56. package/dist/esm/observability/sink.js.map +1 -1
  57. package/package.json +1 -1
@@ -146,8 +146,19 @@ export declare class AgentEngine {
146
146
  private buildToolContext;
147
147
  /** 汇总准备阶段,生成 LoopState */
148
148
  private prepareRun;
149
+ /** 动态构建工具区 prompt(Available Tools + Tool Usage Guidelines)。
150
+ * 从 state.toolDefs 现场拼装,确保与可执行工具集一致,消除静态穿透。 */
151
+ private buildDynamicToolSection;
152
+ /** 刷新 system prompt 中的工具区,确保与 state.toolDefs 一致 */
153
+ private refreshToolSection;
149
154
  /** 主循环:迭代调用 LLM → 执行工具 → 直到完成或达到上限 */
150
155
  private agentLoop;
156
+ /**
157
+ * 生成兜底总结(当 LLM 调用失败或无内容输出时使用)。
158
+ * 从工具调用记录中提取摘要信息,确保用户始终能收到有意义的回复,
159
+ * 而非仅看到 "Max iterations reached"。
160
+ */
161
+ private generateFallbackSummary;
151
162
  /**
152
163
  * 向运行中的 Agent 注入 steering 消息
153
164
  * 消息将在下次 LLM 调用前注入对话上下文
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/agent/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/agent/engine.ts"],"names":[],"mappings":"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"}
@@ -173,7 +173,8 @@ class AgentEngine {
173
173
  });
174
174
  // 初始化工具分类集合(可通过 config 覆盖默认值)
175
175
  this.dataFetchTools = new Set(config.dataFetchTools ?? [
176
- 'web_fetch', 'execute_command', 'write_file', 'append_file',
176
+ 'web_fetch', 'http_request', 'browser_navigate', 'browser_click',
177
+ 'execute_command', 'write_file', 'append_file',
177
178
  'run_script', 'read_file', 'read_tool_doc',
178
179
  ]);
179
180
  this.outputTools = new Set(config.outputTools ?? [
@@ -351,6 +352,15 @@ class AgentEngine {
351
352
  iteration: 0,
352
353
  rawError: err,
353
354
  });
355
+ // 异常终止时以 error 状态结束 trace(正常路径由 emitDone 触发 on_complete,
356
+ // 此处显式调用确保 trace 不会卡在 active,也不会被 runWithTrace 覆盖为 ok)
357
+ this.runHooks('on_complete', {
358
+ sessionId,
359
+ iteration: 0,
360
+ totalIterations: 0,
361
+ runStatus: 'error',
362
+ error: errorMsg,
363
+ }).catch(() => { });
354
364
  this.statusMap.set(sessionId, 'idle');
355
365
  }
356
366
  finally {
@@ -703,7 +713,9 @@ ${textHistory.join('\n')}`;
703
713
  abortSignal: internalAbort.signal,
704
714
  custom: {
705
715
  ...custom,
706
- toolRegistry: custom?.toolRegistry ?? this.config.toolRegistry,
716
+ // Fix A:始终使用引擎授权注册表(已按 selectedTools/disabledTools 过滤),
717
+ // 不接受 custom.toolRegistry 覆盖——防止 read_tool_doc/diagnose 泄漏被禁工具 schema。
718
+ toolRegistry: this.config.toolRegistry,
707
719
  skillManager: custom?.skillManager ?? this.config.skillManager,
708
720
  },
709
721
  emitSubEvent: custom?.emitSubEvent,
@@ -716,9 +728,15 @@ ${textHistory.join('\n')}`;
716
728
  const { skillContent, actualMessage, skillMatched } = await this.resolveSkill(options, options.sessionId);
717
729
  const toolDefs = this.resolveToolDefs(options);
718
730
  (0, logger_ts_1.logEvent)({ event: 'engine.prepare', sessionId: options.sessionId, data: { toolCount: toolDefs.length, tools: toolDefs.map(t => t.function.name) } });
731
+ const messages = await this.buildInitialMessages(options, skillContent, actualMessage, skillMatched);
732
+ // 存储不含工具区的稳定系统提示词,首次注入动态工具区
733
+ const stableSystemPrompt = messages[0]?.content || '';
734
+ if (messages[0]) {
735
+ messages[0].content = stableSystemPrompt + this.buildDynamicToolSection(toolDefs);
736
+ }
719
737
  return {
720
738
  sessionId: options.sessionId,
721
- messages: await this.buildInitialMessages(options, skillContent, actualMessage, skillMatched),
739
+ messages,
722
740
  toolDefs,
723
741
  toolContext: this.buildToolContext(options, internalAbort),
724
742
  internalAbort,
@@ -726,6 +744,7 @@ ${textHistory.join('\n')}`;
726
744
  lastContent: '',
727
745
  toolCallRecords: [],
728
746
  consecutiveToolFailures: new Map(),
747
+ blockedTools: new Set(),
729
748
  iterationGuardInjected: false,
730
749
  consecutiveSilentIterations: 0,
731
750
  lastIterationHadToolCalls: false,
@@ -733,8 +752,39 @@ ${textHistory.join('\n')}`;
733
752
  dataFetchGuardInjected: false,
734
753
  consecutiveAllFailIterations: 0,
735
754
  finalSummaryInjected: false,
755
+ hadFatalError: false,
756
+ fatalErrorMsg: undefined,
757
+ stableSystemPrompt,
736
758
  };
737
759
  }
760
+ /** 动态构建工具区 prompt(Available Tools + Tool Usage Guidelines)。
761
+ * 从 state.toolDefs 现场拼装,确保与可执行工具集一致,消除静态穿透。 */
762
+ buildDynamicToolSection(toolDefs) {
763
+ if (this.config.buildToolSection) {
764
+ return this.config.buildToolSection(toolDefs, this.config.toolRegistry);
765
+ }
766
+ if (toolDefs.length === 0)
767
+ return '';
768
+ const lines = toolDefs.map(d => `- \`${d.function.name}\`: ${d.function.description || ''}`);
769
+ let section = `\n\n## Available Tools\n${lines.join('\n')}`;
770
+ // 收集工具自带的使用引导(tool.prompt)
771
+ const prompts = [];
772
+ for (const def of toolDefs) {
773
+ const tool = this.config.toolRegistry.get(def.function.name);
774
+ if (tool?.prompt)
775
+ prompts.push(tool.prompt);
776
+ }
777
+ if (prompts.length > 0) {
778
+ section += `\n\n## Tool Usage Guidelines\n\n${prompts.join('\n\n')}`;
779
+ }
780
+ return section;
781
+ }
782
+ /** 刷新 system prompt 中的工具区,确保与 state.toolDefs 一致 */
783
+ refreshToolSection(state) {
784
+ if (state.messages[0] && state.stableSystemPrompt != null) {
785
+ state.messages[0].content = state.stableSystemPrompt + this.buildDynamicToolSection(state.toolDefs);
786
+ }
787
+ }
738
788
  // ---- Agent Loop ----
739
789
  /** 主循环:迭代调用 LLM → 执行工具 → 直到完成或达到上限 */
740
790
  async *agentLoop(state) {
@@ -742,6 +792,11 @@ ${textHistory.join('\n')}`;
742
792
  state.iteration++;
743
793
  // 中断检查
744
794
  if (state.internalAbort.signal.aborted) {
795
+ // 外部中断(客户端断开 / 用户主动 abort / 看门狗超时):标记致命错误,
796
+ // 确保 emitDone → on_complete 将 trace 状态写为 error,而非默认的 ok,
797
+ // 避免 trace 卡在 active 或被误判为正常完成。
798
+ state.hadFatalError = true;
799
+ state.fatalErrorMsg = state.fatalErrorMsg || 'Run aborted before completion';
745
800
  yield this.emitDone(state);
746
801
  return;
747
802
  }
@@ -765,11 +820,28 @@ ${textHistory.join('\n')}`;
765
820
  timestamp: Date.now(),
766
821
  iteration: state.iteration,
767
822
  });
823
+ // 记录可观测 error 事件:让 trace 时间线解释"为何熔断",
824
+ // 与 on_complete(runStatus='error') 闭环,确保异常被统计进可观测。
825
+ await this.runHooks('on_error', {
826
+ sessionId: state.sessionId,
827
+ iteration: state.iteration,
828
+ error: budgetErrMsg,
829
+ recoverable: false,
830
+ });
831
+ state.hadFatalError = true;
832
+ state.fatalErrorMsg = budgetErrMsg;
768
833
  yield this.emitDone(state);
769
834
  return;
770
835
  }
771
836
  else {
772
837
  (0, logger_ts_1.logEvent)({ event: 'engine.token_budget_warning', sessionId: state.sessionId, level: 'warn', data: { current: budgetCheck.currentTokens, max: budgetCheck.maxTokens } });
838
+ await this.runHooks('on_budget_warning', {
839
+ sessionId: state.sessionId,
840
+ iteration: state.iteration,
841
+ currentTokens: budgetCheck.currentTokens,
842
+ maxTokens: budgetCheck.maxTokens,
843
+ traceId: state.toolContext?.custom?.traceId,
844
+ });
773
845
  }
774
846
  }
775
847
  }
@@ -787,6 +859,12 @@ ${textHistory.join('\n')}`;
787
859
  messages: steeringResult.messages,
788
860
  count: steeringResult.count,
789
861
  });
862
+ await this.runHooks('on_steering', {
863
+ sessionId: state.sessionId,
864
+ iteration: state.iteration,
865
+ steeringMessages: steeringResult.messages,
866
+ traceId: state.toolContext?.custom?.traceId,
867
+ });
790
868
  }
791
869
  // 迭代预算提醒:接近上限时注入提醒,防止 agent 耗尽迭代仍在收集数据
792
870
  this.injectIterationGuard(state);
@@ -794,6 +872,8 @@ ${textHistory.join('\n')}`;
794
872
  // 首次触发:注入 guard 消息让 LLM 下轮产出总结;不立即终止,给 LLM 一次产出机会
795
873
  // 若 LLM 继续失败,全局连续失败熔断器(consecutiveAllFailIterations >= 3)会兜底
796
874
  this.checkDataFetchLoop(state);
875
+ // 动态拼装工具区,确保 system prompt 工具列表与 state.toolDefs 一致
876
+ this.refreshToolSection(state);
797
877
  // 调用 LLM(流式,含重试与降级)
798
878
  this.statusMap.set(state.sessionId, 'thinking');
799
879
  const llmStart = Date.now();
@@ -809,9 +889,12 @@ ${textHistory.join('\n')}`;
809
889
  state.totalUsage.totalTokens += usage.totalTokens || 0;
810
890
  }
811
891
  if (!assistant) {
812
- // 所有 LLM provider 均已失败(callWithRetry 内部已重试+降级),
813
- // 不再发 done 事件(callWithRetry 已发出带详细错误信息的 error 事件),
814
- // 仅完成清理,避免 done 事件覆盖前端已展示的错误消息
892
+ // 所有 LLM provider 均已失败(callWithRetry 内部已重试+降级)。
893
+ // callWithRetry 已发出 error 事件,此处补充 done 事件以正确关闭 SSE 流。
894
+ // 不发 done 事件会导致:SSE 流不关闭、DB 消息状态卡在 streaming、
895
+ // 前端 onDone 回调不触发(无法更新 UI 状态)。
896
+ // error 和 done 是不同的 SSE 事件类型,前端分别用 onError 和 onDone 处理,
897
+ // done 事件携带 lastContent(之前成功迭代的输出)或兜底总结,不会覆盖 error 消息。
815
898
  const errorMsg = 'All LLM providers exhausted (all calls failed including retries and fallback)';
816
899
  (0, logger_ts_1.logEvent)({ event: 'engine.llm_exhausted', sessionId: state.sessionId, level: 'warn', data: { iteration: state.iteration } });
817
900
  this.statusMap.set(state.sessionId, 'idle');
@@ -823,25 +906,11 @@ ${textHistory.join('\n')}`;
823
906
  timestamp: Date.now(),
824
907
  iteration: state.iteration,
825
908
  });
826
- // 记录 Token 用量到 UsageTracker
827
- if (this.config.usageTracker && state.totalUsage) {
828
- const modelInfo = this.config.llmProvider.getModel();
829
- this.config.usageTracker.record({
830
- sessionId: state.sessionId,
831
- provider: modelInfo.provider,
832
- model: modelInfo.model,
833
- usage: state.totalUsage,
834
- iterations: state.iteration,
835
- });
836
- }
837
- this.runHooks('on_complete', {
838
- sessionId: state.sessionId,
839
- iteration: state.iteration,
840
- totalIterations: state.iteration,
841
- usage: state.totalUsage,
842
- traceId: state.toolContext?.custom?.traceId,
843
- output: state.lastContent || undefined,
844
- }).catch(() => { });
909
+ state.hadFatalError = true;
910
+ state.fatalErrorMsg = errorMsg;
911
+ // 发出 done 事件,确保 SSE 流正确关闭、DB 状态正确更新
912
+ // emitDone 内部会记录 Token 用量、调用 on_complete hook 并生成兜底总结
913
+ yield this.emitDone(state);
845
914
  return;
846
915
  }
847
916
  // Hook: after_ll_call
@@ -877,6 +946,12 @@ ${textHistory.join('\n')}`;
877
946
  state.lastIterationHadToolCalls = false; // 已消费标记,避免重复注入
878
947
  (0, logger_ts_1.logEvent)({ event: 'engine.continuation_nudge', sessionId: state.sessionId, data: { nudge: state.continuationNudges, iteration: state.iteration } });
879
948
  this.injectContinuationNudge(state);
949
+ await this.runHooks('on_continuation_nudge', {
950
+ sessionId: state.sessionId,
951
+ iteration: state.iteration,
952
+ nudgeCount: state.continuationNudges,
953
+ traceId: state.toolContext?.custom?.traceId,
954
+ });
880
955
  // 不退出,继续下一轮迭代让 LLM 继续执行任务
881
956
  continue;
882
957
  }
@@ -913,6 +988,17 @@ ${textHistory.join('\n')}`;
913
988
  timestamp: Date.now(),
914
989
  iteration: state.iteration,
915
990
  });
991
+ // 记录可观测 error 事件:让 trace 时间线解释"为何熔断",
992
+ // 与 on_complete(runStatus='error') 闭环,确保异常被统计进可观测。
993
+ await this.runHooks('on_error', {
994
+ sessionId: state.sessionId,
995
+ iteration: state.iteration,
996
+ error: failMsg,
997
+ recoverable: false,
998
+ });
999
+ state.hadFatalError = true;
1000
+ state.fatalErrorMsg = failMsg;
1001
+ state.lastContent = this.generateFallbackSummary(state);
916
1002
  yield this.emitDone(state);
917
1003
  return;
918
1004
  }
@@ -922,7 +1008,15 @@ ${textHistory.join('\n')}`;
922
1008
  }
923
1009
  }
924
1010
  // 渐进式结果交付:并行分发多个 delegate_task 后,提示 LLM 逐步呈现结果
1011
+ const delegateCount = assistant.tool_calls?.filter(tc => tc.function.name === 'delegate_task').length ?? 0;
925
1012
  this.injectProgressiveNudge(state, assistant.tool_calls);
1013
+ if (delegateCount >= 2) {
1014
+ await this.runHooks('on_progressive_nudge', {
1015
+ sessionId: state.sessionId,
1016
+ iteration: state.iteration,
1017
+ traceId: state.toolContext?.custom?.traceId,
1018
+ });
1019
+ }
926
1020
  // 标记本轮有工具调用(用于下一轮检测 LLM 是否提前停止)
927
1021
  state.lastIterationHadToolCalls = true;
928
1022
  // Middleware.after
@@ -958,8 +1052,13 @@ ${textHistory.join('\n')}`;
958
1052
  };
959
1053
  state.messages.push(summaryMsg);
960
1054
  this.historyManager.addHistory(state.sessionId, summaryMsg);
961
- (0, logger_ts_1.logEvent)({ event: 'engine.final_summary_injected', sessionId: state.sessionId, data: { iteration: state.iteration, max: maxIter } });
1055
+ // 主动压缩上下文:防止累积的工具结果导致 prompt 过大、LLM 调用失败
1056
+ const maxContextTokens = this.config.contextBudget?.maxContextTokens ?? 100000;
1057
+ const compressedTarget = Math.floor(maxContextTokens * 0.6);
1058
+ const tokensAfter = this.historyManager.compressMessages(state.messages, compressedTarget);
1059
+ (0, logger_ts_1.logEvent)({ event: 'engine.final_summary_injected', sessionId: state.sessionId, data: { iteration: state.iteration, max: maxIter, compressedTokens: tokensAfter } });
962
1060
  // 最后一次 LLM 调用(不再执行工具,直接产出文本)
1061
+ this.refreshToolSection(state);
963
1062
  this.statusMap.set(state.sessionId, 'thinking');
964
1063
  const { assistant: finalAssistant, usage: finalUsage } = yield* this.llmCaller.callWithRetry(state.sessionId, state.messages, state.toolDefs, state.internalAbort.signal);
965
1064
  if (finalUsage) {
@@ -977,10 +1076,51 @@ ${textHistory.join('\n')}`;
977
1076
  state.lastContent = (0, types_ts_1.extractTextFromContent)(finalAssistant.content);
978
1077
  }
979
1078
  }
1079
+ else {
1080
+ // LLM 调用失败:生成兜底总结,确保用户始终能收到有意义的回复
1081
+ state.lastContent = this.generateFallbackSummary(state);
1082
+ (0, logger_ts_1.logEvent)({ event: 'engine.fallback_summary', sessionId: state.sessionId, level: 'warn', data: { iteration: state.iteration, toolCalls: state.toolCallRecords.length } });
1083
+ }
980
1084
  this.statusMap.set(state.sessionId, 'idle');
981
1085
  }
982
1086
  yield this.emitDone(state);
983
1087
  }
1088
+ /**
1089
+ * 生成兜底总结(当 LLM 调用失败或无内容输出时使用)。
1090
+ * 从工具调用记录中提取摘要信息,确保用户始终能收到有意义的回复,
1091
+ * 而非仅看到 "Max iterations reached"。
1092
+ */
1093
+ generateFallbackSummary(state) {
1094
+ const records = state.toolCallRecords;
1095
+ if (records.length === 0) {
1096
+ return '任务未能完成:未执行任何工具调用,可能由于配置问题或会话被中断。请重试或联系管理员。';
1097
+ }
1098
+ // 按工具名分组统计
1099
+ const byTool = new Map();
1100
+ for (const r of records) {
1101
+ const entry = byTool.get(r.tool) || { total: 0, failed: 0 };
1102
+ entry.total++;
1103
+ if (r.isError)
1104
+ entry.failed++;
1105
+ byTool.set(r.tool, entry);
1106
+ }
1107
+ const lines = [
1108
+ '抱歉,任务未能完成。以下是执行摘要:',
1109
+ '',
1110
+ `**执行轮次**: ${state.iteration} 轮`,
1111
+ `**工具调用**: ${records.length} 次`,
1112
+ '',
1113
+ '**尝试的工具及结果**:',
1114
+ ];
1115
+ for (const [tool, info] of byTool) {
1116
+ const status = info.failed === info.total
1117
+ ? '全部失败'
1118
+ : info.failed > 0 ? `${info.failed}/${info.total} 失败` : '成功但未获得有效数据';
1119
+ lines.push(`- \`${tool}\`: ${info.total} 次调用 (${status})`);
1120
+ }
1121
+ lines.push('', '**可能的原因**:', '- 外部数据源无法访问(网络限制/API密钥/数据源变更)', '', '**建议**:', '1. 提供具体的数据文件或 API 密钥', '2. 手动提供数据让我进行分析', '3. 稍后重试或更换网络环境');
1122
+ return lines.join('\n');
1123
+ }
984
1124
  // ---- Live Steering(实时转向)----
985
1125
  /**
986
1126
  * 向运行中的 Agent 注入 steering 消息
@@ -1105,8 +1245,16 @@ ${textHistory.join('\n')}`;
1105
1245
  const before = state.toolDefs.length;
1106
1246
  state.toolDefs = state.toolDefs.filter(t => !this.dataFetchTools.has(t.function.name));
1107
1247
  const removed = before - state.toolDefs.length;
1248
+ // 同步将禁用的工具名加入 blockedTools 集合,供 tool executor 在执行层拦截
1249
+ // LLM 基于对话历史的幻觉调用(toolDefs 仅控制 LLM 可见的工具列表,
1250
+ // 但 tool executor 检查的是 toolRegistry,不会拦截已注册但被禁用的工具)
1251
+ if (!state.blockedTools)
1252
+ state.blockedTools = new Set();
1253
+ for (const name of this.dataFetchTools) {
1254
+ state.blockedTools.add(name);
1255
+ }
1108
1256
  if (removed > 0) {
1109
- (0, logger_ts_1.logEvent)({ event: 'engine.tools_disabled', sessionId: state.sessionId, data: { removed, remaining: state.toolDefs.length } });
1257
+ (0, logger_ts_1.logEvent)({ event: 'engine.tools_disabled', sessionId: state.sessionId, data: { removed, remaining: state.toolDefs.length, blocked: state.blockedTools.size } });
1110
1258
  }
1111
1259
  }
1112
1260
  /**
@@ -1146,6 +1294,17 @@ ${textHistory.join('\n')}`;
1146
1294
  return;
1147
1295
  }
1148
1296
  }
1297
+ // --- 条件 3:跨迭代累计失败(总调用 >=8 且失败率 >=70%) ---
1298
+ // 解决 LLM 在不同迭代中切换工具类型(web_fetch -> http_request -> browser_navigate)
1299
+ // 导致每种工具的连续失败计数都不够触发条件 2 的问题。
1300
+ const totalCalls = state.toolCallRecords.length;
1301
+ if (totalCalls >= 8) {
1302
+ const totalErrors = state.toolCallRecords.filter(r => r.isError).length;
1303
+ if (totalErrors >= Math.floor(totalCalls * 0.7)) {
1304
+ this.injectDataFetchGuard(state, state.toolCallRecords.slice(-6), 'cumulative_failures');
1305
+ return;
1306
+ }
1307
+ }
1149
1308
  }
1150
1309
  /** 注入数据获取循环/连续失败熔断提示 */
1151
1310
  injectDataFetchGuard(state, recent, reason) {
@@ -1339,17 +1498,20 @@ ${textHistory.join('\n')}`;
1339
1498
  totalUsage: state.totalUsage,
1340
1499
  });
1341
1500
  }
1501
+ const finalContent = state.lastContent || this.generateFallbackSummary(state);
1342
1502
  this.runHooks('on_complete', {
1343
1503
  sessionId: state.sessionId,
1344
1504
  iteration: state.iteration,
1345
1505
  totalIterations: state.iteration,
1346
1506
  usage: state.totalUsage,
1347
1507
  traceId: state.toolContext?.custom?.traceId,
1348
- output: state.lastContent || undefined,
1508
+ output: finalContent,
1509
+ runStatus: state.hadFatalError ? 'error' : 'ok',
1510
+ error: state.fatalErrorMsg,
1349
1511
  }).catch(() => { });
1350
1512
  return this.createEvent(state.sessionId, {
1351
1513
  type: 'done',
1352
- content: state.lastContent || 'Max iterations reached',
1514
+ content: finalContent,
1353
1515
  iterations: state.iteration,
1354
1516
  usage: state.totalUsage,
1355
1517
  });