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
@@ -0,0 +1,337 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * MainAgent - 主对话 Agent
5
+ * 完整的对话 Agent,支持 session、工具注册、上下文压缩
6
+ */
7
+
8
+ const { BaseAgent } = require('./base');
9
+ const { Logger, LOG_LEVELS } = require('../common/logger');
10
+ const { PROMPT_PRIORITY, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_TEMPERATURE } = require('../common/constants');
11
+ const { SystemPromptBuilder } = require('./prompt');
12
+ const os = require('os');
13
+
14
+ const _agentLogger = new Logger({ namespace: 'agent', level: LOG_LEVELS.INFO });
15
+ const logger = {
16
+ info: (ctx, msg, data) => _agentLogger.info(`[${ctx}] ${msg}`, data !== undefined ? data : ''),
17
+ warn: (ctx, msg, data) => _agentLogger.warn(`[${ctx}] ${msg}`, data !== undefined ? data : ''),
18
+ error: (ctx, msg, data) => _agentLogger.error(`[${ctx}] ${msg}`, data !== undefined ? data : ''),
19
+ debug: (ctx, msg, data) => _agentLogger.debug(`[${ctx}] ${msg}`, data !== undefined ? data : ''),
20
+ };
21
+
22
+ class MainAgent extends BaseAgent {
23
+ constructor(framework, config = {}) {
24
+ super(framework, config);
25
+
26
+ this.name = config.name || 'Agent';
27
+ this.model = config.model || 'deepseek-chat';
28
+ this.apiKey = config.apiKey;
29
+ this.baseURL = config.baseURL;
30
+ this.provider = config.provider || 'deepseek';
31
+ this.providerOptions = {
32
+ ...config.providerOptions,
33
+ maxOutputTokens: config.providerOptions?.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS,
34
+ temperature: config.providerOptions?.temperature ?? DEFAULT_TEMPERATURE,
35
+ };
36
+
37
+ this._originalPrompt = config.systemPrompt || '你是一个智能助手。当用户提出问题或任务时,你会主动分析需求,选择合适的工具来获取信息或执行操作。你善于将复杂任务拆解为多个步骤,通过工具协作完成。';
38
+ this._sharedPrompt = config.sharedPrompt || '';
39
+ const metadata = config.metadata || {};
40
+ this._metadata = new Map(Object.entries(metadata));
41
+
42
+ this._chatHandler = null;
43
+ this._tools = new Map();
44
+ this._skipSyncTools = config.skipSyncTools || false;
45
+ this._subAgents = new Map();
46
+ this._systemPromptBuilder = new SystemPromptBuilder();
47
+ this._registerDefaultPromptParts();
48
+ this.systemPrompt = this._buildSystemPrompt();
49
+
50
+ // Initialize chat handler
51
+ const { NotificationManager } = require('../notification/manager');
52
+ this._notificationManager = new NotificationManager(framework);
53
+ this._initChatHandler();
54
+ }
55
+
56
+ _registerDefaultPromptParts() {
57
+ this._systemPromptBuilder.register('original-prompt', PROMPT_PRIORITY.ORIGINAL_PROMPT, () => this._originalPrompt);
58
+ this._systemPromptBuilder.register('shared-prompt', PROMPT_PRIORITY.SHARED_PROMPT, () => {
59
+ return this._sharedPrompt ? this._replacePlaceholders(this._sharedPrompt) : null;
60
+ });
61
+ this._systemPromptBuilder.register('datetime', PROMPT_PRIORITY.DATETIME, () => {
62
+ const now = new Date();
63
+ const timeStr = now.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
64
+ const dateStr = now.toLocaleDateString('zh-CN', { timeZone: 'Asia/Shanghai', weekday: 'long' });
65
+ return `【当前时间】${timeStr}(${dateStr})`;
66
+ });
67
+ this._systemPromptBuilder.register('metadata', PROMPT_PRIORITY.METADATA, () => {
68
+ if (this._metadata.size === 0) return null;
69
+ const metaParts = ['【元数据】'];
70
+ for (const [key, value] of this._metadata) {
71
+ metaParts.push(`- ${key}: ${typeof value === 'object' ? JSON.stringify(value) : value}`);
72
+ }
73
+ return metaParts.join('\n');
74
+ });
75
+ this._systemPromptBuilder.register('capabilities', PROMPT_PRIORITY.CAPABILITIES, () => this._buildCapabilitiesDescription());
76
+ }
77
+
78
+ _getMetadataValue(key) {
79
+ if (this._metadata.has(key)) return this._metadata.get(key);
80
+ switch (key) {
81
+ case 'WORK_DIR': case 'CWD': return this.framework?.getCwd?.() ?? process.cwd();
82
+ case 'HOME_DIR': return os.homedir();
83
+ case 'HOST_NAME': return os.hostname();
84
+ case 'PLATFORM': return process.platform;
85
+ case 'TIME': return new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
86
+ case 'DATE': return new Date().toISOString().split('T')[0];
87
+ default: return `{{${key}}}`;
88
+ }
89
+ }
90
+
91
+ _replacePlaceholders(template) {
92
+ return template.replace(/\{\{(\w+)\}\}/g, (match, key) => this._getMetadataValue(key));
93
+ }
94
+
95
+ _buildCapabilitiesDescription() {
96
+ const plugins = this.framework.pluginManager.getAll();
97
+ if (!plugins || plugins.length === 0) return '';
98
+ const keyPlugins = plugins.filter(p => p.name !== 'defaults' && p.name !== 'agent');
99
+ if (keyPlugins.length === 0) return '';
100
+ const lines = ['## 系统能力', ''];
101
+ keyPlugins.forEach((plugin, index) => {
102
+ const name = plugin.instance?.name || plugin.name || 'unknown';
103
+ const description = plugin.instance?.description || '无描述';
104
+ lines.push(`${index + 1}. **${name}**:${description}`);
105
+ });
106
+ return lines.join('\n');
107
+ }
108
+
109
+ _buildSystemPrompt() {
110
+ if (!this._systemPromptDirty && this._systemPromptCache !== null) return this._systemPromptCache;
111
+ this._systemPromptCache = this._systemPromptBuilder.build();
112
+ this._systemPromptDirty = false;
113
+ return this._systemPromptCache;
114
+ }
115
+
116
+ _invalidateSystemPromptCache() {
117
+ this._systemPromptDirty = true;
118
+ this._systemPromptBuilder.invalidateAll();
119
+ }
120
+
121
+ _refreshContext() {
122
+ this.systemPrompt = this._buildSystemPrompt();
123
+ if (this._chatHandler) this._chatHandler.setSystemPrompt(this.systemPrompt);
124
+ }
125
+
126
+ _initChatHandler() {
127
+ const { AgentChatHandler } = require('./chat');
128
+ let aiClient = null;
129
+ const aiPlugin = this.framework.pluginManager?.get('ai');
130
+ if (aiPlugin) aiClient = aiPlugin.getAIClient();
131
+
132
+ this._chatHandler = new AgentChatHandler(this, {
133
+ model: this.model, provider: this.provider, apiKey: this.apiKey,
134
+ baseURL: this.baseURL, providerOptions: this.providerOptions,
135
+ maxContextTokens: this.config.maxContextTokens,
136
+ compressionThreshold: this.config.compressionThreshold,
137
+ compressionMessageThreshold: this.config.compressionMessageThreshold,
138
+ keepRecentMessages: this.config.keepRecentMessages,
139
+ enableSmartCompress: this.config.enableSmartCompress,
140
+ });
141
+ if (aiClient) this._chatHandler.setAIClient(aiClient);
142
+
143
+ this._chatHandler.on('message', (msg) => this.emit('message', msg));
144
+ this._chatHandler.on('chunk', (chunk) => this.emit('chunk', chunk));
145
+ this._chatHandler.on('tool-call', (tool) => this.emit('tool-call', tool));
146
+ this._chatHandler.on('tool-result', (result) => this.emit('tool-result', result));
147
+ this._chatHandler.on('error', (err) => this.emit('error', err));
148
+ this._syncTools();
149
+ }
150
+
151
+ getOriginalPrompt() { return this._originalPrompt; }
152
+
153
+ setSystemPrompt(prompt) {
154
+ this._originalPrompt = prompt;
155
+ this.systemPrompt = this._buildSystemPrompt();
156
+ if (this._chatHandler) this._chatHandler.setSystemPrompt(this.systemPrompt);
157
+ return this;
158
+ }
159
+
160
+ registerPromptPart(name, priority, provider) {
161
+ this._systemPromptBuilder.register(name, priority, provider);
162
+ this._invalidateSystemPromptCache();
163
+ this._refreshContext();
164
+ return this;
165
+ }
166
+
167
+ unregisterPromptPart(name) {
168
+ this._systemPromptBuilder.unregister(name);
169
+ this._invalidateSystemPromptCache();
170
+ this._refreshContext();
171
+ return this;
172
+ }
173
+
174
+ registerTool(tool) {
175
+ this.framework.registerTool(tool);
176
+ if (this._chatHandler) this._chatHandler.registerTool(tool);
177
+ this._invalidateSystemPromptCache();
178
+ this._refreshContext();
179
+ return this;
180
+ }
181
+
182
+ getTools() { return this.framework.getTools(); }
183
+
184
+ _syncTools() {
185
+ const tools = this.framework.getTools();
186
+ for (const tool of tools) {
187
+ if (this._chatHandler) this._chatHandler.registerTool(tool);
188
+ }
189
+ }
190
+
191
+ registerSubAgent(name, agent, role, goal) {
192
+ this._subAgents.set(name, { agent, role, goal });
193
+ this._invalidateSystemPromptCache();
194
+ this._refreshContext();
195
+ return this;
196
+ }
197
+
198
+ unregisterSubAgent(name) {
199
+ this._subAgents.delete(name);
200
+ this._invalidateSystemPromptCache();
201
+ this._refreshContext();
202
+ return this;
203
+ }
204
+
205
+ getSubAgents() { return this._subAgents; }
206
+
207
+ async chat(message, options = {}) {
208
+ if (this._status === 'error') this._status = 'idle';
209
+ this._status = 'busy';
210
+ this.emit('status', { status: 'busy' });
211
+ try {
212
+ const enhancedMessage = this._notificationManager.enhanceMessage(message);
213
+ if (!this._skipSyncTools) this._syncTools();
214
+ const result = await this._chatHandler.chat(enhancedMessage, options);
215
+ this._status = 'idle';
216
+ this.emit('status', { status: 'idle' });
217
+ return result;
218
+ } catch (err) {
219
+ this._status = 'error';
220
+ logger.error('Agent', `chat failed: ${err.message}`, { stack: err.stack });
221
+ this.emit('status', { status: 'error', error: err.message });
222
+ throw err;
223
+ }
224
+ }
225
+
226
+ async pushMessage(message, options = {}) {
227
+ const ctx = this.framework.getExecutionContext();
228
+ if (ctx?.sessionId && !options.sessionId) options.sessionId = ctx.sessionId;
229
+ return this.chat(message, options);
230
+ }
231
+
232
+ async *chatStream(message, options = {}) {
233
+ if (this._status === 'busy') throw new Error('Agent is busy');
234
+ if (this._status === 'error') this._status = 'idle';
235
+ this._status = 'busy';
236
+ this.emit('status', { status: 'busy' });
237
+ try {
238
+ const enhancedMessage = this._notificationManager.enhanceMessage(message);
239
+ if (!this._skipSyncTools) this._syncTools();
240
+ yield* this._chatHandler.chatStream(enhancedMessage, options);
241
+ this._status = 'idle';
242
+ this.emit('status', { status: 'idle' });
243
+ } catch (err) {
244
+ this._status = 'error';
245
+ logger.error('Agent', `chatStream failed: ${err.message}`, { stack: err.stack });
246
+ this.emit('status', { status: 'error', error: err.message });
247
+ throw err;
248
+ }
249
+ }
250
+
251
+ createSessionScope(sessionId) { return this._chatHandler.createSessionScope(sessionId); }
252
+ async sendMessage(message, options = {}) { return this._chatHandler.sendMessage(message, options); }
253
+ async *sendMessageStream(message, options = {}) { yield* this._chatHandler.sendMessageStream(message, options); }
254
+ async sendBatch(messages, options = {}) { return this._chatHandler.sendBatch(messages, options); }
255
+ cancelSession(sessionId) { return this._chatHandler.cancelSession(sessionId); }
256
+
257
+ setMetadata(keyOrObj, value) {
258
+ if (typeof keyOrObj === 'string') {
259
+ this._metadata.set(keyOrObj, value);
260
+ } else if (keyOrObj && typeof keyOrObj === 'object') {
261
+ for (const [key, val] of Object.entries(keyOrObj)) this._metadata.set(key, val);
262
+ }
263
+ this._invalidateSystemPromptCache();
264
+ this._refreshContext();
265
+ return this;
266
+ }
267
+
268
+ getMetadata(key) { return this._metadata.get(key); }
269
+ deleteMetadata(key) { this._metadata.delete(key); this._refreshContext(); return this; }
270
+ clearMetadata() { this._metadata.clear(); this._refreshContext(); return this; }
271
+
272
+ clearContext(sessionId) {
273
+ if (!sessionId || !this._chatHandler) return;
274
+ this._chatHandler._chatSession?.clearSessionMessages(sessionId, true);
275
+ const sessionCtx = this.framework?.getSessionContext(sessionId);
276
+ if (sessionCtx) {
277
+ sessionCtx.clearMessages();
278
+ if (sessionCtx.compressionState) sessionCtx.compressionState.count = 0;
279
+ if (sessionCtx.metadata) sessionCtx.metadata.compressionCount = 0;
280
+ }
281
+ const sessionManager = this.framework?._sessionContexts?.get(sessionId);
282
+ if (sessionManager) {
283
+ const { generateEntryId, EntryTypes } = require('../session/entry');
284
+ sessionManager._appendEntry({
285
+ id: generateEntryId(sessionManager.byId), type: EntryTypes.SESSION_INFO,
286
+ info: 'context_cleared', timestamp: Date.now(),
287
+ });
288
+ }
289
+ }
290
+
291
+ async compressContext(sessionId) {
292
+ if (!sessionId || !this._chatHandler) throw new Error('AgentChatHandler not available');
293
+ const chatHandler = this._chatHandler;
294
+ const messageStore = chatHandler._chatSession?.getSessionMessageStore(sessionId);
295
+ if (!messageStore) throw new Error('MessageStore not available');
296
+ messageStore.historyLoaded = false;
297
+ chatHandler._chatSession?.loadHistory(sessionId);
298
+ let messages = messageStore.messages;
299
+ const sessionCtx = this.framework?.getSessionContext(sessionId);
300
+ if (!messages?.length && sessionCtx) {
301
+ messages = sessionCtx.getMessages();
302
+ messageStore.messages = messages;
303
+ }
304
+ if (!messages?.length) throw new Error('No messages to compress');
305
+ if (!chatHandler._contextCompressor) throw new Error('ContextCompressor not available');
306
+ const before = messages.length;
307
+ await chatHandler._contextCompressor.compress(sessionId, messages, messageStore);
308
+ const after = messages.length;
309
+ if (sessionCtx) sessionCtx.replaceMessages(messages);
310
+ const sessionManager = this.framework?._sessionContexts?.get(sessionId);
311
+ if (sessionManager) {
312
+ const { generateEntryId, EntryTypes } = require('../session/entry');
313
+ sessionManager._appendEntry({
314
+ id: generateEntryId(sessionManager.byId), type: EntryTypes.COMPACTION,
315
+ summary: messages.find(m => m.role === 'assistant' && m.content?.startsWith('['))?.content || '',
316
+ firstKeptEntryId: null, tokensBefore: before, timestamp: Date.now(),
317
+ });
318
+ }
319
+ return { before, after, keepRecent: chatHandler._contextCompressor._keepRecentMessages };
320
+ }
321
+
322
+ getCurrentAgentId() { return this.name; }
323
+
324
+ destroy() {
325
+ for (const [name, { agent }] of this._subAgents) {
326
+ if (typeof agent.destroy === 'function') agent.destroy();
327
+ }
328
+ this._subAgents.clear();
329
+ if (this._chatHandler) this._chatHandler.destroy();
330
+ this._tools.clear();
331
+ this.removeAllListeners();
332
+ this.emit('destroyed');
333
+ logger.info('Agent', `Agent "${this.name}" destroyed`);
334
+ }
335
+ }
336
+
337
+ module.exports = { MainAgent };
@@ -0,0 +1,78 @@
1
+ 'use strict';
2
+
3
+ class SystemPromptPart {
4
+ constructor(name, priority, provider) {
5
+ this.name = name;
6
+ this.priority = priority;
7
+ this._provider = provider;
8
+ this._dirty = true;
9
+ this._cached = null;
10
+ }
11
+
12
+ get() {
13
+ if (this._dirty || this._cached === null) {
14
+ try {
15
+ this._cached = this._provider();
16
+ this._dirty = false;
17
+ } catch (e) {
18
+ console.warn(`[SystemPromptPart:${this.name}] Provider failed:`, e.message);
19
+ this._cached = null;
20
+ }
21
+ }
22
+ return this._cached;
23
+ }
24
+
25
+ invalidate() { this._dirty = true; }
26
+ }
27
+
28
+ class SystemPromptBuilder {
29
+ constructor() {
30
+ this._parts = new Map();
31
+ this._orderedParts = [];
32
+ }
33
+
34
+ register(name, priority, provider) {
35
+ this._parts.set(name, new SystemPromptPart(name, priority, provider));
36
+ this._reorder();
37
+ return this;
38
+ }
39
+
40
+ unregister(name) {
41
+ this._parts.delete(name);
42
+ this._reorder();
43
+ return this;
44
+ }
45
+
46
+ invalidate(name) {
47
+ const part = this._parts.get(name);
48
+ if (part) part.invalidate();
49
+ return this;
50
+ }
51
+
52
+ invalidateAll() {
53
+ for (const part of this._parts.values()) part.invalidate();
54
+ return this;
55
+ }
56
+
57
+ getPart(name) {
58
+ const part = this._parts.get(name);
59
+ return part ? part.get() : null;
60
+ }
61
+
62
+ build() {
63
+ const parts = [];
64
+ for (const part of this._orderedParts) {
65
+ const content = part.get();
66
+ if (content && content.trim()) parts.push(content.trim());
67
+ }
68
+ return parts.join('\n\n');
69
+ }
70
+
71
+ getPartNames() { return [...this._parts.keys()]; }
72
+
73
+ _reorder() {
74
+ this._orderedParts = Array.from(this._parts.values()).sort((a, b) => a.priority - b.priority);
75
+ }
76
+ }
77
+
78
+ module.exports = { SystemPromptBuilder, SystemPromptPart };
@@ -0,0 +1,198 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * SubAgent - 轻量子 Agent
5
+ * 继承 BaseAgent,使用 AI SDK 直接执行单次任务
6
+ * 不需要 session、上下文压缩等复杂功能
7
+ */
8
+
9
+ const { BaseAgent } = require('./base');
10
+ const { cleanResponse } = require('../utils');
11
+ const { generateText, tool, stepCountIs, ToolLoopAgent } = require('ai');
12
+ const { z } = require('zod');
13
+ const { logger } = require('../common/logger');
14
+ const { validateAll } = require('../utils/message-validator');
15
+ const { isNetworkError, calculateDelay } = require('../common/retry');
16
+
17
+ class SubAgent extends BaseAgent {
18
+ constructor(config = {}) {
19
+ super(config.framework || null, config);
20
+
21
+ this.name = config.name || 'SubAgent';
22
+ this.role = config.role || '助手';
23
+ this.description = config.description || '';
24
+ this.model = config.model || 'deepseek-chat';
25
+ this.provider = config.provider || 'deepseek';
26
+ this.apiKey = config.apiKey;
27
+ this.baseURL = config.baseURL;
28
+ this.providerOptions = config.providerOptions || {};
29
+
30
+ this.defaulTools = ['ext_skill', 'loadSkill', 'ext_call', 'subagent_call', 'mcp_call', 'mcp_tool_schema'];
31
+ this.bindTools = {
32
+ read: 'read_file', write: 'write_file', edit: 'modify_file',
33
+ glob: 'read_directory', grep: 'search_file', bash: 'bash',
34
+ };
35
+
36
+ this._customSystemPrompt = config.systemPrompt || null;
37
+ this.parentTools = config.parentTools;
38
+ this.tools = config.tools || {};
39
+ this.maxRetries = config.maxRetries ?? 2;
40
+ this.retryDelay = config.retryDelay ?? 5000;
41
+ this.disableTools = config.disableTools ?? false;
42
+ this._promptParts = [];
43
+ this._isSubagent = true;
44
+
45
+ this.providerOptions.deepseek = this.providerOptions.deepseek || {};
46
+ this.providerOptions.deepseek.thinking = { type: 'disabled' };
47
+
48
+ if (this.framework) {
49
+ this.framework.once('framework:ready', () => {
50
+ const extExecutor = this.framework?.pluginManager?.get('extension-executor');
51
+ extExecutor?._refreshAllAgentsExtPrompt?.(this.framework);
52
+ });
53
+ }
54
+ }
55
+
56
+ registerPromptPart(name, priority = 100, provider) {
57
+ this._promptParts.push({ name, priority, provider });
58
+ this._promptParts.sort((a, b) => a.priority - b.priority);
59
+ return this;
60
+ }
61
+
62
+ _validateMessagesPairing(messages) { return validateAll(messages); }
63
+
64
+ _registerTools(tools) {
65
+ if (Array.isArray(tools)) {
66
+ for (const t of tools) { if (t?.name) this.tools[t.name] = t; }
67
+ } else if (tools && typeof tools === 'object') {
68
+ for (const [name, t] of Object.entries(tools)) { this.tools[name] = t; }
69
+ }
70
+ }
71
+
72
+ addTool(toolDef) { this.tools[toolDef.name] = toolDef; return this; }
73
+ getTools() { return Object.values(this.tools); }
74
+
75
+ _getAIProvider() {
76
+ const { createAI } = require('../llm/provider');
77
+ return createAI({ provider: this.provider, model: this.model, apiKey: this.apiKey, baseURL: this.baseURL });
78
+ }
79
+
80
+ _buildAITools() {
81
+ const tools = {};
82
+ const all_tools = this.framework?.getTools() || [];
83
+ let parentTools = [];
84
+ if (Array.isArray(this.parentTools)) {
85
+ parentTools = this.parentTools.map(key => this.bindTools[key.toLocaleLowerCase()] || key);
86
+ for (const toolName of parentTools) {
87
+ const toolDef = all_tools.find(t => t.name === toolName);
88
+ if (toolDef) tools[toolDef.name] = toolDef;
89
+ }
90
+ for (const tool of all_tools.filter(a => this.defaulTools.includes(a.name))) {
91
+ tools[tool.name] = tool;
92
+ }
93
+ } else {
94
+ all_tools.forEach(item => { tools[item.name] = item; });
95
+ }
96
+ return { ...tools, ...this.tools };
97
+ }
98
+
99
+ _buildSystemPrompt() {
100
+ const lines = [];
101
+ const roleName = this.role || '助手';
102
+ const descText = this.description || '';
103
+ lines.push(`你是 ${roleName}。`);
104
+ if (descText) lines.push(descText);
105
+ lines.push('');
106
+ const now = new Date();
107
+ lines.push(`【当前时间】${now.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })}(${now.toLocaleDateString('zh-CN', { timeZone: 'Asia/Shanghai', weekday: 'long' })})`);
108
+ if (this._customSystemPrompt) lines.push(this._customSystemPrompt);
109
+ const mainAgent = this.framework?.getMainAgent?.();
110
+ if (mainAgent) {
111
+ const mainPrompt = mainAgent.getOriginalPrompt();
112
+ if (mainPrompt) { lines.push('## 主Agent的系统提示词'); lines.push(mainPrompt); lines.push(''); }
113
+ }
114
+ const subagentManager = this.framework?.pluginManager?.get('subagent-manager');
115
+ if (subagentManager?._buildDescription) lines.push(subagentManager._buildDescription());
116
+ lines.push('');
117
+ const extExecutor = this.framework?.pluginManager?.get('extension-executor');
118
+ const extDesc = extExecutor?._buildExtensionsDescription?.();
119
+ if (extDesc) { lines.push(extDesc); lines.push(''); }
120
+ for (const part of this._promptParts) {
121
+ try {
122
+ const content = typeof part.provider === 'function' ? part.provider() : '';
123
+ if (content?.trim()) lines.push('', content.trim());
124
+ } catch (err) {
125
+ logger.warn(`[SubAgent:${this.name}] Prompt part '${part.name}' failed:`, err.message);
126
+ }
127
+ }
128
+ lines.push('', '## 核心职责', `- **专注本职**:只完成与 ${this.role} 相关的任务`, '- **不越界**:不要代替其他子Agent完成任务', '- **返回结果**:返回简洁明确的操作结果', '');
129
+ lines.push('当你被调用时,你应该:', '1. 仔细理解任务要求', '2. 如果任务不在职责范围内,返回"此任务不在我的职责范围内"', '3. 使用你的工具集完成任务', '4. 返回完整结果');
130
+ lines.push('', '## 禁止事项', '- 处理本职外的任务', '- 代替其他子Agent执行操作', '- 在回复中透露内部实现细节', '- 不先调用 ext_skill/loadSkill 就直接使用 ext_call');
131
+ return lines.join('\n');
132
+ }
133
+
134
+ async chat(taskOrMessages, options = {}) {
135
+ const maxSteps = options?.maxSteps || 30;
136
+ const maxRetries = options?.maxRetries ?? this.maxRetries;
137
+ const retryDelay = options?.retryDelay ?? this.retryDelay;
138
+ const aiProvider = this._getAIProvider();
139
+
140
+ let messages = Array.isArray(taskOrMessages) ? [...taskOrMessages] : [{ role: 'user', content: taskOrMessages }];
141
+
142
+ let lastError;
143
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
144
+ try {
145
+ const tools = this.disableTools ? {} : this._buildAITools();
146
+ const systemPrompt = this.disableTools ? this._customSystemPrompt : this._buildSystemPrompt();
147
+ const validated = this._validateMessagesPairing(messages);
148
+ if (validated.length !== messages.length) { messages.length = 0; messages.push(...validated); }
149
+
150
+ const agent = new ToolLoopAgent({
151
+ model: aiProvider(this.model),
152
+ instructions: systemPrompt,
153
+ tools,
154
+ stopWhen: stepCountIs(maxSteps),
155
+ });
156
+
157
+ const result = await agent.generate({ messages, ...this.providerOptions, abortSignal: options.signal });
158
+ messages.push(...result.response.messages);
159
+ const full_text = cleanResponse(result.text);
160
+ this.emit('complete', { message: full_text, steps: result.steps?.length || 0 });
161
+ return { success: true, message: full_text, steps: result.steps?.length || 0, messages };
162
+ } catch (err) {
163
+ lastError = err;
164
+ if (isNetworkError(err) && attempt < maxRetries) {
165
+ logger.warn(`[SubAgent:${this.name}] AI unavailable, retrying (${attempt + 1}/${maxRetries})`);
166
+ await new Promise(resolve => setTimeout(resolve, retryDelay * Math.pow(2, attempt)));
167
+ continue;
168
+ }
169
+ logger.warn(`[SubAgent:${this.name}] chat error: ${err.message}`);
170
+ const friendlyMessage = this._getFriendlyError(err);
171
+ this.emit('error', { error: friendlyMessage });
172
+ return { success: false, message: '', error: friendlyMessage, steps: 0, messages };
173
+ }
174
+ }
175
+ }
176
+
177
+ _getFriendlyError(err) {
178
+ if (isNetworkError(err)) return 'AI 服务暂时不可用,请稍后重试';
179
+ const errorMessages = {
180
+ AI_NoContentGeneratedError: 'AI 未生成有效内容',
181
+ AI_NoSuchModelError: '指定的 AI 模型不存在',
182
+ AI_NoSuchProviderError: 'AI 提供商配置错误',
183
+ AI_LoadAPIKeyError: 'AI API 密钥配置错误',
184
+ };
185
+ return errorMessages[err?.name || ''] || err?.message?.split('\n')[0] || '未知错误';
186
+ }
187
+
188
+ setRole(role) { this.role = role; this._customSystemPrompt = null; }
189
+ setDescription(description) { this.description = description; }
190
+ setSystemPrompt(prompt) { this._customSystemPrompt = prompt; }
191
+
192
+ destroy() {
193
+ this.removeAllListeners();
194
+ this.tools = {};
195
+ }
196
+ }
197
+
198
+ module.exports = { SubAgent };