foliko 1.1.92 → 2.0.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 (212) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/CLAUDE.md +56 -30
  3. package/REFACTORING_PLAN.md +645 -0
  4. package/docs/architecture.md +131 -0
  5. package/docs/migration.md +57 -0
  6. package/docs/public-api.md +138 -0
  7. package/docs/usage.md +385 -0
  8. package/examples/ambient-example.js +20 -137
  9. package/examples/basic.js +21 -48
  10. package/examples/bootstrap.js +16 -74
  11. package/examples/mcp-example.js +6 -29
  12. package/examples/skill-example.js +6 -19
  13. package/examples/workflow.js +8 -56
  14. package/package.json +8 -4
  15. package/plugins/README.md +49 -0
  16. package/plugins/{ambient-agent → ambient}/EventWatcher.js +1 -1
  17. package/plugins/{ambient-agent → ambient}/ExplorerLoop.js +3 -3
  18. package/plugins/{ambient-agent → ambient}/GoalManager.js +2 -2
  19. package/plugins/ambient/README.md +14 -0
  20. package/plugins/{ambient-agent → ambient}/Reflector.js +1 -1
  21. package/plugins/{ambient-agent → ambient}/StateStore.js +1 -1
  22. package/plugins/{ambient-agent → ambient}/index.js +2 -2
  23. package/plugins/{ai-plugin.js → core/ai/index.js} +14 -30
  24. package/plugins/{audit-plugin.js → core/audit/index.js} +3 -30
  25. package/plugins/{coordinator-plugin.js → core/coordinator/index.js} +3 -35
  26. package/plugins/core/default/bootstrap.js +202 -0
  27. package/plugins/core/default/config.js +220 -0
  28. package/plugins/core/default/index.js +58 -0
  29. package/plugins/core/mcp/index.js +1 -0
  30. package/plugins/{python-plugin-loader.js → core/python-loader/index.js} +7 -187
  31. package/plugins/{rules-plugin.js → core/rules/index.js} +121 -64
  32. package/plugins/{scheduler-plugin.js → core/scheduler/index.js} +12 -114
  33. package/plugins/{session-plugin.js → core/session/index.js} +9 -73
  34. package/{src/capabilities/skill-manager.js → plugins/core/skill-manager/index.js} +64 -18
  35. package/plugins/{storage-plugin.js → core/storage/index.js} +5 -29
  36. package/plugins/{subagent-plugin.js → core/sub-agent/index.js} +10 -171
  37. package/plugins/{think-plugin.js → core/think/index.js} +24 -91
  38. package/{src/capabilities/workflow-engine.js → plugins/core/workflow/index.js} +87 -85
  39. package/plugins/default-plugins.js +6 -720
  40. package/plugins/{data-splitter-plugin.js → executors/data-splitter/index.js} +9 -83
  41. package/plugins/{extension-executor-plugin.js → executors/extension/index.js} +13 -97
  42. package/plugins/{python-executor-plugin.js → executors/python/index.js} +6 -31
  43. package/plugins/{shell-executor-plugin.js → executors/shell/index.js} +2 -5
  44. package/plugins/install/README.md +9 -0
  45. package/plugins/{install-plugin.js → install/index.js} +3 -3
  46. package/plugins/{file-system-plugin.js → io/file-system/index.js} +34 -236
  47. package/plugins/{web-plugin.js → io/web/index.js} +11 -113
  48. package/plugins/memory/README.md +13 -0
  49. package/plugins/{memory-plugin.js → memory/index.js} +4 -18
  50. package/plugins/messaging/email/README.md +19 -0
  51. package/plugins/{email → messaging/email}/index.js +2 -2
  52. package/plugins/{feishu-plugin.js → messaging/feishu/index.js} +3 -3
  53. package/plugins/{qq-plugin.js → messaging/qq/index.js} +5 -16
  54. package/plugins/{telegram-plugin.js → messaging/telegram/index.js} +3 -3
  55. package/plugins/{weixin-plugin.js → messaging/weixin/index.js} +15 -15
  56. package/plugins/{plugin-manager-plugin.js → plugin-manager/index.js} +36 -180
  57. package/plugins/{tools-plugin.js → tools/index.js} +68 -116
  58. package/plugins/trading/README.md +15 -0
  59. package/plugins/{gate-trading.js → trading/index.js} +8 -8
  60. package/{examples → sandbox}/test-concurrent-chat.js +2 -2
  61. package/{examples → sandbox}/test-long-chat.js +2 -2
  62. package/{examples → sandbox}/test-session-chat.js +2 -2
  63. package/{examples → sandbox}/test-web-plugin.js +1 -1
  64. package/{examples → sandbox}/test-weixin-feishu.js +2 -2
  65. package/src/agent/base.js +56 -0
  66. package/src/{core/agent-chat.js → agent/chat.js} +11 -11
  67. package/src/{core/coordinator-manager.js → agent/coordinator.js} +3 -3
  68. package/src/agent/index.js +111 -0
  69. package/src/agent/main.js +337 -0
  70. package/src/agent/prompt.js +78 -0
  71. package/src/agent/sub.js +198 -0
  72. package/src/agent/worker.js +104 -0
  73. package/{cli/bin/foliko.js → src/cli/bin.js} +1 -1
  74. package/{cli/src → src/cli}/commands/chat.js +25 -21
  75. package/{cli/src → src/cli}/index.js +1 -0
  76. package/{cli/src → src/cli}/ui/chat-ui-old.js +40 -178
  77. package/{cli/src → src/cli}/ui/chat-ui.js +3 -3
  78. package/{cli/src → src/cli}/ui/components/footer-bar.js +1 -1
  79. package/src/{core → common}/constants.js +3 -0
  80. package/src/common/errors.js +402 -0
  81. package/src/{utils → common}/logger.js +33 -0
  82. package/src/{utils/chat-queue.js → common/queue.js} +2 -2
  83. package/src/config/plugin-config.js +50 -0
  84. package/src/context/agent.js +32 -0
  85. package/src/context/compaction-prompts.js +170 -0
  86. package/src/context/compaction-utils.js +191 -0
  87. package/src/context/compressor.js +413 -0
  88. package/src/context/index.js +9 -0
  89. package/src/{core/context-manager.js → context/manager.js} +1 -1
  90. package/src/context/request.js +50 -0
  91. package/src/context/session.js +33 -0
  92. package/src/context/storage.js +30 -0
  93. package/src/executors/mcp-client.js +153 -0
  94. package/src/executors/mcp-desc.js +236 -0
  95. package/src/executors/mcp-executor.js +91 -956
  96. package/src/{core → framework}/command-registry.js +1 -1
  97. package/src/framework/framework.js +300 -0
  98. package/src/framework/index.js +18 -0
  99. package/src/framework/lifecycle.js +203 -0
  100. package/src/framework/loader.js +78 -0
  101. package/src/framework/registry.js +86 -0
  102. package/src/{core/ui-extension-context.js → framework/ui-extension.js} +1 -1
  103. package/src/index.js +130 -15
  104. package/src/llm/index.js +26 -0
  105. package/src/llm/provider.js +212 -0
  106. package/src/llm/registry.js +11 -0
  107. package/src/{core/token-counter.js → llm/tokens.js} +4 -37
  108. package/src/{core/plugin-base.js → plugin/base.js} +10 -136
  109. package/src/plugin/index.js +14 -0
  110. package/src/plugin/loader.js +101 -0
  111. package/src/plugin/manager.js +261 -0
  112. package/src/{core → session}/branch-summary-auto.js +2 -2
  113. package/src/{core/chat-session.js → session/chat.js} +2 -2
  114. package/src/session/index.js +7 -0
  115. package/src/{core/session-manager.js → session/session.js} +2 -2
  116. package/src/session/ttl.js +92 -0
  117. package/src/{core/jsonl-storage.js → storage/jsonl.js} +1 -1
  118. package/src/tool/executor.js +85 -0
  119. package/src/tool/index.js +15 -0
  120. package/src/tool/registry.js +143 -0
  121. package/src/{core/tool-router.js → tool/router.js} +17 -124
  122. package/src/tool/schema.js +108 -0
  123. package/src/utils/data-splitter.js +1 -1
  124. package/src/utils/download.js +1 -1
  125. package/src/utils/index.js +6 -6
  126. package/src/utils/message-validator.js +1 -1
  127. package/tests/core/context-storage.test.js +46 -0
  128. package/tests/core/llm.test.js +54 -0
  129. package/tests/core/plugin.test.js +42 -0
  130. package/tests/core/tool.test.js +60 -0
  131. package/tests/setup.js +10 -0
  132. package/tests/smoke.test.js +58 -0
  133. package/vitest.config.js +9 -0
  134. package/cli/src/daemon.js +0 -149
  135. package/docs/CONTEXT_DESIGN.md +0 -1596
  136. package/docs/ai-sdk-optimization.md +0 -655
  137. package/docs/features.md +0 -120
  138. package/docs/qq-bot.md +0 -976
  139. package/docs/quick-reference.md +0 -160
  140. package/docs/user-manual.md +0 -1391
  141. package/images/geometric_shapes.jpg +0 -0
  142. package/images/sunset_mountain_lake.jpg +0 -0
  143. package/skills/poster-guide/SKILL.md +0 -792
  144. package/src/capabilities/index.js +0 -11
  145. package/src/core/agent.js +0 -808
  146. package/src/core/context-compressor.js +0 -959
  147. package/src/core/enhanced-context-compressor.js +0 -210
  148. package/src/core/framework.js +0 -1422
  149. package/src/core/index.js +0 -30
  150. package/src/core/plugin-manager.js +0 -961
  151. package/src/core/provider-registry.js +0 -159
  152. package/src/core/provider.js +0 -156
  153. package/src/core/request-context.js +0 -98
  154. package/src/core/subagent.js +0 -442
  155. package/src/core/system-prompt-builder.js +0 -120
  156. package/src/core/tool-executor.js +0 -202
  157. package/src/core/tool-registry.js +0 -517
  158. package/src/core/worker-agent.js +0 -192
  159. package/src/executors/executor-base.js +0 -58
  160. package/src/utils/error-boundary.js +0 -363
  161. package/src/utils/error.js +0 -374
  162. package/system.md +0 -1645
  163. package/website_v2/README.md +0 -57
  164. package/website_v2/SPEC.md +0 -1
  165. package/website_v2/docs/api.html +0 -128
  166. package/website_v2/docs/configuration.html +0 -147
  167. package/website_v2/docs/plugin-development.html +0 -129
  168. package/website_v2/docs/project-structure.html +0 -89
  169. package/website_v2/docs/skill-development.html +0 -85
  170. package/website_v2/index.html +0 -489
  171. package/website_v2/scripts/main.js +0 -93
  172. package/website_v2/styles/animations.css +0 -8
  173. package/website_v2/styles/docs.css +0 -83
  174. package/website_v2/styles/main.css +0 -417
  175. package/xhs_auth.json +0 -268
  176. package//346/265/267/346/212/245/346/217/222/344/273/266.md +0 -621
  177. /package/plugins/{ambient-agent → ambient}/constants.js +0 -0
  178. /package/plugins/{email → messaging/email}/constants.js +0 -0
  179. /package/plugins/{email → messaging/email}/handlers.js +0 -0
  180. /package/plugins/{email → messaging/email}/monitor.js +0 -0
  181. /package/plugins/{email → messaging/email}/parser.js +0 -0
  182. /package/plugins/{email → messaging/email}/reply.js +0 -0
  183. /package/plugins/{email → messaging/email}/utils.js +0 -0
  184. /package/{examples → sandbox}/test-chat.js +0 -0
  185. /package/{examples → sandbox}/test-mcp.js +0 -0
  186. /package/{examples → sandbox}/test-reload.js +0 -0
  187. /package/{examples → sandbox}/test-telegram.js +0 -0
  188. /package/{examples → sandbox}/test-tg-bot.js +0 -0
  189. /package/{examples → sandbox}/test-tg-simple.js +0 -0
  190. /package/{examples → sandbox}/test-tg.js +0 -0
  191. /package/{examples → sandbox}/test-think.js +0 -0
  192. /package/src/{core/sub-agent-config.js → agent/sub-config.js} +0 -0
  193. /package/{cli/src → src/cli}/commands/daemon.js +0 -0
  194. /package/{cli/src → src/cli}/commands/list.js +0 -0
  195. /package/{cli/src → src/cli}/commands/plugin.js +0 -0
  196. /package/{cli/src → src/cli}/ui/components/agent-mention-provider.js +0 -0
  197. /package/{cli/src → src/cli}/ui/components/chained-autocomplete-provider.js +0 -0
  198. /package/{cli/src → src/cli}/ui/components/message-bubble.js +0 -0
  199. /package/{cli/src → src/cli}/ui/components/status-bar.js +0 -0
  200. /package/{cli/src → src/cli}/utils/ansi.js +0 -0
  201. /package/{cli/src → src/cli}/utils/config.js +0 -0
  202. /package/{cli/src → src/cli}/utils/markdown.js +0 -0
  203. /package/{cli/src → src/cli}/utils/plugin-config.js +0 -0
  204. /package/{cli/src → src/cli}/utils/render-diff.js +0 -0
  205. /package/src/{utils/circuit-breaker.js → common/circuit.js} +0 -0
  206. /package/src/{utils/edit-diff.js → common/diff.js} +0 -0
  207. /package/src/{utils/event-emitter.js → common/events.js} +0 -0
  208. /package/src/{utils → common}/id.js +0 -0
  209. /package/src/{utils → common}/retry.js +0 -0
  210. /package/src/{core/notification-manager.js → notification/manager.js} +0 -0
  211. /package/src/{core/session-entry.js → session/entry.js} +0 -0
  212. /package/src/{core/storage-manager.js → storage/manager.js} +0 -0
@@ -1,442 +0,0 @@
1
- /**
2
- * Subagent 类
3
- * 轻量级子 Agent,用于完成单次任务
4
- * 不需要 session、上下文压缩等复杂功能
5
- */
6
-
7
- const { EventEmitter } = require('../utils/event-emitter');
8
- const { cleanResponse } = require('../utils');
9
- const { generateText, tool, stepCountIs, isLoopFinished, RetryError, ToolLoopAgent } = require('ai');
10
- const { z } = require('zod');
11
- const { logger } = require('../utils/logger');
12
- const { validateAll } = require('../utils/message-validator');
13
- const { isNetworkError, calculateDelay } = require('../utils/retry');
14
-
15
- class Subagent extends EventEmitter {
16
- /**
17
- * @param {Object} config - 配置
18
- * @param {string} config.name - Subagent 名称
19
- * @param {string} [config.role] - 角色描述
20
- * @param {string} [config.description] - 描述
21
- * @param {string} [config.systemPrompt] - 系统提示词(可选,如果不提供则在 chat 时动态构建)
22
- * @param {string} [config.model] - 模型名称
23
- * @param {string} [config.provider] - 提供者
24
- * @param {string} [config.apiKey] - API 密钥
25
- * @param {string} [config.baseURL] - base URL
26
- * @param {Object} [config.providerOptions] - 提供者选项
27
- * @param {Array|Object} [config.tools] - 工具定义
28
- * @param {Object} [config.framework] - Framework 引用(用于动态构建系统提示词)
29
- */
30
- constructor(config = {}) {
31
- super();
32
-
33
- this.name = config.name || 'Subagent';
34
- this.role = config.role || '助手';
35
- this.description = config.description || '';
36
- this.model = config.model || 'deepseek-chat';
37
- this.provider = config.provider || 'deepseek';
38
- this.apiKey = config.apiKey;
39
- this.baseURL = config.baseURL;
40
- this.providerOptions = config.providerOptions || {};
41
- this.framework = config.framework || null;
42
-
43
- this.defaulTools = ['ext_skill','loadSkill','ext_call','subagent_call','mcp_call','mcp_tool_schema'];
44
-
45
- this.bindTools = {
46
- read: 'read_file',
47
- write: 'write_file',
48
- edit: 'modify_file',
49
- glob: 'read_directory',
50
- grep: 'search_file',
51
- bash: 'bash',
52
- };
53
- // 如果提供了 systemPrompt 则使用,否则标记为需要动态构建
54
- this._customSystemPrompt = config.systemPrompt || null;
55
- this.parentTools = config?.parentTools;
56
- // 工具管理
57
- this.tools=config.tools||{}
58
-
59
- // 重试配置
60
- this.maxRetries = config.maxRetries ?? 2;
61
- this.retryDelay = config.retryDelay ?? 5000;
62
- // 禁用工具
63
- this.disableTools = config.disableTools ?? false;
64
-
65
- // 提示词部分注册(支持插件注册提示词)
66
- this._promptParts = [];
67
-
68
- // 标记为子Agent(用于区分主Agent)
69
- this._isSubagent = true;
70
-
71
- this.providerOptions.deepseek = this.providerOptions.deepseek || {};
72
- this.providerOptions.deepseek.thinking={ type: 'disabled' }
73
-
74
- this.framework.once('framework:ready', () => {
75
- const extExecutor = this.framework?.pluginManager?.get('extension-executor');
76
- extExecutor._refreshAllAgentsExtPrompt(this.framework);
77
- });
78
- }
79
-
80
- /**
81
- * 注册系统提示词部分
82
- * @param {string} name - 部分名称
83
- * @param {number} [priority=100] - 优先级
84
- * @param {Function} provider - 返回提示词内容的函数
85
- */
86
- registerPromptPart(name, priority = 100, provider) {
87
- this._promptParts.push({ name, priority, provider });
88
- // 按优先级排序
89
- this._promptParts.sort((a, b) => a.priority - b.priority);
90
- return this;
91
- }
92
-
93
- /**
94
- * 验证消息配对(工具调用和结果配对)
95
- * @private
96
- */
97
- _validateMessagesPairing(messages) {
98
- return validateAll(messages);
99
- }
100
-
101
- /**
102
- * 验证工具调用
103
- * @private
104
- */
105
- _validateToolCalls(messages) {
106
- // validateAll 内部已包含 validateMessagesPairing + validateToolCalls
107
- // Subagent 的 chat() 中 _validateMessagesPairing 已调用 validateAll,所以这里不再重复
108
- }
109
-
110
- /**
111
- * 注册工具
112
- * @param {Array|Object} tools - 工具定义
113
- */
114
- _registerTools(tools) {
115
- if (Array.isArray(tools)) {
116
- for (const t of tools) {
117
- if (t && t.name) {
118
- this.tools[t.name]=t
119
- }
120
- }
121
- } else if (tools && typeof tools === 'object') {
122
- for (const [name, t] of Object.entries(tools)) {
123
- this.tools[name]=t
124
- }
125
- }
126
- }
127
-
128
- /**
129
- * 添加工具
130
- * @param {Object} toolDef - 工具定义 { name, description, inputSchema, execute }
131
- */
132
- addTool(toolDef) {
133
- this.tools[toolDef.name]=toolDef
134
- return this;
135
- }
136
-
137
- /**
138
- * 获取所有工具
139
- */
140
- getTools() {
141
- return Object.values(this.tools);
142
- }
143
-
144
- /**
145
- * 获取 AI 提供者
146
- */
147
- _getAIProvider() {
148
- const { createAI } = require('./provider');
149
- return createAI({
150
- provider: this.provider,
151
- model: this.model,
152
- apiKey: this.apiKey,
153
- baseURL: this.baseURL,
154
- });
155
- }
156
-
157
- /**
158
- * 构建 AI 工具格式
159
- */
160
- _buildAITools() {
161
- // 禁用工具时返回空
162
- const tools = {};
163
- // 从父Agent继承工具
164
- const all_tools = this.framework.getTools();
165
- let parentTools = [];
166
- if (Array.isArray(this.parentTools)) {
167
- parentTools = this.parentTools.map((key) => {
168
- return this.bindTools[key.toLocaleLowerCase()] || key;
169
- });
170
- for (const toolName of parentTools) {
171
- const toolDef = all_tools.find((t) => t.name === toolName);
172
- if (toolDef) {
173
- tools[toolDef.name] = toolDef;
174
- }
175
- }
176
- const defaulTools = all_tools.filter((a) => this.defaulTools.includes(a.name));
177
-
178
- defaulTools.map((tool) => {
179
- tools[tool.name] = tool;
180
- });
181
- } else {
182
-
183
- all_tools.forEach(item => {
184
- tools[item.name]=item
185
- });
186
- }
187
- return { ...tools, ...this.tools };
188
- }
189
-
190
- /**
191
- * 动态构建系统提示词
192
- * @private
193
- */
194
- _buildSystemPrompt() {
195
- const lines = [];
196
- const roleName = this.role || '助手';
197
- const descText = this.description || '';
198
-
199
- // 1. 角色定义
200
- lines.push(`你是 ${roleName}。`);
201
- if (descText) {
202
- lines.push(descText);
203
- }
204
- lines.push('');
205
- const now = new Date();
206
- const timeStr = now.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
207
- const dateStr = now.toLocaleDateString('zh-CN', {
208
- timeZone: 'Asia/Shanghai',
209
- weekday: 'long',
210
- });
211
- lines.push(`【当前时间】${timeStr}(${dateStr})`)
212
-
213
- if (this._customSystemPrompt) {
214
- lines.push(this._customSystemPrompt);
215
- }
216
-
217
- // 2. 主Agent的系统提示词
218
- const mainAgent = this.framework?.getMainAgent?.();
219
- if (mainAgent) {
220
- const mainPrompt = mainAgent.getOriginalPrompt();
221
- if (mainPrompt) {
222
- lines.push('## 主Agent的系统提示词');
223
- lines.push(mainPrompt);
224
- lines.push('');
225
- }
226
- }
227
-
228
- // // 3. 工具描述
229
- // const tools = this._buildAITools();
230
-
231
- // const toolKeys = Object.keys(tools);
232
- // if (toolKeys.length > 0) {
233
- // lines.push('## 可用工具');
234
- // lines.push('');
235
- // for (const key of toolKeys) {
236
- // const toolDef = tools[key];
237
- // const desc = toolDef.description || '无描述';
238
- // lines.push(`- **${toolDef.name}**: ${desc}`);
239
- // }
240
- // lines.push('');
241
- // }
242
- const subagentManager = this.framework?.pluginManager?.get('subagent-manager');
243
- lines.push(subagentManager._buildDescription());
244
- lines.push('');
245
- // 3.5 扩展插件工具描述
246
- const extExecutor = this.framework?.pluginManager?.get('extension-executor');
247
- const extDesc = extExecutor?._buildExtensionsDescription();
248
- if (extDesc) {
249
- lines.push(extDesc);
250
- lines.push('');
251
- }
252
-
253
- // 5. 注册的提示词部分(来自插件)
254
- if (this._promptParts && this._promptParts.length > 0) {
255
- for (const part of this._promptParts) {
256
- try {
257
- const content = typeof part.provider === 'function' ? part.provider() : '';
258
- if (content && content.trim()) {
259
- lines.push('');
260
- lines.push(content.trim());
261
- }
262
- } catch (err) {
263
- logger.warn(`[Subagent:${this.name}] Failed to get prompt part '${part.name}':`, err.message);
264
- }
265
- }
266
- }
267
-
268
- // 6. SubAgentPlugin 的核心职责和规则
269
- lines.push('');
270
- lines.push(`## 核心职责`);
271
- lines.push(`- **专注本职**:只完成与 ${this.role} 相关的任务,不要处理其他领域的请求`);
272
- lines.push(`- **不越界**:不要代替其他子Agent完成任务,不要主动介入其他专业领域`);
273
- lines.push(`- **返回结果**:返回简洁明确的操作结果,不需要解释过程`);
274
- lines.push('');
275
- lines.push(`当你被调用时,你应该:`);
276
- lines.push(`1. 仔细理解任务要求,确认是否属于你的职责范围`);
277
- lines.push(`2. 如果任务不属于你的职责范围,返回"此任务不在我的职责范围内"`);
278
- lines.push(`3. 使用你的工具集完成任务`);
279
- lines.push(`4. 返回完整的操作结果`);
280
- lines.push('');
281
- lines.push(`## 禁止事项`);
282
- lines.push(`- 处理本职外的任务`);
283
- lines.push(`- 代替其他子Agent执行操作`);
284
- lines.push(`- 在回复中透露你的内部实现细节`);
285
- lines.push(`- 不先调用 ext_skill/loadSkill 就直接使用 ext_call`);
286
-
287
- return lines.join('\n');
288
- }
289
-
290
- /**
291
- * 执行任务
292
- * @param {string|Array} taskOrMessages - 任务描述或消息数组
293
- * @param {Object} [options] - 选项
294
- * @param {number} [options.maxSteps] - 最大步数
295
- * @param {AbortSignal} [options.signal] - 中止信号
296
- * @param {number} [options.maxRetries] - 最大重试次数
297
- * @returns {Promise<{success: boolean, message: string, steps: number}>}
298
- */
299
- async chat(taskOrMessages, options = {}) {
300
- const maxSteps = options?.maxSteps || 30;
301
- const maxRetries = options?.maxRetries ?? this.maxRetries;
302
- const retryDelay = options?.retryDelay ?? this.retryDelay;
303
- const aiProvider = this._getAIProvider();
304
-
305
- // 支持传入消息数组(用于持续对话上下文)或单个任务字符串
306
- let messages;
307
- if (Array.isArray(taskOrMessages)) {
308
- messages = [...taskOrMessages];
309
- } else {
310
- messages = [{ role: 'user', content: taskOrMessages }];
311
- }
312
-
313
- // logger.info(
314
- // `[Subagent:${this.name}] chat called, maxRetries=${maxRetries}, disableTools=${this.disableTools}`
315
- // );
316
-
317
- let lastError;
318
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
319
- // logger.info(
320
- // `[Subagent:${this.name}] attempt ${attempt + 1}, model=${this.model}, provider=${this.provider}`
321
- // );
322
- try {
323
- const tools = this.disableTools ? {} : this._buildAITools();
324
- const systemPrompt = this.disableTools
325
- ? this._customSystemPrompt
326
- : this._buildSystemPrompt();
327
- // console.log(Object.keys(tools))
328
- //await fs.writeFile('subagent.md',systemPrompt)
329
- // 验证消息配对,清理无效的 tool call/result
330
- const validated = this._validateMessagesPairing(messages);
331
- if (validated.length !== messages.length) {
332
- messages.length = 0;
333
- messages.push(...validated);
334
- }
335
- this._validateToolCalls(messages);
336
-
337
- // 使用 ToolLoopAgent 实现自动工具执行循环
338
- const agent = new ToolLoopAgent({
339
- model: aiProvider(this.model),
340
- instructions: systemPrompt,
341
- tools,
342
- stopWhen: stepCountIs(maxSteps),
343
- });
344
-
345
- const result = await agent.generate({
346
- messages,
347
- ...this.providerOptions,
348
- abortSignal: options.signal,
349
- });
350
- // console.log('[子Agent] ' ,result.text)
351
- // ToolLoopAgent 会自动执行工具并收集结果,messages 已经被修改
352
- messages.push(...result.response.messages);
353
- const full_text = cleanResponse(result.text);
354
- this.emit('complete', { message: full_text, steps: result.steps?.length || 0 });
355
- return {
356
- success: true,
357
- message: full_text,
358
- steps: result.steps?.length || 0,
359
- messages, // 返回完整消息历史,用于持续对话上下文
360
- };
361
- } catch (err) {
362
-
363
- lastError = err;
364
- // 统一使用 retry.js 的错误判断
365
- if (isNetworkError(err) && attempt < maxRetries) {
366
- logger.warn(
367
- `[Subagent:${this.name}] AI 服务暂时不可用,正在重试 (${attempt + 1}/${maxRetries})`
368
- );
369
- await new Promise((resolve) => setTimeout(resolve, retryDelay * Math.pow(2, attempt)));
370
- continue;
371
- }
372
- logger.warn(`[Subagent:${this.name}] generateText error: ${err.message}`);
373
- // 最终失败,转换为友好消息
374
- const friendlyMessage = this._getFriendlyError(err);
375
- this.emit('error', { error: friendlyMessage });
376
- return {
377
- success: false,
378
- message: '',
379
- error: friendlyMessage,
380
- steps: 0,
381
- messages, // 即使失败也返回消息历史
382
- };
383
- }
384
- }
385
- }
386
-
387
- /**
388
- * 获取友好错误消息
389
- * @private
390
- */
391
- _getFriendlyError(err) {
392
- const errName = err?.name || '';
393
-
394
- // 先用 isNetworkError 统一判断
395
- if (isNetworkError(err)) {
396
- return 'AI 服务暂时不可用,请稍后重试';
397
- }
398
-
399
- const errorMessages = {
400
- AI_NoContentGeneratedError: 'AI 未生成有效内容,请重试',
401
- AI_NoSuchModelError: '指定的 AI 模型不存在',
402
- AI_NoSuchProviderError: 'AI 提供商配置错误',
403
- AI_LoadAPIKeyError: 'AI API 密钥配置错误',
404
- };
405
- return errorMessages[errName] || err.message?.split('\n')[0] || '未知错误';
406
- }
407
-
408
- /**
409
- * 设置角色(会清除自定义 systemPrompt)
410
- * @param {string} role - 角色描述
411
- */
412
- setRole(role) {
413
- this.role = role;
414
- this._customSystemPrompt = null;
415
- }
416
-
417
- /**
418
- * 设置描述
419
- * @param {string} description - 描述
420
- */
421
- setDescription(description) {
422
- this.description = description;
423
- }
424
-
425
- /**
426
- * 设置系统提示词(会覆盖动态构建)
427
- * @param {string} prompt - 系统提示词
428
- */
429
- setSystemPrompt(prompt) {
430
- this._customSystemPrompt = prompt;
431
- }
432
-
433
- /**
434
- * 销毁
435
- */
436
- destroy() {
437
- this.removeAllListeners();
438
- this.tools={}
439
- }
440
- }
441
-
442
- module.exports = { Subagent };
@@ -1,120 +0,0 @@
1
- /**
2
- * 系统提示词构建器
3
- * 负责从多个来源组合系统提示词各部分
4
- * 支持插件注册自己的提示词部分,支持重载后更新
5
- */
6
-
7
- class SystemPromptPart {
8
- constructor(name, priority, provider) {
9
- this.name = name;
10
- this.priority = priority;
11
- this._provider = provider;
12
- this._dirty = true;
13
- this._cached = null;
14
- }
15
-
16
- get() {
17
- if (this._dirty || this._cached === null) {
18
- try {
19
- this._cached = this._provider();
20
- this._dirty = false;
21
- } catch (e) {
22
- console.warn(`[SystemPromptPart:${this.name}] Provider failed:`, e.message);
23
- this._cached = null;
24
- }
25
- }
26
- return this._cached;
27
- }
28
-
29
- invalidate() {
30
- this._dirty = true;
31
- }
32
- }
33
-
34
- class SystemPromptBuilder {
35
- constructor() {
36
- this._parts = new Map(); // name -> SystemPromptPart
37
- this._orderedParts = []; // sorted by priority
38
- }
39
-
40
- /**
41
- * 注册一个提示词部分
42
- * @param {string} name - 部分名称(唯一标识)
43
- * @param {number} priority - 优先级(数字越小越靠前)
44
- * @param {Function} provider - 返回提示词内容的函数
45
- */
46
- register(name, priority, provider) {
47
- const part = new SystemPromptPart(name, priority, provider);
48
- this._parts.set(name, part);
49
- this._reorder();
50
- return this;
51
- }
52
-
53
- /**
54
- * 注销一个提示词部分
55
- */
56
- unregister(name) {
57
- this._parts.delete(name);
58
- this._reorder();
59
- return this;
60
- }
61
-
62
- /**
63
- * 使某个部分失效(下次 get 时会重新计算)
64
- */
65
- invalidate(name) {
66
- const part = this._parts.get(name);
67
- if (part) {
68
- part.invalidate();
69
- }
70
- return this;
71
- }
72
-
73
- /**
74
- * 使所有部分失效
75
- */
76
- invalidateAll() {
77
- for (const part of this._parts.values()) {
78
- part.invalidate();
79
- }
80
- return this;
81
- }
82
-
83
- /**
84
- * 获取某个部分的内容
85
- */
86
- getPart(name) {
87
- const part = this._parts.get(name);
88
- return part ? part.get() : null;
89
- }
90
-
91
- /**
92
- * 构建完整的系统提示词
93
- * @returns {string}
94
- */
95
- build() {
96
- const parts = [];
97
- for (const part of this._orderedParts) {
98
- const content = part.get();
99
- if (content && content.trim()) {
100
- parts.push(content.trim());
101
- }
102
- }
103
- return parts.join('\n\n');
104
- }
105
-
106
- /**
107
- * 获取所有部分名称
108
- */
109
- getPartNames() {
110
- return [...this._parts.keys()];
111
- }
112
-
113
- _reorder() {
114
- this._orderedParts = Array.from(this._parts.values()).sort(
115
- (a, b) => a.priority - b.priority
116
- );
117
- }
118
- }
119
-
120
- module.exports = { SystemPromptBuilder, SystemPromptPart };