@cicctencent/agent-core 1.0.34 → 1.0.35

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 (139) hide show
  1. package/dist/cjs/a2a/rest-runner.d.ts.map +1 -1
  2. package/dist/cjs/a2a/rest-runner.js +24 -5
  3. package/dist/cjs/a2a/rest-runner.js.map +1 -1
  4. package/dist/cjs/a2a/runner.d.ts.map +1 -1
  5. package/dist/cjs/a2a/runner.js +33 -8
  6. package/dist/cjs/a2a/runner.js.map +1 -1
  7. package/dist/cjs/agent/compaction.d.ts.map +1 -1
  8. package/dist/cjs/agent/compaction.js +1 -4
  9. package/dist/cjs/agent/compaction.js.map +1 -1
  10. package/dist/cjs/agent/constants.d.ts +14 -0
  11. package/dist/cjs/agent/constants.d.ts.map +1 -0
  12. package/dist/cjs/agent/constants.js +30 -0
  13. package/dist/cjs/agent/constants.js.map +1 -0
  14. package/dist/cjs/agent/engine.d.ts +38 -2
  15. package/dist/cjs/agent/engine.d.ts.map +1 -1
  16. package/dist/cjs/agent/engine.js +53 -378
  17. package/dist/cjs/agent/engine.js.map +1 -1
  18. package/dist/cjs/agent/guard-controller.d.ts +64 -0
  19. package/dist/cjs/agent/guard-controller.d.ts.map +1 -0
  20. package/dist/cjs/agent/guard-controller.js +265 -0
  21. package/dist/cjs/agent/guard-controller.js.map +1 -0
  22. package/dist/cjs/agent/llm-caller.d.ts.map +1 -1
  23. package/dist/cjs/agent/llm-caller.js +6 -3
  24. package/dist/cjs/agent/llm-caller.js.map +1 -1
  25. package/dist/cjs/agent/rate-limit-manager.d.ts +8 -0
  26. package/dist/cjs/agent/rate-limit-manager.d.ts.map +1 -1
  27. package/dist/cjs/agent/rate-limit-manager.js +36 -0
  28. package/dist/cjs/agent/rate-limit-manager.js.map +1 -1
  29. package/dist/cjs/agent/runtime-builder.d.ts +44 -0
  30. package/dist/cjs/agent/runtime-builder.d.ts.map +1 -1
  31. package/dist/cjs/agent/runtime-builder.js +78 -20
  32. package/dist/cjs/agent/runtime-builder.js.map +1 -1
  33. package/dist/cjs/agent/session-state-manager.d.ts +47 -0
  34. package/dist/cjs/agent/session-state-manager.d.ts.map +1 -0
  35. package/dist/cjs/agent/session-state-manager.js +85 -0
  36. package/dist/cjs/agent/session-state-manager.js.map +1 -0
  37. package/dist/cjs/agent/tool-executor.d.ts +2 -3
  38. package/dist/cjs/agent/tool-executor.d.ts.map +1 -1
  39. package/dist/cjs/agent/tool-executor.js +9 -16
  40. package/dist/cjs/agent/tool-executor.js.map +1 -1
  41. package/dist/cjs/agent/tool-section-builder.d.ts +55 -0
  42. package/dist/cjs/agent/tool-section-builder.d.ts.map +1 -0
  43. package/dist/cjs/agent/tool-section-builder.js +191 -0
  44. package/dist/cjs/agent/tool-section-builder.js.map +1 -0
  45. package/dist/cjs/agent/types.d.ts +1 -1
  46. package/dist/cjs/agent/types.d.ts.map +1 -1
  47. package/dist/cjs/index.d.ts +1 -1
  48. package/dist/cjs/index.d.ts.map +1 -1
  49. package/dist/cjs/index.js +8 -5
  50. package/dist/cjs/index.js.map +1 -1
  51. package/dist/cjs/mcp/base-client.js +2 -2
  52. package/dist/cjs/mcp/base-client.js.map +1 -1
  53. package/dist/cjs/mcp/client.js +4 -4
  54. package/dist/cjs/mcp/client.js.map +1 -1
  55. package/dist/cjs/mcp/factory.d.ts +6 -1
  56. package/dist/cjs/mcp/factory.d.ts.map +1 -1
  57. package/dist/cjs/mcp/factory.js +7 -2
  58. package/dist/cjs/mcp/factory.js.map +1 -1
  59. package/dist/cjs/mcp/http-client.js +5 -5
  60. package/dist/cjs/mcp/http-client.js.map +1 -1
  61. package/dist/cjs/observability/logger.d.ts.map +1 -1
  62. package/dist/cjs/observability/logger.js +56 -2
  63. package/dist/cjs/observability/logger.js.map +1 -1
  64. package/dist/cjs/tool/ask-user.js +3 -3
  65. package/dist/cjs/tool/delegate.d.ts.map +1 -1
  66. package/dist/cjs/tool/delegate.js +4 -2
  67. package/dist/cjs/tool/delegate.js.map +1 -1
  68. package/dist/cjs/utils/dsml-parser.d.ts +1 -1
  69. package/dist/cjs/utils/dsml-parser.js +1 -1
  70. package/dist/esm/a2a/rest-runner.d.ts.map +1 -1
  71. package/dist/esm/a2a/rest-runner.js +24 -5
  72. package/dist/esm/a2a/rest-runner.js.map +1 -1
  73. package/dist/esm/a2a/runner.d.ts.map +1 -1
  74. package/dist/esm/a2a/runner.js +33 -8
  75. package/dist/esm/a2a/runner.js.map +1 -1
  76. package/dist/esm/agent/compaction.d.ts.map +1 -1
  77. package/dist/esm/agent/compaction.js +1 -4
  78. package/dist/esm/agent/compaction.js.map +1 -1
  79. package/dist/esm/agent/constants.d.ts +14 -0
  80. package/dist/esm/agent/constants.d.ts.map +1 -0
  81. package/dist/esm/agent/constants.js +27 -0
  82. package/dist/esm/agent/constants.js.map +1 -0
  83. package/dist/esm/agent/engine.d.ts +38 -2
  84. package/dist/esm/agent/engine.d.ts.map +1 -1
  85. package/dist/esm/agent/engine.js +54 -379
  86. package/dist/esm/agent/engine.js.map +1 -1
  87. package/dist/esm/agent/guard-controller.d.ts +64 -0
  88. package/dist/esm/agent/guard-controller.d.ts.map +1 -0
  89. package/dist/esm/agent/guard-controller.js +256 -0
  90. package/dist/esm/agent/guard-controller.js.map +1 -0
  91. package/dist/esm/agent/llm-caller.d.ts.map +1 -1
  92. package/dist/esm/agent/llm-caller.js +6 -3
  93. package/dist/esm/agent/llm-caller.js.map +1 -1
  94. package/dist/esm/agent/rate-limit-manager.d.ts +8 -0
  95. package/dist/esm/agent/rate-limit-manager.d.ts.map +1 -1
  96. package/dist/esm/agent/rate-limit-manager.js +36 -0
  97. package/dist/esm/agent/rate-limit-manager.js.map +1 -1
  98. package/dist/esm/agent/runtime-builder.d.ts +44 -0
  99. package/dist/esm/agent/runtime-builder.d.ts.map +1 -1
  100. package/dist/esm/agent/runtime-builder.js +75 -20
  101. package/dist/esm/agent/runtime-builder.js.map +1 -1
  102. package/dist/esm/agent/session-state-manager.d.ts +47 -0
  103. package/dist/esm/agent/session-state-manager.d.ts.map +1 -0
  104. package/dist/esm/agent/session-state-manager.js +78 -0
  105. package/dist/esm/agent/session-state-manager.js.map +1 -0
  106. package/dist/esm/agent/tool-executor.d.ts +2 -3
  107. package/dist/esm/agent/tool-executor.d.ts.map +1 -1
  108. package/dist/esm/agent/tool-executor.js +9 -16
  109. package/dist/esm/agent/tool-executor.js.map +1 -1
  110. package/dist/esm/agent/tool-section-builder.d.ts +55 -0
  111. package/dist/esm/agent/tool-section-builder.d.ts.map +1 -0
  112. package/dist/esm/agent/tool-section-builder.js +182 -0
  113. package/dist/esm/agent/tool-section-builder.js.map +1 -0
  114. package/dist/esm/agent/types.d.ts +1 -1
  115. package/dist/esm/agent/types.d.ts.map +1 -1
  116. package/dist/esm/index.d.ts +1 -1
  117. package/dist/esm/index.d.ts.map +1 -1
  118. package/dist/esm/index.js +1 -1
  119. package/dist/esm/index.js.map +1 -1
  120. package/dist/esm/mcp/base-client.js +2 -2
  121. package/dist/esm/mcp/base-client.js.map +1 -1
  122. package/dist/esm/mcp/client.js +4 -4
  123. package/dist/esm/mcp/client.js.map +1 -1
  124. package/dist/esm/mcp/factory.d.ts +6 -1
  125. package/dist/esm/mcp/factory.d.ts.map +1 -1
  126. package/dist/esm/mcp/factory.js +7 -2
  127. package/dist/esm/mcp/factory.js.map +1 -1
  128. package/dist/esm/mcp/http-client.js +5 -5
  129. package/dist/esm/mcp/http-client.js.map +1 -1
  130. package/dist/esm/observability/logger.d.ts.map +1 -1
  131. package/dist/esm/observability/logger.js +56 -2
  132. package/dist/esm/observability/logger.js.map +1 -1
  133. package/dist/esm/tool/ask-user.js +3 -3
  134. package/dist/esm/tool/delegate.d.ts.map +1 -1
  135. package/dist/esm/tool/delegate.js +4 -2
  136. package/dist/esm/tool/delegate.js.map +1 -1
  137. package/dist/esm/utils/dsml-parser.d.ts +1 -1
  138. package/dist/esm/utils/dsml-parser.js +1 -1
  139. package/package.json +1 -1
@@ -9,31 +9,12 @@ import { LLMCaller } from "./llm-caller.js";
9
9
  import { AgentToolExecutor } from "./tool-executor.js";
10
10
  import { logEvent } from "../observability/logger.js";
11
11
  import { rateLimitManager } from "./rate-limit-manager.js";
12
- import { summarizeToolCall, summarizeToolDef } from "./compaction.js";
12
+ import { summarizeToolCall } from "./compaction.js";
13
+ import { resolveToolDefs as resolveToolDefsFn, summarizeToolDefImpl, buildToolContext as buildToolContextFn, buildDynamicToolSection as buildDynamicToolSectionFn, renderToolRefs as renderToolRefsFn, refreshToolSection as refreshToolSectionFn, createNoopShell, } from "./tool-section-builder.js";
14
+ import { injectProgressNudge as injectProgressNudgeFn, injectIterationGuard as injectIterationGuardFn, disableDataFetchTools as disableDataFetchToolsFn, checkDataFetchLoop as checkDataFetchLoopFn, injectDataFetchGuard as injectDataFetchGuardFn, injectContinuationNudge as injectContinuationNudgeFn, injectProgressiveNudge as injectProgressiveNudgeFn, } from "./guard-controller.js";
15
+ import { steer as steerFn, drainSteering as drainSteeringFn, abortSession as abortSessionFn, abortCascade as abortCascadeFn, cleanupSessionRelations as cleanupSessionRelationsFn, } from "./session-state-manager.js";
13
16
  /** 默认系统提示词 */
14
17
  const DEFAULT_BASE_PROMPT = `You are a helpful AI assistant with access to tools. Use tools when needed to accomplish tasks. Always explain your actions clearly.`;
15
- /**
16
- * 核心工具集(summary-all 模式下保持全量 schema 的工具)。
17
- *
18
- * 这些工具是 Agent 执行绝大多数任务的基础(文件读写、代码搜索、命令执行、
19
- * 工具发现、委托派发),必须让 LLM 看到完整参数 schema 以保证调用可靠性。
20
- * 非核心工具(通讯、日历、自动化、文档生成等)仅以摘要形式呈现,
21
- * LLM 需通过 read_tool_doc 获取完整 schema 后再调用。
22
- */
23
- const CORE_TOOL_NAMES = new Set([
24
- 'read_file',
25
- 'write_file',
26
- 'edit_file',
27
- 'append_file',
28
- 'search_code',
29
- 'execute_command',
30
- 'run_script',
31
- 'read_terminal',
32
- 'read_tool_doc',
33
- 'read_tool_result',
34
- 'read_offload',
35
- 'delegate_task',
36
- ]);
37
18
  /** 进度叙述规则(追加到 system prompt,引导 LLM 在工具调用间输出可见文本)— 可通过 AgentConfig.progressNarrationRules 覆盖 */
38
19
  const DEFAULT_PROGRESS_NARRATION_RULES = `
39
20
 
@@ -103,6 +84,8 @@ export class AgentEngine {
103
84
  childSessionMap = new Map();
104
85
  /** childSessionId → parentSessionId(反向索引,用于快速查找父会话) */
105
86
  parentSessionMap = new Map();
87
+ /** 被摘要化的工具名集合(progressive loading:这些工具的 prompt 不注入 system prompt) */
88
+ summarizedToolNames = new Set();
106
89
  /** 子模块:历史管理器 */
107
90
  historyManager;
108
91
  /** 子模块:LLM 调用器 */
@@ -123,6 +106,33 @@ export class AgentEngine {
123
106
  guardConfig;
124
107
  /** 错误恢复策略配置 */
125
108
  errorRecovery;
109
+ /** 为 ToolSectionBuilder 提供依赖 */
110
+ get toolSectionDeps() {
111
+ return { config: this.config, summarizedToolNames: this.summarizedToolNames };
112
+ }
113
+ /** 为 GuardController 提供依赖 */
114
+ get guardControllerDeps() {
115
+ return {
116
+ historyManager: this.historyManager,
117
+ dataFetchTools: this.dataFetchTools,
118
+ outputTools: this.outputTools,
119
+ guardConfig: this.guardConfig,
120
+ maxIterations: this.config.maxIterations,
121
+ hasTool: (name) => this.hasTool(name),
122
+ runHooks: (event, ctx) => this.runHooks(event, ctx),
123
+ };
124
+ }
125
+ /** 为 SessionStateManager 提供依赖 */
126
+ get sessionStateDeps() {
127
+ return {
128
+ abortMap: this.abortMap,
129
+ steeringMap: this.steeringMap,
130
+ statusMap: this.statusMap,
131
+ childSessionMap: this.childSessionMap,
132
+ parentSessionMap: this.parentSessionMap,
133
+ historyManager: this.historyManager,
134
+ };
135
+ }
126
136
  constructor(config) {
127
137
  this.config = {
128
138
  ...config,
@@ -173,8 +183,7 @@ export class AgentEngine {
173
183
  // 模型在收尾阶段无法再用它读取 data/ 下的结果文件。
174
184
  // 若某 agent 仍需将其纳入数据获取循环检测,可在 AgentConfig.dataFetchTools 中显式加回。
175
185
  this.outputTools = new Set(config.outputTools ?? [
176
- 'delegate_task', 'create_presentation', 'create_document',
177
- 'create_pdf', 'create_report', 'show_widget', 'save_artifact',
186
+ 'delegate_task', 'create_document', 'show_widget', 'save_artifact',
178
187
  ]);
179
188
  // 外部数据源获取工具(用于数据出处水印判定,仅含真正访问外部/实时数据源的工具,
180
189
  // 不含 read_file 等本地读取——本地读取不构成「实时数据核验」但也不代表数据源失败)
@@ -691,67 +700,19 @@ ${textHistory.join('\n')}`;
691
700
  // ---- 工具定义 ----
692
701
  /** 获取工具定义列表(含预过滤 + 渐进式发现) */
693
702
  resolveToolDefs(options) {
694
- const toolNames = [...(this.config.defaultTools || []), ...(options.extraTools || [])];
695
- let defs = toolNames.length > 0
696
- ? this.config.toolRegistry.getDefinitions({ names: toolNames })
697
- : this.config.toolRegistry.getDefinitions();
698
- if (this.config.deniedTools?.length) {
699
- const denied = new Set(this.config.deniedTools);
700
- defs = defs.filter(d => !denied.has(d.function.name));
701
- }
702
- const discovery = this.config.toolDiscovery;
703
- // summary 模式:仅 MCP 工具摘要
704
- if (discovery === 'summary') {
705
- defs = defs.map(d => this.summarizeToolDef(d, 'mcp'));
706
- }
707
- // summary-all 模式:所有非核心工具摘要(MCP + 非核心内置工具)
708
- if (discovery === 'summary-all') {
709
- const coreOverride = this.config.coreToolNames
710
- ? new Set(this.config.coreToolNames)
711
- : CORE_TOOL_NAMES;
712
- defs = defs.map(d => {
713
- const isCore = coreOverride.has(d.function.name);
714
- return this.summarizeToolDef(d, isCore ? null : 'all');
715
- });
716
- }
717
- return defs;
703
+ return resolveToolDefsFn(this.toolSectionDeps, options);
718
704
  }
719
705
  /**
720
706
  * 将工具定义转为摘要形式(截断 description + 清空 parameters)。
721
- * 实现抽离至 ./compaction.ts,保持单一来源。
707
+ * 实现抽离至 ./tool-section-builder.ts -> compaction.ts,保持单一来源。
722
708
  */
723
709
  summarizeToolDef(def, scope) {
724
- return summarizeToolDef(def, {
725
- scope,
726
- maxDesc: this.config.toolSummaryMaxDesc ?? 100,
727
- getCategory: (name) => this.config.toolRegistry.get(name)?.category,
728
- });
710
+ return summarizeToolDefImpl(this.toolSectionDeps, def, scope);
729
711
  }
730
712
  // ---- 准备阶段(续) ----
731
713
  /** 构建 ToolContext */
732
714
  buildToolContext(options, internalAbort) {
733
- const custom = options.context?.custom;
734
- return {
735
- sessionId: options.sessionId,
736
- shell: custom?.shell || this.createNoopShell(),
737
- fs: custom?.fs,
738
- env: options.context?.env,
739
- abortSignal: internalAbort.signal,
740
- custom: {
741
- ...custom,
742
- // Fix A:始终使用引擎授权注册表(已按 selectedTools/disabledTools 过滤),
743
- // 不接受 custom.toolRegistry 覆盖——防止 read_tool_doc/diagnose 泄漏被禁工具 schema。
744
- toolRegistry: this.config.toolRegistry,
745
- skillManager: custom?.skillManager ?? this.config.skillManager,
746
- },
747
- emitSubEvent: custom?.emitSubEvent,
748
- emitWidget: custom?.emitWidget,
749
- emitArtifact: custom?.emitArtifact,
750
- emitToolLog: custom?.emitToolLog,
751
- emitToolHeartbeat: custom?.emitToolHeartbeat,
752
- toolTimeout: this.config.toolTimeout,
753
- maxScriptTimeoutMs: this.config.maxScriptTimeoutMs,
754
- };
715
+ return buildToolContextFn(this.toolSectionDeps, options, internalAbort);
755
716
  }
756
717
  /** 汇总准备阶段,生成 LoopState */
757
718
  async prepareRun(options, internalAbort) {
@@ -790,28 +751,7 @@ ${textHistory.join('\n')}`;
790
751
  /** 动态构建工具区 prompt(Available Tools + Tool Usage Guidelines)。
791
752
  * 从 state.toolDefs 现场拼装,确保与可执行工具集一致,消除静态穿透。 */
792
753
  buildDynamicToolSection(toolDefs) {
793
- if (this.config.buildToolSection) {
794
- return this.config.buildToolSection(toolDefs, this.config.toolRegistry);
795
- }
796
- if (toolDefs.length === 0)
797
- return '';
798
- // 可用工具名集合:用于占位符 {{TOOL:name}} 渲染时判定引用是否真实可用
799
- const available = new Set(toolDefs.map(d => d.function.name));
800
- const lines = toolDefs.map(d => `- \`${d.function.name}\`: ${this.renderToolRefs(d.function.description || '', available)}`);
801
- let section = `\n\n## Available Tools\n${lines.join('\n')}`;
802
- // 收集工具自带的使用引导(tool.prompt),并对其中对其他工具的引用做可用裁剪。
803
- // tool.relatedTools 是声明式的「本工具引用的其它工具」清单,
804
- // 用于:若某占位符 {{TOOL:x}} 未注册,则整段引用被剔除(见 renderToolRefs)。
805
- const prompts = [];
806
- for (const def of toolDefs) {
807
- const tool = this.config.toolRegistry.get(def.function.name);
808
- if (tool?.prompt)
809
- prompts.push(this.renderToolRefs(tool.prompt, available, tool.relatedTools));
810
- }
811
- if (prompts.length > 0) {
812
- section += `\n\n## Tool Usage Guidelines\n\n${prompts.join('\n\n')}`;
813
- }
814
- return section;
754
+ return buildDynamicToolSectionFn(this.toolSectionDeps, toolDefs);
815
755
  }
816
756
  /**
817
757
  * 渲染工具提示中对其它工具的引用,避免「提示穿透」:
@@ -826,45 +766,11 @@ ${textHistory.join('\n')}`;
826
766
  * 显式声明在 relatedTools 中的工具名也可被识别为「已知引用」参与裁剪。
827
767
  */
828
768
  renderToolRefs(text, available, relatedTools) {
829
- // relatedTools 声明本工具引用的「其他工具」。占位符 {{TOOL:name}} 只有在
830
- // - name 确实注册(available)时才渲染为 `name`;
831
- // - 否则用哨兵标记,后续做子句级清除,避免留下 "do NOT call after" 这类残缺。
832
- // 不在 relatedTools 且未注册的占位符视为误写,保留原文。
833
- const knownRefs = relatedTools && relatedTools.length > 0 ? new Set(relatedTools) : null;
834
- const SENTINEL = '\x00'; // 哨兵:标记被剔除的占位符位置
835
- // 1) 替换:可用 -> name;不可用且为已知引用 -> 哨兵;其余保留原文
836
- let out = text.replace(/\{\{TOOL:([a-zA-Z0-9_-]+)\}\}/g, (m, name) => {
837
- if (available.has(name))
838
- return `\`${name}\``;
839
- if (knownRefs && !knownRefs.has(name))
840
- return m;
841
- return SENTINEL;
842
- });
843
- // 2) 子句级清除:以 " - " 或 ", " 分隔的子句中含哨兵,则移除整个子句
844
- out = out
845
- .split(/(?<=\S)\s+-\s+|(?<=\S),\s+/)
846
- .filter(clause => !clause.includes(SENTINEL))
847
- .join(', ');
848
- // 3) 行级清除:多行 prompt 中,整行仅剩哨兵/标点/空白的予以移除
849
- out = out
850
- .split('\n')
851
- .filter(line => {
852
- const trimmed = line.trim();
853
- if (trimmed === '')
854
- return true;
855
- const meaningful = trimmed.replace(/[`.,;:()\-]+/g, '').replace(/\s+/g, '');
856
- return meaningful.length > 0 && !trimmed.includes(SENTINEL);
857
- })
858
- .join('\n');
859
- // 4) 清理残留哨兵 + 收缩多余空格
860
- out = out.replace(/\x00/g, '').replace(/ {2,}/g, ' ').replace(/\s+\n/g, '\n').trim();
861
- return out;
769
+ return renderToolRefsFn(text, available, relatedTools);
862
770
  }
863
771
  /** 刷新 system prompt 中的工具区,确保与 state.toolDefs 一致 */
864
772
  refreshToolSection(state) {
865
- if (state.messages[0] && state.stableSystemPrompt != null) {
866
- state.messages[0].content = state.stableSystemPrompt + this.buildDynamicToolSection(state.toolDefs);
867
- }
773
+ refreshToolSectionFn(this.toolSectionDeps, state);
868
774
  }
869
775
  // ---- Agent Loop ----
870
776
  /** 主循环:迭代调用 LLM → 执行工具 → 直到完成或达到上限 */
@@ -1275,27 +1181,11 @@ ${textHistory.join('\n')}`;
1275
1181
  * 消息将在下次 LLM 调用前注入对话上下文
1276
1182
  */
1277
1183
  steer(sessionId, message) {
1278
- if (!this.abortMap.has(sessionId))
1279
- return false;
1280
- if (!this.steeringMap.has(sessionId)) {
1281
- this.steeringMap.set(sessionId, []);
1282
- }
1283
- this.steeringMap.get(sessionId).push(message);
1284
- return true;
1184
+ return steerFn(this.sessionStateDeps, sessionId, message);
1285
1185
  }
1286
1186
  /** 消费当前会话的 steering 消息,注入到 messages 中 */
1287
1187
  drainSteering(state) {
1288
- const msgs = this.steeringMap.get(state.sessionId);
1289
- if (!msgs || msgs.length === 0)
1290
- return null;
1291
- const drained = [...msgs];
1292
- msgs.length = 0;
1293
- for (const msg of drained) {
1294
- const userMsg = { role: 'user', content: msg };
1295
- state.messages.push(userMsg);
1296
- this.historyManager.addHistory(state.sessionId, userMsg);
1297
- }
1298
- return { messages: drained, count: drained.length };
1188
+ return drainSteeringFn(this.sessionStateDeps, state);
1299
1189
  }
1300
1190
  /**
1301
1191
  * 进度叙述提醒
@@ -1303,23 +1193,7 @@ ${textHistory.join('\n')}`;
1303
1193
  * 确保用户在对话中有可见的文字输出,而不是所有内容都收在执行步骤中。
1304
1194
  */
1305
1195
  injectProgressNudge(state) {
1306
- const msg = {
1307
- role: 'user',
1308
- content: [
1309
- '[SYSTEM: PROGRESS NARRATION]',
1310
- '',
1311
- 'You have been calling tools without providing visible text to the user.',
1312
- 'Please provide a brief progress update in the user\'s language:',
1313
- '- What have you accomplished so far?',
1314
- '- What are you working on next?',
1315
- '- If you found important data, summarize the key findings.',
1316
- '',
1317
- 'Keep it to 1-3 sentences. This text is displayed directly to the user.',
1318
- ].join('\n'),
1319
- };
1320
- state.messages.push(msg);
1321
- this.historyManager.addHistory(state.sessionId, msg);
1322
- logEvent({ event: 'engine.progress_nudge', sessionId: state.sessionId, data: { iteration: state.iteration } });
1196
+ injectProgressNudgeFn(this.guardControllerDeps, state);
1323
1197
  }
1324
1198
  /**
1325
1199
  * 迭代预算提醒
@@ -1327,49 +1201,7 @@ ${textHistory.join('\n')}`;
1327
1201
  * 解决 agent 在数据查询循环中耗尽迭代、永远无法进入文档生成/委托阶段的问题。
1328
1202
  */
1329
1203
  injectIterationGuard(state) {
1330
- if (state.iterationGuardInjected)
1331
- return;
1332
- const max = this.config.maxIterations;
1333
- const cur = state.iteration;
1334
- const remaining = max - cur;
1335
- const ratio = cur / max;
1336
- if (ratio >= this.guardConfig.iterationGuardThreshold && remaining > 0) {
1337
- state.iterationGuardInjected = true;
1338
- const toolCallCount = state.toolCallRecords.length;
1339
- const targets = this.guardConfig.delegateTargets;
1340
- const reportAgent = targets.report ? `"${targets.report}"` : 'the appropriate report agent';
1341
- const docAgent = targets.document ? `"${targets.document}"` : 'the appropriate document agent';
1342
- const pptAgent = targets.presentation ? `"${targets.presentation}"` : docAgent;
1343
- // P1 #6: 使用可配置的提示文本,默认为通用英文提示(不绑定业务 Agent 名称)
1344
- // 仅引用当前 registry 实际注册的工具,避免引导 LLM 调用未绑定工具
1345
- const hasDelegate = this.hasTool('delegate_task');
1346
- const delegateLines = [];
1347
- if (hasDelegate) {
1348
- delegateLines.push(` - For reports/dashboards: call \`delegate_task\` with agent ${reportAgent}.`);
1349
- delegateLines.push(` - For documents: call \`delegate_task\` with agent ${docAgent}.`);
1350
- delegateLines.push(` - For PPT: call \`delegate_task\` with agent ${pptAgent}.`);
1351
- }
1352
- const promptText = this.guardConfig.iterationGuardPrompt || [
1353
- `[SYSTEM ITERATION BUDGET: iteration ${cur}/${max}, ${remaining} remaining]`,
1354
- '',
1355
- `⚠️ You have used ${cur} of ${max} iterations (${toolCallCount} tool calls). Budget is running low.`,
1356
- '',
1357
- '**MANDATORY rules for remaining iterations — violating these will result in task failure:**',
1358
- '1. **STOP all data gathering immediately.** You already have enough data from previous tool calls.',
1359
- '2. **DO NOT call web_fetch, execute_command, write_file, or run_script again.**',
1360
- ...(hasDelegate ? [`3. **NOW delegate remaining work (only if the user explicitly requested file/document generation):**`, ...delegateLines] : []),
1361
- '4. If you cannot delegate, produce the final output NOW with the data you have.',
1362
- '5. **Do NOT proactively generate files (PPT/PDF/HTML/Word) unless the user explicitly asked for them.** If the user only asked for analysis or information, present your findings directly in the conversation as text/markdown.',
1363
- '',
1364
- 'Do NOT write any more scripts or fetch any more data. Your data collection phase is OVER.',
1365
- ].join('\n');
1366
- const msg = { role: 'user', content: promptText };
1367
- state.messages.push(msg);
1368
- this.historyManager.addHistory(state.sessionId, msg);
1369
- logEvent({ event: 'engine.iteration_guard', sessionId: state.sessionId, data: { iteration: cur, max } });
1370
- // 硬性禁用数据获取工具,防止 LLM 无视文字提示继续调用
1371
- this.disableDataFetchTools(state);
1372
- }
1204
+ injectIterationGuardFn(this.guardControllerDeps, state);
1373
1205
  }
1374
1206
  /**
1375
1207
  * 从可用工具列表中移除数据获取类工具,强制 LLM 转向产出。
@@ -1377,20 +1209,7 @@ ${textHistory.join('\n')}`;
1377
1209
  * 仅移除尚未执行的 toolDefs,不影响正在执行的工具。
1378
1210
  */
1379
1211
  disableDataFetchTools(state) {
1380
- const before = state.toolDefs.length;
1381
- state.toolDefs = state.toolDefs.filter(t => !this.dataFetchTools.has(t.function.name));
1382
- const removed = before - state.toolDefs.length;
1383
- // 同步将禁用的工具名加入 blockedTools 集合,供 tool executor 在执行层拦截
1384
- // LLM 基于对话历史的幻觉调用(toolDefs 仅控制 LLM 可见的工具列表,
1385
- // 但 tool executor 检查的是 toolRegistry,不会拦截已注册但被禁用的工具)
1386
- if (!state.blockedTools)
1387
- state.blockedTools = new Set();
1388
- for (const name of this.dataFetchTools) {
1389
- state.blockedTools.add(name);
1390
- }
1391
- if (removed > 0) {
1392
- logEvent({ event: 'engine.tools_disabled', sessionId: state.sessionId, data: { removed, remaining: state.toolDefs.length, blocked: state.blockedTools.size } });
1393
- }
1212
+ disableDataFetchToolsFn(this.guardControllerDeps, state);
1394
1213
  }
1395
1214
  /**
1396
1215
  * 数据获取循环检测 + 连续失败熔断
@@ -1400,90 +1219,11 @@ ${textHistory.join('\n')}`;
1400
1219
  * 2. 连续失败熔断:最近 5+ 条工具调用中 4+ 条失败,强制停止试错、转向产出
1401
1220
  */
1402
1221
  checkDataFetchLoop(state) {
1403
- if (state.dataFetchGuardInjected)
1404
- return;
1405
- // 数据获取类工具(非产出型)— 使用实例配置
1406
- const DATA_FETCH_TOOLS = this.dataFetchTools;
1407
- // 产出型工具(有这些就不算数据获取循环)— 使用实例配置
1408
- const OUTPUT_TOOLS = this.outputTools;
1409
- const hasOutput = state.toolCallRecords.some(r => OUTPUT_TOOLS.has(r.tool));
1410
- if (hasOutput)
1411
- return; // 已调用过产出型工具,不再干预
1412
- const recent = state.toolCallRecords.slice(-6);
1413
- // --- 条件 1:数据获取循环(最近 N+ 条全是数据获取类) ---
1414
- const loopMin = this.guardConfig.dataFetchLoopMinCount;
1415
- if (recent.length >= loopMin) {
1416
- const lastN = recent.slice(-loopMin);
1417
- const allDataFetch = lastN.every(r => DATA_FETCH_TOOLS.has(r.tool));
1418
- if (allDataFetch) {
1419
- this.injectDataFetchGuard(state, recent, 'loop');
1420
- return;
1421
- }
1422
- }
1423
- // --- 条件 2:连续失败熔断(最近 N+ 条中 M+ 条失败) ---
1424
- const failMin = this.guardConfig.consecutiveFailMinCount;
1425
- if (recent.length >= failMin) {
1426
- const errorCount = recent.filter(r => r.isError).length;
1427
- if (errorCount >= this.guardConfig.consecutiveFailErrorThreshold) {
1428
- this.injectDataFetchGuard(state, recent, 'failures');
1429
- return;
1430
- }
1431
- }
1432
- // --- 条件 3:跨迭代累计失败(总调用 >=8 且失败率 >=70%) ---
1433
- // 解决 LLM 在不同迭代中切换工具类型(web_fetch -> http_request -> browser_navigate)
1434
- // 导致每种工具的连续失败计数都不够触发条件 2 的问题。
1435
- const totalCalls = state.toolCallRecords.length;
1436
- if (totalCalls >= 8) {
1437
- const totalErrors = state.toolCallRecords.filter(r => r.isError).length;
1438
- if (totalErrors >= Math.floor(totalCalls * 0.7)) {
1439
- this.injectDataFetchGuard(state, state.toolCallRecords.slice(-6), 'cumulative_failures');
1440
- return;
1441
- }
1442
- }
1222
+ checkDataFetchLoopFn(this.guardControllerDeps, state);
1443
1223
  }
1444
1224
  /** 注入数据获取循环/连续失败熔断提示 */
1445
1225
  injectDataFetchGuard(state, recent, reason) {
1446
- state.dataFetchGuardInjected = true;
1447
- // 触发 guardrail 可观测事件(fire-and-forget,不阻塞主流程)
1448
- void this.runHooks('on_data_fetch_loop', {
1449
- sessionId: state.sessionId,
1450
- iteration: state.iteration,
1451
- traceId: state.toolContext?.custom?.traceId,
1452
- });
1453
- const toolSummary = recent.map(r => `${r.tool}(${r.isError ? 'err' : 'ok'})`).join(', ');
1454
- const errorCount = recent.filter(r => r.isError).length;
1455
- const reasonText = reason === 'loop'
1456
- ? `Your last ${recent.length} tool calls are all data-fetching without any output: ${toolSummary}`
1457
- : `${errorCount} of your last ${recent.length} tool calls FAILED: ${toolSummary}`;
1458
- const msg = {
1459
- role: 'user',
1460
- content: [
1461
- `[SYSTEM: ${reason === 'loop' ? 'DATA GATHERING LOOP' : 'CONSECUTIVE FAILURES'} DETECTED]`,
1462
- '',
1463
- reasonText,
1464
- '',
1465
- '⛔ **You MUST stop trying to fetch more data NOW.**',
1466
- '',
1467
- '**Required action — do ONE of the following immediately:**',
1468
- ...(this.hasTool('delegate_task')
1469
- ? ['1. Call `delegate_task` with whatever data/context you have to let a specialist generate the output.']
1470
- : []),
1471
- '2. If delegation is not suitable, produce the final output NOW with available data.',
1472
- '3. At minimum, provide a clear text summary to the user explaining what you found and what failed.',
1473
- '',
1474
- '**Do NOT:**',
1475
- '- Write another script to fetch data',
1476
- '- Try another API, URL, or npm package',
1477
- '- Debug the current failure — accept it and move on',
1478
- '',
1479
- 'The user is waiting for OUTPUT, not more failed attempts.',
1480
- ].join('\n'),
1481
- };
1482
- state.messages.push(msg);
1483
- this.historyManager.addHistory(state.sessionId, msg);
1484
- logEvent({ event: 'engine.data_fetch_guard', sessionId: state.sessionId, data: { reason, iteration: state.iteration, recent: toolSummary } });
1485
- // 硬性禁用数据获取工具,防止 LLM 无视文字提示继续调用
1486
- this.disableDataFetchTools(state);
1226
+ injectDataFetchGuardFn(this.guardControllerDeps, state, recent, reason);
1487
1227
  }
1488
1228
  /**
1489
1229
  * 任务继续提示
@@ -1491,28 +1231,7 @@ ${textHistory.join('\n')}`;
1491
1231
  * 典型场景:多步委托任务中,第一步数据采集完成后 LLM 未生成后续步骤的 delegate_task。
1492
1232
  */
1493
1233
  injectContinuationNudge(state) {
1494
- const toolCallCount = state.toolCallRecords.length;
1495
- const msg = {
1496
- role: 'user',
1497
- content: [
1498
- '[SYSTEM: TASK CONTINUATION]',
1499
- '',
1500
- `You have completed ${toolCallCount} tool call(s) so far, but the original user request has NOT been fully fulfilled.`,
1501
- '',
1502
- 'Please review the original request and continue with the remaining steps:',
1503
- '- If you planned multi-step tasks (e.g., data gathering → PPT/report generation), **NOW execute the remaining steps**.',
1504
- ...(this.hasTool('delegate_task')
1505
- ? ['- Use `delegate_task` to delegate remaining work to appropriate specialists.']
1506
- : []),
1507
- '- Only if the user **explicitly requested** document/PPT/report generation, call the relevant tools now. Do NOT proactively generate files the user did not ask for.',
1508
- '- Do NOT re-gather data you already have. Use the results from previous tool calls.',
1509
- '',
1510
- 'Continue your work now.',
1511
- ].join('\n'),
1512
- };
1513
- state.messages.push(msg);
1514
- this.historyManager.addHistory(state.sessionId, msg);
1515
- logEvent({ event: 'engine.continuation_nudge_injected', sessionId: state.sessionId, data: { iteration: state.iteration, nudge: state.continuationNudges } });
1234
+ injectContinuationNudgeFn(this.guardControllerDeps, state);
1516
1235
  }
1517
1236
  /**
1518
1237
  * 渐进式结果交付
@@ -1520,28 +1239,7 @@ ${textHistory.join('\n')}`;
1520
1239
  * 而不是等所有结果全部返回后才开始输出。减少用户感知等待时间。
1521
1240
  */
1522
1241
  injectProgressiveNudge(state, toolCalls) {
1523
- const delegateCalls = toolCalls.filter(tc => tc.function.name === 'delegate_task');
1524
- if (delegateCalls.length < 2)
1525
- return;
1526
- const msg = {
1527
- role: 'user',
1528
- content: [
1529
- '[SYSTEM: PROGRESSIVE DELIVERY]',
1530
- '',
1531
- `You dispatched ${delegateCalls.length} sub-tasks in parallel. Some may succeed while others fail or take longer.`,
1532
- '',
1533
- 'When presenting results to the user:',
1534
- '1. **Present each completed result as it becomes available** — do NOT wait for all tasks to finish.',
1535
- '2. If a sub-agent reports network/sandbox restrictions or fails, **acknowledge it briefly and move on** with available data.',
1536
- '3. **Do NOT re-dispatch** a failed task to the same or different agent — use what you already have.',
1537
- '4. Start your final analysis/report with whatever data is available, noting which parts are missing.',
1538
- '',
1539
- 'The user values SPEED over COMPLETENESS. A partial result delivered in 2 minutes is better than a perfect result delivered in 15 minutes.',
1540
- ].join('\n'),
1541
- };
1542
- state.messages.push(msg);
1543
- this.historyManager.addHistory(state.sessionId, msg);
1544
- logEvent({ event: 'engine.progressive_nudge', sessionId: state.sessionId, data: { iteration: state.iteration, delegates: delegateCalls.length } });
1242
+ injectProgressiveNudgeFn(this.guardControllerDeps, state, toolCalls);
1545
1243
  }
1546
1244
  // ---- Hooks ----
1547
1245
  /** 执行指定事件类型的所有 Hook */
@@ -1661,12 +1359,7 @@ ${textHistory.join('\n')}`;
1661
1359
  }
1662
1360
  /** 中断指定会话的 Agent 执行 */
1663
1361
  abort(sessionId) {
1664
- const controller = this.abortMap.get(sessionId);
1665
- if (controller) {
1666
- controller.abort();
1667
- this.statusMap.set(sessionId, 'aborting');
1668
- logEvent({ event: 'engine.abort', sessionId, data: { reason: 'abort() called' } });
1669
- }
1362
+ abortSessionFn(this.sessionStateDeps, sessionId);
1670
1363
  }
1671
1364
  /**
1672
1365
  * 中断指定会话及其所有子会话的 Agent 执行(级联 abort)。
@@ -1675,14 +1368,7 @@ ${textHistory.join('\n')}`;
1675
1368
  * 用于确保父会话异常终止时,所有 delegate_task 派生的子任务也被终止。
1676
1369
  */
1677
1370
  abortCascade(sessionId) {
1678
- this.abort(sessionId);
1679
- // 遍历直接子会话,递归 abort
1680
- const children = this.getChildSessions(sessionId);
1681
- for (const childId of children) {
1682
- this.abortCascade(childId);
1683
- }
1684
- // 清理父子关系
1685
- this.cleanupSessionRelations(sessionId);
1371
+ abortCascadeFn(this.sessionStateDeps, sessionId);
1686
1372
  }
1687
1373
  /** 查询指定会话的 Agent 状态 */
1688
1374
  getStatus(sessionId) {
@@ -1762,25 +1448,14 @@ ${textHistory.join('\n')}`;
1762
1448
  * - 如果是子会话:从父会话的子集中移除自身
1763
1449
  */
1764
1450
  cleanupSessionRelations(sessionId) {
1765
- // 从父会话的子集中移除自身
1766
- const parentId = this.parentSessionMap.get(sessionId);
1767
- if (parentId) {
1768
- this.childSessionMap.get(parentId)?.delete(sessionId);
1769
- this.parentSessionMap.delete(sessionId);
1770
- }
1771
- // 删除自身作为父会话的所有子关系记录(但保留子会话本身仍在运行)
1772
- this.childSessionMap.delete(sessionId);
1451
+ cleanupSessionRelationsFn(this.sessionStateDeps, sessionId);
1773
1452
  }
1774
1453
  // ---- 内部辅助 ----
1775
1454
  createEvent(sessionId, event) {
1776
1455
  return { ...event, timestamp: Date.now(), sessionId };
1777
1456
  }
1778
1457
  createNoopShell() {
1779
- return {
1780
- exec: async () => 'Error: Shell executor not configured',
1781
- write: () => { },
1782
- read: () => '',
1783
- };
1458
+ return createNoopShell();
1784
1459
  }
1785
1460
  }
1786
1461
  //# sourceMappingURL=engine.js.map