foliko 1.0.75 → 1.0.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +159 -157
- package/cli/bin/foliko.js +12 -12
- package/cli/src/commands/chat.js +143 -143
- package/cli/src/commands/list.js +93 -93
- package/cli/src/index.js +75 -75
- package/cli/src/ui/chat-ui.js +201 -201
- package/cli/src/utils/ansi.js +40 -40
- package/cli/src/utils/markdown.js +292 -292
- package/examples/ambient-example.js +194 -194
- package/examples/basic.js +115 -115
- package/examples/bootstrap.js +121 -121
- package/examples/mcp-example.js +56 -56
- package/examples/skill-example.js +49 -49
- package/examples/test-chat.js +137 -137
- package/examples/test-mcp.js +85 -85
- package/examples/test-reload.js +59 -59
- package/examples/test-telegram.js +50 -50
- package/examples/test-tg-bot.js +45 -45
- package/examples/test-tg-simple.js +47 -47
- package/examples/test-tg.js +62 -62
- package/examples/test-think.js +43 -43
- package/examples/test-web-plugin.js +103 -103
- package/examples/test-weixin-feishu.js +103 -103
- package/examples/workflow.js +158 -158
- package/package.json +1 -1
- package/plugins/ai-plugin.js +102 -102
- package/plugins/ambient-agent/EventWatcher.js +113 -113
- package/plugins/ambient-agent/ExplorerLoop.js +640 -640
- package/plugins/ambient-agent/GoalManager.js +197 -197
- package/plugins/ambient-agent/Reflector.js +95 -95
- package/plugins/ambient-agent/StateStore.js +90 -90
- package/plugins/ambient-agent/constants.js +101 -101
- package/plugins/ambient-agent/index.js +579 -579
- package/plugins/audit-plugin.js +187 -187
- package/plugins/default-plugins.js +662 -662
- package/plugins/email/constants.js +64 -64
- package/plugins/email/handlers.js +461 -461
- package/plugins/email/index.js +278 -278
- package/plugins/email/monitor.js +269 -269
- package/plugins/email/parser.js +138 -138
- package/plugins/email/reply.js +151 -151
- package/plugins/email/utils.js +124 -124
- package/plugins/feishu-plugin.js +481 -481
- package/plugins/file-system-plugin.js +826 -826
- package/plugins/install-plugin.js +199 -199
- package/plugins/python-executor-plugin.js +367 -367
- package/plugins/python-plugin-loader.js +481 -481
- package/plugins/rules-plugin.js +294 -294
- package/plugins/scheduler-plugin.js +691 -691
- package/plugins/session-plugin.js +369 -369
- package/plugins/shell-executor-plugin.js +197 -197
- package/plugins/storage-plugin.js +240 -240
- package/plugins/subagent-plugin.js +845 -845
- package/plugins/telegram-plugin.js +482 -482
- package/plugins/think-plugin.js +345 -345
- package/plugins/tools-plugin.js +196 -196
- package/plugins/web-plugin.js +606 -606
- package/plugins/weixin-plugin.js +545 -545
- package/src/capabilities/index.js +11 -11
- package/src/capabilities/skill-manager.js +609 -609
- package/src/capabilities/workflow-engine.js +1109 -1109
- package/src/core/agent-chat.js +882 -882
- package/src/core/agent.js +892 -892
- package/src/core/framework.js +465 -465
- package/src/core/index.js +19 -19
- package/src/core/plugin-base.js +219 -219
- package/src/core/plugin-manager.js +863 -863
- package/src/core/provider.js +114 -114
- package/src/core/sub-agent-config.js +264 -264
- package/src/core/system-prompt-builder.js +120 -120
- package/src/core/tool-registry.js +517 -517
- package/src/core/tool-router.js +297 -297
- package/src/executors/executor-base.js +58 -58
- package/src/executors/mcp-executor.js +741 -741
- package/src/index.js +25 -25
- package/src/utils/circuit-breaker.js +301 -301
- package/src/utils/error-boundary.js +363 -363
- package/src/utils/error.js +374 -374
- package/src/utils/event-emitter.js +97 -97
- package/src/utils/id.js +133 -133
- package/src/utils/index.js +217 -217
- package/src/utils/logger.js +181 -181
- package/src/utils/plugin-helpers.js +90 -90
- package/src/utils/retry.js +122 -122
- package/src/utils/sandbox.js +292 -292
- package/test/tool-registry-validation.test.js +218 -218
- package/website/script.js +136 -136
- package/foliko-1.0.75.tgz +0 -0
|
@@ -1,846 +1,846 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SubAgent 子Agent插件
|
|
3
|
-
* 创建具有独立工具集的子Agent,用于分工处理不同领域的任务
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs')
|
|
7
|
-
const path = require('path')
|
|
8
|
-
const { Plugin } = require('../src/core/plugin-base')
|
|
9
|
-
const { logger } = require('../src/utils/logger')
|
|
10
|
-
const log = logger.child('SubAgent')
|
|
11
|
-
const { z } = require('zod')
|
|
12
|
-
const { Agent } = require('../src/core/agent')
|
|
13
|
-
|
|
14
|
-
class SubAgentPlugin extends Plugin {
|
|
15
|
-
constructor(config = {}) {
|
|
16
|
-
super()
|
|
17
|
-
this.name = config.name
|
|
18
|
-
this.version = '1.0.0'
|
|
19
|
-
this.description = config.description || `子Agent: ${config.name}`
|
|
20
|
-
this.priority = 10
|
|
21
|
-
|
|
22
|
-
this.system = true
|
|
23
|
-
|
|
24
|
-
this.role= config.role || config.name
|
|
25
|
-
this.tools = config.tools || {}
|
|
26
|
-
this.parentTools = config.parentTools || []
|
|
27
|
-
this.llmConfig = config.llmConfig || null
|
|
28
|
-
|
|
29
|
-
// 子Agent配置
|
|
30
|
-
// tools: { toolName: toolFn } 自定义工具(只属于此子Agent)
|
|
31
|
-
// parentTools: ['read_file'] 从父Agent继承的工具名称列表
|
|
32
|
-
this.config = {}
|
|
33
|
-
|
|
34
|
-
this._framework = null
|
|
35
|
-
this._agent = null
|
|
36
|
-
this._parentAgent = null
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
install(framework) {
|
|
40
|
-
this._framework = framework
|
|
41
|
-
return this
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
start(framework) {
|
|
45
|
-
// 创建子Agent(可能需要等待父agent准备好)
|
|
46
|
-
this._createSubAgent()
|
|
47
|
-
|
|
48
|
-
// 注册委托工具到主Agent(通过framework获取父agent)
|
|
49
|
-
this._registerDelegateTool()
|
|
50
|
-
|
|
51
|
-
return this
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* 创建子Agent
|
|
56
|
-
*/
|
|
57
|
-
_createSubAgent() {
|
|
58
|
-
// 获取父Agent(主Agent)
|
|
59
|
-
const parentAgent = this._getParentAgent()
|
|
60
|
-
if (!parentAgent) {
|
|
61
|
-
// 父agent还没创建,监听事件等其创建
|
|
62
|
-
if (this._framework) {
|
|
63
|
-
this._framework.on('agent:created', (agent) => {
|
|
64
|
-
if (this._framework._mainAgent && agent === this._framework._mainAgent) {
|
|
65
|
-
// 父agent已创建,更新父agent引用
|
|
66
|
-
this._parentAgent = agent
|
|
67
|
-
// 重新尝试创建子agent
|
|
68
|
-
this._doCreateSubAgent(agent)
|
|
69
|
-
// 注册委托工具
|
|
70
|
-
this._registerDelegateTool()
|
|
71
|
-
}
|
|
72
|
-
})
|
|
73
|
-
}
|
|
74
|
-
return
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
this._doCreateSubAgent(parentAgent)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* 实际执行创建子Agent
|
|
82
|
-
*/
|
|
83
|
-
_doCreateSubAgent(parentAgent) {
|
|
84
|
-
// 避免重复创建
|
|
85
|
-
if (this._agent) {
|
|
86
|
-
return
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// 获取从父Agent继承的工具
|
|
90
|
-
const parentTools = this._getParentTools(parentAgent)
|
|
91
|
-
|
|
92
|
-
// 确定LLM配置
|
|
93
|
-
const aiPlugin = this._framework.pluginManager.get('ai')
|
|
94
|
-
const llmConfig = this.llmConfig || (aiPlugin ? aiPlugin.getConfig() : {})
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// 创建子Agent,使用完整的 md 文件内容作为系统提示词
|
|
98
|
-
this._agent = this._framework.createSubAgent({
|
|
99
|
-
name: this.name,
|
|
100
|
-
systemPrompt: this._getFullSystemPrompt(),
|
|
101
|
-
model: llmConfig.model,
|
|
102
|
-
provider: llmConfig.provider,
|
|
103
|
-
apiKey: llmConfig.apiKey,
|
|
104
|
-
baseURL: llmConfig.baseURL,
|
|
105
|
-
tools: this.tools||{}, // 自定义工具
|
|
106
|
-
parentTools: parentTools
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
// 注册从父Agent继承的工具
|
|
110
|
-
// for (const tool of parentTools) {
|
|
111
|
-
// this._framework.registerTool(tool)
|
|
112
|
-
// }
|
|
113
|
-
|
|
114
|
-
// Register custom tools (only for this subAgent, won't pollute global)
|
|
115
|
-
// tools format: { toolName: toolDef }
|
|
116
|
-
|
|
117
|
-
this._parentAgent = parentAgent
|
|
118
|
-
|
|
119
|
-
// 注册子Agent到父Agent
|
|
120
|
-
parentAgent.registerSubAgent(this.name, this._agent, this.role, this.description)
|
|
121
|
-
|
|
122
|
-
// 监听子Agent的chunk事件并转发给父Agent
|
|
123
|
-
this._agent.on('chunk', (chunk) => {
|
|
124
|
-
chunk.fromSubAgent = this.name
|
|
125
|
-
parentAgent.emit('chunk', chunk)
|
|
126
|
-
})
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* 获取父Agent
|
|
131
|
-
*/
|
|
132
|
-
_getParentAgent() {
|
|
133
|
-
// 优先使用外部传入的获取器
|
|
134
|
-
if (this.config._parentAgentGetter) {
|
|
135
|
-
return this.config._parentAgentGetter()
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// 尝试从framework获取主agent
|
|
139
|
-
if (this._framework._mainAgent) {
|
|
140
|
-
return this._framework._mainAgent
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// 查找最后一个创建的agent作为父agent
|
|
144
|
-
const agents = this._framework._agents || []
|
|
145
|
-
if (agents.length > 0) {
|
|
146
|
-
return agents[agents.length - 1]
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return null
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* 获取从父Agent继承的工具
|
|
154
|
-
*/
|
|
155
|
-
_getParentTools(parentAgent) {
|
|
156
|
-
const allTools = parentAgent.getTools ? parentAgent.getTools() : []
|
|
157
|
-
const toolMap = new Map(allTools.map(t => [t.name, t]))
|
|
158
|
-
|
|
159
|
-
// 如果没有指定 parentTools,返回全部
|
|
160
|
-
if (!this.parentTools || this.parentTools.length === 0) {
|
|
161
|
-
return allTools
|
|
162
|
-
}
|
|
163
|
-
// 过滤指定工具
|
|
164
|
-
const filtered = []
|
|
165
|
-
for (const toolName of this.parentTools) {
|
|
166
|
-
const tool = toolMap.get(toolName)
|
|
167
|
-
if (tool) {
|
|
168
|
-
filtered.push(tool)
|
|
169
|
-
}
|
|
170
|
-
// else {
|
|
171
|
-
// console.warn(`[SubAgent:${this.config.name}] Parent tool not found: ${toolName}`)
|
|
172
|
-
// }
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return filtered
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* 构建系统提示
|
|
180
|
-
*/
|
|
181
|
-
_buildSystemPrompt() {
|
|
182
|
-
return `你是 ${this.role}。
|
|
183
|
-
|
|
184
|
-
角色描述:${this.description || '一个专业的子Agent'}
|
|
185
|
-
|
|
186
|
-
当你被调用时,你应该:
|
|
187
|
-
1. 仔细理解任务要求
|
|
188
|
-
2. 使用你的工具集完成任务
|
|
189
|
-
3. 返回完整的操作结果
|
|
190
|
-
|
|
191
|
-
重要:返回结果要简洁明确,只包含最终结果,不需要解释过程。`
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* 注册委托工具到框架和主Agent
|
|
196
|
-
*/
|
|
197
|
-
_registerDelegateTool() {
|
|
198
|
-
const framework = this._framework
|
|
199
|
-
if (!framework) return
|
|
200
|
-
|
|
201
|
-
const agentName = this.name
|
|
202
|
-
const role = this.role
|
|
203
|
-
|
|
204
|
-
const toolDef = {
|
|
205
|
-
name: agentName,
|
|
206
|
-
description: `${role}:当需要${role}时,调用此工具执行任务。如:编译代码、运行测试、代码重构等。`,
|
|
207
|
-
inputSchema: z.object({
|
|
208
|
-
task: z.string().describe('给子Agent的具体任务描述')
|
|
209
|
-
}),
|
|
210
|
-
execute: async (args, fw) => {
|
|
211
|
-
if (!this._agent) {
|
|
212
|
-
return { error: `SubAgent ${agentName} not initialized` }
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const parentAgent = fw._mainAgent
|
|
216
|
-
if (!parentAgent) {
|
|
217
|
-
return { error: `Parent agent not found` }
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// 发射子Agent开始处理事件
|
|
221
|
-
parentAgent.emit('subagent:chat:start', {
|
|
222
|
-
parentAgent,
|
|
223
|
-
subAgent: this._agent,
|
|
224
|
-
subAgentName: agentName,
|
|
225
|
-
task: args.task
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
try {
|
|
229
|
-
const result = await this._agent.chat(args.task)
|
|
230
|
-
// 发射子Agent完成处理事件
|
|
231
|
-
parentAgent.emit('subagent:chat:end', {
|
|
232
|
-
parentAgent,
|
|
233
|
-
subAgent: this._agent,
|
|
234
|
-
subAgentName: agentName,
|
|
235
|
-
result
|
|
236
|
-
})
|
|
237
|
-
return {
|
|
238
|
-
agent: agentName,
|
|
239
|
-
result: result.message || result.error || result,
|
|
240
|
-
success: result.success !== false
|
|
241
|
-
}
|
|
242
|
-
} catch (err) {
|
|
243
|
-
// 发射子Agent错误事件
|
|
244
|
-
parentAgent.emit('subagent:error', {
|
|
245
|
-
parentAgent,
|
|
246
|
-
subAgent: this._agent,
|
|
247
|
-
subAgentName: agentName,
|
|
248
|
-
error: err.message
|
|
249
|
-
})
|
|
250
|
-
return {
|
|
251
|
-
agent: agentName,
|
|
252
|
-
error: err.message,
|
|
253
|
-
success: false
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// 注册到框架(供后续创建的 Agent 同步)
|
|
260
|
-
// framework.registerTool(toolDef)
|
|
261
|
-
|
|
262
|
-
// // 如果主 Agent 已存在,直接注册到它
|
|
263
|
-
// if (framework._mainAgent) {
|
|
264
|
-
// framework._mainAgent.registerTool(toolDef)
|
|
265
|
-
// }
|
|
266
|
-
|
|
267
|
-
//console.log(`[SubAgent:${this.config.name}] Delegate tool registered`)
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* 获取子Agent实例
|
|
272
|
-
*/
|
|
273
|
-
getAgent() {
|
|
274
|
-
return this._agent
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* 调用子Agent处理任务
|
|
279
|
-
* 延迟创建 Agent,使用完整的 md 文件内容作为系统提示词
|
|
280
|
-
*/
|
|
281
|
-
async chat(task) {
|
|
282
|
-
// 延迟创建 Agent(如果尚未创建)
|
|
283
|
-
if (!this._agent) {
|
|
284
|
-
await this._createSubAgentForChat();
|
|
285
|
-
} else {
|
|
286
|
-
// 每次调用时更新系统提示词,确保使用最新的 md 文件内容
|
|
287
|
-
this._updateAgentSystemPrompt();
|
|
288
|
-
}
|
|
289
|
-
return this._agent.chat(task)
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* 创建用于聊天的高完整度 Agent
|
|
294
|
-
* 使用 SubAgentConfig 读取完整的 md 文件内容
|
|
295
|
-
*/
|
|
296
|
-
async _createSubAgentForChat() {
|
|
297
|
-
const parentAgent = this._getParentAgent();
|
|
298
|
-
if (!parentAgent) {
|
|
299
|
-
throw new Error(`SubAgent ${this.name}: parent agent not found`);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// 使用 SubAgentConfig 获取完整的系统提示词
|
|
303
|
-
const systemPrompt = this._getFullSystemPrompt();
|
|
304
|
-
const parentTools = this._getParentTools(parentAgent)
|
|
305
|
-
// 获取 LLM 配置
|
|
306
|
-
const aiPlugin = this._framework.pluginManager.get('ai');
|
|
307
|
-
const llmConfig = this.llmConfig || (aiPlugin ? aiPlugin.getConfig() : {});
|
|
308
|
-
|
|
309
|
-
// 创建 Agent,使用完整的 md 内容作为系统提示词
|
|
310
|
-
this._agent = this._framework.createSubAgent({
|
|
311
|
-
name: this.name,
|
|
312
|
-
systemPrompt: systemPrompt,
|
|
313
|
-
model: llmConfig.model,
|
|
314
|
-
provider: llmConfig.provider,
|
|
315
|
-
apiKey: llmConfig.apiKey,
|
|
316
|
-
baseURL: llmConfig.baseURL,
|
|
317
|
-
tools: this.tools||{}, // 自定义工具
|
|
318
|
-
parentTools: parentTools
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
// 注册到父 Agent
|
|
322
|
-
this._parentAgent = parentAgent;
|
|
323
|
-
parentAgent.registerSubAgent(this.name, this._agent, this.role, this.description);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* 获取完整的系统提示词(从 md 文件读取)
|
|
328
|
-
*/
|
|
329
|
-
_getFullSystemPrompt() {
|
|
330
|
-
// 尝试从 SubAgentConfigManager 获取完整内容
|
|
331
|
-
const configManager = this._framework._subAgentConfigManager;
|
|
332
|
-
if (configManager) {
|
|
333
|
-
const config = configManager.get(this.name);
|
|
334
|
-
if (config) {
|
|
335
|
-
return config.getSystemPrompt();
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// 回退到默认的 _buildSystemPrompt
|
|
340
|
-
return this._buildSystemPrompt();
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* 更新 Agent 的系统提示词
|
|
345
|
-
*/
|
|
346
|
-
_updateAgentSystemPrompt() {
|
|
347
|
-
if (!this._agent) return;
|
|
348
|
-
const systemPrompt = this._getFullSystemPrompt();
|
|
349
|
-
this._agent.setSystemPrompt(systemPrompt);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
reload(framework) {
|
|
353
|
-
this._framework = framework
|
|
354
|
-
this._createSubAgent()
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
uninstall(framework) {
|
|
358
|
-
if (this._parentAgent) {
|
|
359
|
-
this._parentAgent.unregisterSubAgent(this.name)
|
|
360
|
-
}
|
|
361
|
-
if (this._agent) {
|
|
362
|
-
this._agent.destroy()
|
|
363
|
-
this._agent = null
|
|
364
|
-
}
|
|
365
|
-
this._parentAgent = null
|
|
366
|
-
this._framework = null
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* SubAgentManager - 管理多个子Agent
|
|
372
|
-
*/
|
|
373
|
-
class SubAgentManagerPlugin extends Plugin {
|
|
374
|
-
constructor(config = {}) {
|
|
375
|
-
super()
|
|
376
|
-
this.name = 'subagent-manager'
|
|
377
|
-
this.version = '1.0.0'
|
|
378
|
-
this.description = '子Agent管理器,统一管理多个子Agent'
|
|
379
|
-
|
|
380
|
-
this.systemPrompt= `你是一个子Agent管理器,负责管理多个子Agent。每个子Agent都有独立的工具集和角色描述。当需要处理特定任务时,你会选择合适的子Agent来执行。`
|
|
381
|
-
|
|
382
|
-
this.priority = 10
|
|
383
|
-
this.agents = config.agents || [] // 预定义的子Agent配置列表
|
|
384
|
-
this.config = {
|
|
385
|
-
agentsDir: config.agentsDir || null, // 子Agent配置目录
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
this._framework = null
|
|
389
|
-
this._subAgents = new Map()
|
|
390
|
-
this._fileWatcher = null
|
|
391
|
-
this._lastFileStates = new Map() // 记录文件状态用于检测变化
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
install(framework) {
|
|
395
|
-
this._framework = framework
|
|
396
|
-
return this
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
start(framework) {
|
|
400
|
-
// 从 agentsDir 加载子Agent配置
|
|
401
|
-
this._loadAgentsFromDir()
|
|
402
|
-
|
|
403
|
-
// 创建所有配置的子Agent
|
|
404
|
-
for (const agentConfig of this.agents) {
|
|
405
|
-
const plugin = new SubAgentPlugin(agentConfig)
|
|
406
|
-
plugin.install(framework)
|
|
407
|
-
plugin.start(framework)
|
|
408
|
-
this._subAgents.set(agentConfig.name, plugin)
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// 注册管理工具
|
|
412
|
-
// 如果主 Agent 已存在,直接注册到它(因为它不会自动同步框架工具)
|
|
413
|
-
if (framework._mainAgent) {
|
|
414
|
-
this._registerToolsToAgent(framework._mainAgent)
|
|
415
|
-
}
|
|
416
|
-
// 同时注册到框架(供后续创建的 Agent 同步)
|
|
417
|
-
this._registerTools(framework)
|
|
418
|
-
|
|
419
|
-
// 监听主 Agent 创建事件,为后续创建的主 Agent 注册工具
|
|
420
|
-
framework.on('agent:created', (agent) => {
|
|
421
|
-
if (framework._mainAgent && agent === framework._mainAgent) {
|
|
422
|
-
this._registerToolsToAgent(agent)
|
|
423
|
-
}
|
|
424
|
-
})
|
|
425
|
-
|
|
426
|
-
// 启动文件监控
|
|
427
|
-
this._startFileWatcher()
|
|
428
|
-
|
|
429
|
-
return this
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/**
|
|
433
|
-
* 注册管理工具到指定 Agent
|
|
434
|
-
*/
|
|
435
|
-
_registerToolsToAgent(agent) {
|
|
436
|
-
agent.registerTool({
|
|
437
|
-
name: 'subagent_list',
|
|
438
|
-
description: '列出所有子Agent',
|
|
439
|
-
inputSchema: z.object({}),
|
|
440
|
-
execute: async () => {
|
|
441
|
-
const agents = Array.from(this._subAgents.values()).map(p => ({
|
|
442
|
-
name: p.name,
|
|
443
|
-
role: p.role,
|
|
444
|
-
description: p.description,
|
|
445
|
-
toolCount: p._agent ? p._agent.getTools().length : 0
|
|
446
|
-
}))
|
|
447
|
-
return { success: true, agents }
|
|
448
|
-
}
|
|
449
|
-
})
|
|
450
|
-
|
|
451
|
-
agent.registerTool({
|
|
452
|
-
name: 'subagent_call',
|
|
453
|
-
description: '调用指定的子Agent处理任务',
|
|
454
|
-
inputSchema: z.object({
|
|
455
|
-
agentName: z.string().describe('子Agent名称'),
|
|
456
|
-
task: z.string().describe('任务描述')
|
|
457
|
-
}),
|
|
458
|
-
execute: async (args) => {
|
|
459
|
-
const plugin = this._subAgents.get(args.agentName)
|
|
460
|
-
if (!plugin) {
|
|
461
|
-
return { success: false, error: `SubAgent ${args.agentName} not found` }
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
try {
|
|
465
|
-
const result = await plugin.chat(args.task)
|
|
466
|
-
return {
|
|
467
|
-
success: true,
|
|
468
|
-
agent: args.agentName,
|
|
469
|
-
result: result.message || result,
|
|
470
|
-
success: result.success !== false
|
|
471
|
-
}
|
|
472
|
-
} catch (err) {
|
|
473
|
-
return { success: false, error: err.message }
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
})
|
|
477
|
-
|
|
478
|
-
agent.registerTool({
|
|
479
|
-
name: 'subagent_reload',
|
|
480
|
-
description: '重新加载子Agent配置(当新增或删除.agent/agents下的文件后使用)',
|
|
481
|
-
inputSchema: z.object({}),
|
|
482
|
-
execute: async () => {
|
|
483
|
-
try {
|
|
484
|
-
this.reload(this._framework)
|
|
485
|
-
return { success: true, message: '子Agent配置已重新加载' }
|
|
486
|
-
} catch (err) {
|
|
487
|
-
return { success: false, error: err.message }
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
})
|
|
491
|
-
|
|
492
|
-
//log.info(' Management tools registered to agent')
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
/**
|
|
496
|
-
* 注册管理工具到框架工具注册表
|
|
497
|
-
*/
|
|
498
|
-
_registerTools(framework) {
|
|
499
|
-
framework.registerTool({
|
|
500
|
-
name: 'subagent_list',
|
|
501
|
-
description: '列出所有子Agent',
|
|
502
|
-
inputSchema: z.object({}),
|
|
503
|
-
execute: async () => {
|
|
504
|
-
const agents = Array.from(this._subAgents.values()).map(p => ({
|
|
505
|
-
name: p.name,
|
|
506
|
-
role: p.role,
|
|
507
|
-
description: p.description,
|
|
508
|
-
toolCount: p._agent ? p._agent.getTools().length : 0
|
|
509
|
-
}))
|
|
510
|
-
return { success: true, agents }
|
|
511
|
-
}
|
|
512
|
-
})
|
|
513
|
-
|
|
514
|
-
framework.registerTool({
|
|
515
|
-
name: 'subagent_call',
|
|
516
|
-
description: '调用指定的子Agent处理任务',
|
|
517
|
-
inputSchema: z.object({
|
|
518
|
-
agentName: z.string().describe('子Agent名称'),
|
|
519
|
-
task: z.string().describe('任务描述')
|
|
520
|
-
}),
|
|
521
|
-
execute: async (args) => {
|
|
522
|
-
const plugin = this._subAgents.get(args.agentName)
|
|
523
|
-
if (!plugin) {
|
|
524
|
-
return { success: false, error: `SubAgent ${args.agentName} not found` }
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
try {
|
|
528
|
-
const result = await plugin.chat(args.task)
|
|
529
|
-
return {
|
|
530
|
-
success: true,
|
|
531
|
-
agent: args.agentName,
|
|
532
|
-
result: result.message || result,
|
|
533
|
-
success: result.success !== false
|
|
534
|
-
}
|
|
535
|
-
} catch (err) {
|
|
536
|
-
return { success: false, error: err.message }
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
})
|
|
540
|
-
|
|
541
|
-
framework.registerTool({
|
|
542
|
-
name: 'subagent_reload',
|
|
543
|
-
description: '重新加载子Agent配置(当新增或删除.agent/agents下的文件后使用)',
|
|
544
|
-
inputSchema: z.object({}),
|
|
545
|
-
execute: async () => {
|
|
546
|
-
try {
|
|
547
|
-
this.reload(this._framework)
|
|
548
|
-
return { success: true, message: '子Agent配置已重新加载' }
|
|
549
|
-
} catch (err) {
|
|
550
|
-
return { success: false, error: err.message }
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
})
|
|
554
|
-
|
|
555
|
-
//log.info(' Management tools registered to framework')
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
/**
|
|
559
|
-
* 从 agentsDir 目录加载子Agent配置
|
|
560
|
-
*/
|
|
561
|
-
_loadAgentsFromDir() {
|
|
562
|
-
const agentsDir = this.config.agentsDir
|
|
563
|
-
//log.info(' _loadAgentsFromDir called, agentsDir:', agentsDir)
|
|
564
|
-
if (!agentsDir || !fs.existsSync(agentsDir)) {
|
|
565
|
-
log.info(' agentsDir not found or does not exist')
|
|
566
|
-
return
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
const entries = fs.readdirSync(agentsDir, { withFileTypes: true })
|
|
570
|
-
//log.info(' Found entries:', entries.length)
|
|
571
|
-
|
|
572
|
-
for (const entry of entries) {
|
|
573
|
-
if (entry.isFile() && (entry.name.endsWith('.js') || entry.name.endsWith('.json') || entry.name.endsWith('.md'))) {
|
|
574
|
-
const baseName = entry.name.replace(/\.(js|json|md)$/, '')
|
|
575
|
-
const filePath = path.join(agentsDir, entry.name)
|
|
576
|
-
|
|
577
|
-
// 跳过与已有配置同名的
|
|
578
|
-
if (this.agents.some(a => a.name === baseName)) {
|
|
579
|
-
continue
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
try {
|
|
583
|
-
let agentConfig
|
|
584
|
-
if (entry.name.endsWith('.json')) {
|
|
585
|
-
const content = fs.readFileSync(filePath, 'utf-8')
|
|
586
|
-
agentConfig = JSON.parse(content)
|
|
587
|
-
} else if (entry.name.endsWith('.md')) {
|
|
588
|
-
// 解析 markdown 文件,提取 JSON 配置
|
|
589
|
-
agentConfig = this._parseMarkdownConfig(filePath, baseName)
|
|
590
|
-
//log.info(' Parsed md:', baseName, agentConfig)
|
|
591
|
-
} else {
|
|
592
|
-
// 清除缓存并加载
|
|
593
|
-
delete require.cache[require.resolve(filePath)]
|
|
594
|
-
const mod = require(filePath)
|
|
595
|
-
agentConfig = typeof mod === 'function' ? mod() : mod
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
if (agentConfig && agentConfig.name) {
|
|
599
|
-
agentConfig._fromDir = true
|
|
600
|
-
this.agents.push(agentConfig)
|
|
601
|
-
//log.info(' Loaded agent:', agentConfig.name)
|
|
602
|
-
} else {
|
|
603
|
-
log.warn(' Agent config has no name:', baseName, agentConfig)
|
|
604
|
-
}
|
|
605
|
-
} catch (err) {
|
|
606
|
-
log.warn(` Failed to load agent config from ${filePath}:`, err.message, err.stack)
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
/**
|
|
613
|
-
* 解析 markdown 文件中的配置
|
|
614
|
-
* 支持格式:
|
|
615
|
-
* ```json
|
|
616
|
-
* { "name": "xxx", "role": "xxx" }
|
|
617
|
-
* ```
|
|
618
|
-
* 或直接用 frontmatter 格式
|
|
619
|
-
*/
|
|
620
|
-
_parseMarkdownConfig(filePath, defaultName) {
|
|
621
|
-
const content = fs.readFileSync(filePath, 'utf-8')
|
|
622
|
-
//log.info(' _parseMarkdownConfig:', filePath)
|
|
623
|
-
|
|
624
|
-
// 尝试从 code block 中提取 JSON
|
|
625
|
-
const jsonMatch = content.match(/```(?:json)?\s*\n([\s\S]*?)\n\s*```/)
|
|
626
|
-
if (jsonMatch) {
|
|
627
|
-
try {
|
|
628
|
-
const config = JSON.parse(jsonMatch[1].trim())
|
|
629
|
-
//log.info(' Found JSON in code block')
|
|
630
|
-
return config
|
|
631
|
-
} catch (err) {
|
|
632
|
-
// JSON 解析失败,继续
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
// 尝试从 frontmatter 格式中提取
|
|
637
|
-
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/)
|
|
638
|
-
if (frontmatterMatch) {
|
|
639
|
-
//log.info(' Found frontmatter')
|
|
640
|
-
const frontmatterContent = frontmatterMatch[1]
|
|
641
|
-
const parsed = this._parseYamlLike(frontmatterContent)
|
|
642
|
-
if (parsed && parsed.name) {
|
|
643
|
-
//log.info(' Parsed frontmatter:', parsed)
|
|
644
|
-
return { name: parsed.name || defaultName, ...parsed }
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
// 从 markdown 内容中提取配置(key: value 格式)
|
|
649
|
-
const config = { name: defaultName }
|
|
650
|
-
const yamlMatch = content.match(/^(name|role|description|parentTools|tools):\s*(.+)$/m)
|
|
651
|
-
if (yamlMatch) {
|
|
652
|
-
config[yamlMatch[1]] = yamlMatch[2].trim()
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
// 如果没有找到配置,使用文件名作为 name
|
|
656
|
-
return config
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
/**
|
|
660
|
-
* 获取所有 subAgent 的基本信息列表
|
|
661
|
-
*/
|
|
662
|
-
getAllSubAgents() {
|
|
663
|
-
return Array.from(this._subAgents.values());
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
/**
|
|
667
|
-
* 解析类似 YAML 的配置
|
|
668
|
-
*/
|
|
669
|
-
_parseYamlLike(content) {
|
|
670
|
-
const result = {}
|
|
671
|
-
const lines = content.split('\n')
|
|
672
|
-
|
|
673
|
-
for (const line of lines) {
|
|
674
|
-
const match = line.match(/^(\w+):\s*(.+)$/)
|
|
675
|
-
if (match) {
|
|
676
|
-
const key = match[1]
|
|
677
|
-
let value = match[2].trim()
|
|
678
|
-
|
|
679
|
-
// 处理数组格式 [a, b, c] 或 [a b c]
|
|
680
|
-
if (value.startsWith('[') && value.endsWith(']')) {
|
|
681
|
-
value = value.slice(1, -1).split(/[,\s]+/).filter(Boolean)
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
result[key] = value
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
return result
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
/**
|
|
692
|
-
* 启动文件监控
|
|
693
|
-
*/
|
|
694
|
-
_startFileWatcher() {
|
|
695
|
-
const agentsDir = this.config.agentsDir
|
|
696
|
-
if (!agentsDir || !fs.existsSync(agentsDir)) {
|
|
697
|
-
return
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
// 记录初始文件状态
|
|
701
|
-
this._updateFileStates()
|
|
702
|
-
|
|
703
|
-
// 使用 fs.watch 监控目录变化
|
|
704
|
-
this._fileWatcher = fs.watch(agentsDir, { recursive: false }, (eventType, filename) => {
|
|
705
|
-
if (filename && (filename.endsWith('.js') || filename.endsWith('.json') || filename.endsWith('.md'))) {
|
|
706
|
-
//console.log(`[SubAgentManager] Detected change in agents dir: ${eventType} - ${filename}`)
|
|
707
|
-
|
|
708
|
-
// 防抖:延迟处理,等文件操作完成
|
|
709
|
-
setTimeout(() => {
|
|
710
|
-
this._checkAndReload()
|
|
711
|
-
}, 500)
|
|
712
|
-
}
|
|
713
|
-
})
|
|
714
|
-
|
|
715
|
-
//console.log(`[SubAgentManager] Watching agents dir for changes: ${agentsDir}`)
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
/**
|
|
719
|
-
* 更新文件状态
|
|
720
|
-
*/
|
|
721
|
-
_updateFileStates() {
|
|
722
|
-
const agentsDir = this.config.agentsDir
|
|
723
|
-
if (!agentsDir || !fs.existsSync(agentsDir)) {
|
|
724
|
-
return
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
this._lastFileStates.clear()
|
|
728
|
-
const entries = fs.readdirSync(agentsDir, { withFileTypes: true })
|
|
729
|
-
|
|
730
|
-
for (const entry of entries) {
|
|
731
|
-
if (entry.isFile() && (entry.name.endsWith('.js') || entry.name.endsWith('.json') || entry.name.endsWith('.md'))) {
|
|
732
|
-
const filePath = path.join(agentsDir, entry.name)
|
|
733
|
-
try {
|
|
734
|
-
const stat = fs.statSync(filePath)
|
|
735
|
-
this._lastFileStates.set(entry.name, {
|
|
736
|
-
mtime: stat.mtime.getTime(),
|
|
737
|
-
size: stat.size
|
|
738
|
-
})
|
|
739
|
-
} catch (err) {
|
|
740
|
-
// 忽略
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
/**
|
|
747
|
-
* 检查并重载(如果有变化)
|
|
748
|
-
*/
|
|
749
|
-
_checkAndReload() {
|
|
750
|
-
const agentsDir = this.config.agentsDir
|
|
751
|
-
if (!agentsDir || !fs.existsSync(agentsDir)) {
|
|
752
|
-
return
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
const currentStates = new Map()
|
|
756
|
-
const entries = fs.readdirSync(agentsDir, { withFileTypes: true })
|
|
757
|
-
let hasChanges = false
|
|
758
|
-
|
|
759
|
-
// 检查当前文件
|
|
760
|
-
for (const entry of entries) {
|
|
761
|
-
if (entry.isFile() && (entry.name.endsWith('.js') || entry.name.endsWith('.json') || entry.name.endsWith('.md'))) {
|
|
762
|
-
const filePath = path.join(agentsDir, entry.name)
|
|
763
|
-
try {
|
|
764
|
-
const stat = fs.statSync(filePath)
|
|
765
|
-
currentStates.set(entry.name, {
|
|
766
|
-
mtime: stat.mtime.getTime(),
|
|
767
|
-
size: stat.size
|
|
768
|
-
})
|
|
769
|
-
|
|
770
|
-
const lastState = this._lastFileStates.get(entry.name)
|
|
771
|
-
if (!lastState || lastState.mtime !== stat.mtime.getTime() || lastState.size !== stat.size) {
|
|
772
|
-
hasChanges = true
|
|
773
|
-
}
|
|
774
|
-
} catch (err) {
|
|
775
|
-
// 忽略
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
// 检查删除的文件
|
|
781
|
-
for (const [name] of this._lastFileStates) {
|
|
782
|
-
if (!currentStates.has(name)) {
|
|
783
|
-
hasChanges = true
|
|
784
|
-
break
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
if (hasChanges) {
|
|
789
|
-
//log.info(' File changes detected, reloading...')
|
|
790
|
-
this._updateFileStates()
|
|
791
|
-
this.reload(this._framework)
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
/**
|
|
796
|
-
* 获取子Agent
|
|
797
|
-
*/
|
|
798
|
-
getSubAgent(name) {
|
|
799
|
-
const plugin = this._subAgents.get(name)
|
|
800
|
-
return plugin ? plugin.getAgent() : null
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
reload(framework) {
|
|
804
|
-
// 停止文件监控
|
|
805
|
-
if (this._fileWatcher) {
|
|
806
|
-
this._fileWatcher.close()
|
|
807
|
-
this._fileWatcher = null
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
// 销毁旧的子Agent
|
|
811
|
-
for (const plugin of this._subAgents.values()) {
|
|
812
|
-
plugin.uninstall(framework)
|
|
813
|
-
}
|
|
814
|
-
this._subAgents.clear()
|
|
815
|
-
|
|
816
|
-
// 清空从目录加载的配置,重新加载
|
|
817
|
-
this.agents = this.agents.filter(a => !a._fromDir)
|
|
818
|
-
|
|
819
|
-
// 刷新 SubAgentConfigManager(如果存在)
|
|
820
|
-
if (framework._subAgentConfigManager) {
|
|
821
|
-
framework._subAgentConfigManager.refresh()
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
// 重新创建
|
|
825
|
-
this.start(framework)
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
uninstall(framework) {
|
|
829
|
-
// 停止文件监控
|
|
830
|
-
if (this._fileWatcher) {
|
|
831
|
-
this._fileWatcher.close()
|
|
832
|
-
this._fileWatcher = null
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
for (const plugin of this._subAgents.values()) {
|
|
836
|
-
plugin.uninstall(framework)
|
|
837
|
-
}
|
|
838
|
-
this._subAgents.clear()
|
|
839
|
-
this._framework = null
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
module.exports = {
|
|
844
|
-
SubAgentPlugin,
|
|
845
|
-
SubAgentManagerPlugin
|
|
1
|
+
/**
|
|
2
|
+
* SubAgent 子Agent插件
|
|
3
|
+
* 创建具有独立工具集的子Agent,用于分工处理不同领域的任务
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs')
|
|
7
|
+
const path = require('path')
|
|
8
|
+
const { Plugin } = require('../src/core/plugin-base')
|
|
9
|
+
const { logger } = require('../src/utils/logger')
|
|
10
|
+
const log = logger.child('SubAgent')
|
|
11
|
+
const { z } = require('zod')
|
|
12
|
+
const { Agent } = require('../src/core/agent')
|
|
13
|
+
|
|
14
|
+
class SubAgentPlugin extends Plugin {
|
|
15
|
+
constructor(config = {}) {
|
|
16
|
+
super()
|
|
17
|
+
this.name = config.name
|
|
18
|
+
this.version = '1.0.0'
|
|
19
|
+
this.description = config.description || `子Agent: ${config.name}`
|
|
20
|
+
this.priority = 10
|
|
21
|
+
|
|
22
|
+
this.system = true
|
|
23
|
+
|
|
24
|
+
this.role= config.role || config.name
|
|
25
|
+
this.tools = config.tools || {}
|
|
26
|
+
this.parentTools = config.parentTools || []
|
|
27
|
+
this.llmConfig = config.llmConfig || null
|
|
28
|
+
|
|
29
|
+
// 子Agent配置
|
|
30
|
+
// tools: { toolName: toolFn } 自定义工具(只属于此子Agent)
|
|
31
|
+
// parentTools: ['read_file'] 从父Agent继承的工具名称列表
|
|
32
|
+
this.config = {}
|
|
33
|
+
|
|
34
|
+
this._framework = null
|
|
35
|
+
this._agent = null
|
|
36
|
+
this._parentAgent = null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
install(framework) {
|
|
40
|
+
this._framework = framework
|
|
41
|
+
return this
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
start(framework) {
|
|
45
|
+
// 创建子Agent(可能需要等待父agent准备好)
|
|
46
|
+
this._createSubAgent()
|
|
47
|
+
|
|
48
|
+
// 注册委托工具到主Agent(通过framework获取父agent)
|
|
49
|
+
this._registerDelegateTool()
|
|
50
|
+
|
|
51
|
+
return this
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 创建子Agent
|
|
56
|
+
*/
|
|
57
|
+
_createSubAgent() {
|
|
58
|
+
// 获取父Agent(主Agent)
|
|
59
|
+
const parentAgent = this._getParentAgent()
|
|
60
|
+
if (!parentAgent) {
|
|
61
|
+
// 父agent还没创建,监听事件等其创建
|
|
62
|
+
if (this._framework) {
|
|
63
|
+
this._framework.on('agent:created', (agent) => {
|
|
64
|
+
if (this._framework._mainAgent && agent === this._framework._mainAgent) {
|
|
65
|
+
// 父agent已创建,更新父agent引用
|
|
66
|
+
this._parentAgent = agent
|
|
67
|
+
// 重新尝试创建子agent
|
|
68
|
+
this._doCreateSubAgent(agent)
|
|
69
|
+
// 注册委托工具
|
|
70
|
+
this._registerDelegateTool()
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this._doCreateSubAgent(parentAgent)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 实际执行创建子Agent
|
|
82
|
+
*/
|
|
83
|
+
_doCreateSubAgent(parentAgent) {
|
|
84
|
+
// 避免重复创建
|
|
85
|
+
if (this._agent) {
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 获取从父Agent继承的工具
|
|
90
|
+
const parentTools = this._getParentTools(parentAgent)
|
|
91
|
+
|
|
92
|
+
// 确定LLM配置
|
|
93
|
+
const aiPlugin = this._framework.pluginManager.get('ai')
|
|
94
|
+
const llmConfig = this.llmConfig || (aiPlugin ? aiPlugin.getConfig() : {})
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
// 创建子Agent,使用完整的 md 文件内容作为系统提示词
|
|
98
|
+
this._agent = this._framework.createSubAgent({
|
|
99
|
+
name: this.name,
|
|
100
|
+
systemPrompt: this._getFullSystemPrompt(),
|
|
101
|
+
model: llmConfig.model,
|
|
102
|
+
provider: llmConfig.provider,
|
|
103
|
+
apiKey: llmConfig.apiKey,
|
|
104
|
+
baseURL: llmConfig.baseURL,
|
|
105
|
+
tools: this.tools||{}, // 自定义工具
|
|
106
|
+
parentTools: parentTools
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
// 注册从父Agent继承的工具
|
|
110
|
+
// for (const tool of parentTools) {
|
|
111
|
+
// this._framework.registerTool(tool)
|
|
112
|
+
// }
|
|
113
|
+
|
|
114
|
+
// Register custom tools (only for this subAgent, won't pollute global)
|
|
115
|
+
// tools format: { toolName: toolDef }
|
|
116
|
+
|
|
117
|
+
this._parentAgent = parentAgent
|
|
118
|
+
|
|
119
|
+
// 注册子Agent到父Agent
|
|
120
|
+
parentAgent.registerSubAgent(this.name, this._agent, this.role, this.description)
|
|
121
|
+
|
|
122
|
+
// 监听子Agent的chunk事件并转发给父Agent
|
|
123
|
+
this._agent.on('chunk', (chunk) => {
|
|
124
|
+
chunk.fromSubAgent = this.name
|
|
125
|
+
parentAgent.emit('chunk', chunk)
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 获取父Agent
|
|
131
|
+
*/
|
|
132
|
+
_getParentAgent() {
|
|
133
|
+
// 优先使用外部传入的获取器
|
|
134
|
+
if (this.config._parentAgentGetter) {
|
|
135
|
+
return this.config._parentAgentGetter()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 尝试从framework获取主agent
|
|
139
|
+
if (this._framework._mainAgent) {
|
|
140
|
+
return this._framework._mainAgent
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 查找最后一个创建的agent作为父agent
|
|
144
|
+
const agents = this._framework._agents || []
|
|
145
|
+
if (agents.length > 0) {
|
|
146
|
+
return agents[agents.length - 1]
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return null
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 获取从父Agent继承的工具
|
|
154
|
+
*/
|
|
155
|
+
_getParentTools(parentAgent) {
|
|
156
|
+
const allTools = parentAgent.getTools ? parentAgent.getTools() : []
|
|
157
|
+
const toolMap = new Map(allTools.map(t => [t.name, t]))
|
|
158
|
+
|
|
159
|
+
// 如果没有指定 parentTools,返回全部
|
|
160
|
+
if (!this.parentTools || this.parentTools.length === 0) {
|
|
161
|
+
return allTools
|
|
162
|
+
}
|
|
163
|
+
// 过滤指定工具
|
|
164
|
+
const filtered = []
|
|
165
|
+
for (const toolName of this.parentTools) {
|
|
166
|
+
const tool = toolMap.get(toolName)
|
|
167
|
+
if (tool) {
|
|
168
|
+
filtered.push(tool)
|
|
169
|
+
}
|
|
170
|
+
// else {
|
|
171
|
+
// console.warn(`[SubAgent:${this.config.name}] Parent tool not found: ${toolName}`)
|
|
172
|
+
// }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return filtered
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 构建系统提示
|
|
180
|
+
*/
|
|
181
|
+
_buildSystemPrompt() {
|
|
182
|
+
return `你是 ${this.role}。
|
|
183
|
+
|
|
184
|
+
角色描述:${this.description || '一个专业的子Agent'}
|
|
185
|
+
|
|
186
|
+
当你被调用时,你应该:
|
|
187
|
+
1. 仔细理解任务要求
|
|
188
|
+
2. 使用你的工具集完成任务
|
|
189
|
+
3. 返回完整的操作结果
|
|
190
|
+
|
|
191
|
+
重要:返回结果要简洁明确,只包含最终结果,不需要解释过程。`
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 注册委托工具到框架和主Agent
|
|
196
|
+
*/
|
|
197
|
+
_registerDelegateTool() {
|
|
198
|
+
const framework = this._framework
|
|
199
|
+
if (!framework) return
|
|
200
|
+
|
|
201
|
+
const agentName = this.name
|
|
202
|
+
const role = this.role
|
|
203
|
+
|
|
204
|
+
const toolDef = {
|
|
205
|
+
name: agentName,
|
|
206
|
+
description: `${role}:当需要${role}时,调用此工具执行任务。如:编译代码、运行测试、代码重构等。`,
|
|
207
|
+
inputSchema: z.object({
|
|
208
|
+
task: z.string().describe('给子Agent的具体任务描述')
|
|
209
|
+
}),
|
|
210
|
+
execute: async (args, fw) => {
|
|
211
|
+
if (!this._agent) {
|
|
212
|
+
return { error: `SubAgent ${agentName} not initialized` }
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const parentAgent = fw._mainAgent
|
|
216
|
+
if (!parentAgent) {
|
|
217
|
+
return { error: `Parent agent not found` }
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// 发射子Agent开始处理事件
|
|
221
|
+
parentAgent.emit('subagent:chat:start', {
|
|
222
|
+
parentAgent,
|
|
223
|
+
subAgent: this._agent,
|
|
224
|
+
subAgentName: agentName,
|
|
225
|
+
task: args.task
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
const result = await this._agent.chat(args.task)
|
|
230
|
+
// 发射子Agent完成处理事件
|
|
231
|
+
parentAgent.emit('subagent:chat:end', {
|
|
232
|
+
parentAgent,
|
|
233
|
+
subAgent: this._agent,
|
|
234
|
+
subAgentName: agentName,
|
|
235
|
+
result
|
|
236
|
+
})
|
|
237
|
+
return {
|
|
238
|
+
agent: agentName,
|
|
239
|
+
result: result.message || result.error || result,
|
|
240
|
+
success: result.success !== false
|
|
241
|
+
}
|
|
242
|
+
} catch (err) {
|
|
243
|
+
// 发射子Agent错误事件
|
|
244
|
+
parentAgent.emit('subagent:error', {
|
|
245
|
+
parentAgent,
|
|
246
|
+
subAgent: this._agent,
|
|
247
|
+
subAgentName: agentName,
|
|
248
|
+
error: err.message
|
|
249
|
+
})
|
|
250
|
+
return {
|
|
251
|
+
agent: agentName,
|
|
252
|
+
error: err.message,
|
|
253
|
+
success: false
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// 注册到框架(供后续创建的 Agent 同步)
|
|
260
|
+
// framework.registerTool(toolDef)
|
|
261
|
+
|
|
262
|
+
// // 如果主 Agent 已存在,直接注册到它
|
|
263
|
+
// if (framework._mainAgent) {
|
|
264
|
+
// framework._mainAgent.registerTool(toolDef)
|
|
265
|
+
// }
|
|
266
|
+
|
|
267
|
+
//console.log(`[SubAgent:${this.config.name}] Delegate tool registered`)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* 获取子Agent实例
|
|
272
|
+
*/
|
|
273
|
+
getAgent() {
|
|
274
|
+
return this._agent
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* 调用子Agent处理任务
|
|
279
|
+
* 延迟创建 Agent,使用完整的 md 文件内容作为系统提示词
|
|
280
|
+
*/
|
|
281
|
+
async chat(task) {
|
|
282
|
+
// 延迟创建 Agent(如果尚未创建)
|
|
283
|
+
if (!this._agent) {
|
|
284
|
+
await this._createSubAgentForChat();
|
|
285
|
+
} else {
|
|
286
|
+
// 每次调用时更新系统提示词,确保使用最新的 md 文件内容
|
|
287
|
+
this._updateAgentSystemPrompt();
|
|
288
|
+
}
|
|
289
|
+
return this._agent.chat(task)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* 创建用于聊天的高完整度 Agent
|
|
294
|
+
* 使用 SubAgentConfig 读取完整的 md 文件内容
|
|
295
|
+
*/
|
|
296
|
+
async _createSubAgentForChat() {
|
|
297
|
+
const parentAgent = this._getParentAgent();
|
|
298
|
+
if (!parentAgent) {
|
|
299
|
+
throw new Error(`SubAgent ${this.name}: parent agent not found`);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// 使用 SubAgentConfig 获取完整的系统提示词
|
|
303
|
+
const systemPrompt = this._getFullSystemPrompt();
|
|
304
|
+
const parentTools = this._getParentTools(parentAgent)
|
|
305
|
+
// 获取 LLM 配置
|
|
306
|
+
const aiPlugin = this._framework.pluginManager.get('ai');
|
|
307
|
+
const llmConfig = this.llmConfig || (aiPlugin ? aiPlugin.getConfig() : {});
|
|
308
|
+
|
|
309
|
+
// 创建 Agent,使用完整的 md 内容作为系统提示词
|
|
310
|
+
this._agent = this._framework.createSubAgent({
|
|
311
|
+
name: this.name,
|
|
312
|
+
systemPrompt: systemPrompt,
|
|
313
|
+
model: llmConfig.model,
|
|
314
|
+
provider: llmConfig.provider,
|
|
315
|
+
apiKey: llmConfig.apiKey,
|
|
316
|
+
baseURL: llmConfig.baseURL,
|
|
317
|
+
tools: this.tools||{}, // 自定义工具
|
|
318
|
+
parentTools: parentTools
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// 注册到父 Agent
|
|
322
|
+
this._parentAgent = parentAgent;
|
|
323
|
+
parentAgent.registerSubAgent(this.name, this._agent, this.role, this.description);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* 获取完整的系统提示词(从 md 文件读取)
|
|
328
|
+
*/
|
|
329
|
+
_getFullSystemPrompt() {
|
|
330
|
+
// 尝试从 SubAgentConfigManager 获取完整内容
|
|
331
|
+
const configManager = this._framework._subAgentConfigManager;
|
|
332
|
+
if (configManager) {
|
|
333
|
+
const config = configManager.get(this.name);
|
|
334
|
+
if (config) {
|
|
335
|
+
return config.getSystemPrompt();
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// 回退到默认的 _buildSystemPrompt
|
|
340
|
+
return this._buildSystemPrompt();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* 更新 Agent 的系统提示词
|
|
345
|
+
*/
|
|
346
|
+
_updateAgentSystemPrompt() {
|
|
347
|
+
if (!this._agent) return;
|
|
348
|
+
const systemPrompt = this._getFullSystemPrompt();
|
|
349
|
+
this._agent.setSystemPrompt(systemPrompt);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
reload(framework) {
|
|
353
|
+
this._framework = framework
|
|
354
|
+
this._createSubAgent()
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
uninstall(framework) {
|
|
358
|
+
if (this._parentAgent) {
|
|
359
|
+
this._parentAgent.unregisterSubAgent(this.name)
|
|
360
|
+
}
|
|
361
|
+
if (this._agent) {
|
|
362
|
+
this._agent.destroy()
|
|
363
|
+
this._agent = null
|
|
364
|
+
}
|
|
365
|
+
this._parentAgent = null
|
|
366
|
+
this._framework = null
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* SubAgentManager - 管理多个子Agent
|
|
372
|
+
*/
|
|
373
|
+
class SubAgentManagerPlugin extends Plugin {
|
|
374
|
+
constructor(config = {}) {
|
|
375
|
+
super()
|
|
376
|
+
this.name = 'subagent-manager'
|
|
377
|
+
this.version = '1.0.0'
|
|
378
|
+
this.description = '子Agent管理器,统一管理多个子Agent'
|
|
379
|
+
|
|
380
|
+
this.systemPrompt= `你是一个子Agent管理器,负责管理多个子Agent。每个子Agent都有独立的工具集和角色描述。当需要处理特定任务时,你会选择合适的子Agent来执行。`
|
|
381
|
+
|
|
382
|
+
this.priority = 10
|
|
383
|
+
this.agents = config.agents || [] // 预定义的子Agent配置列表
|
|
384
|
+
this.config = {
|
|
385
|
+
agentsDir: config.agentsDir || null, // 子Agent配置目录
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
this._framework = null
|
|
389
|
+
this._subAgents = new Map()
|
|
390
|
+
this._fileWatcher = null
|
|
391
|
+
this._lastFileStates = new Map() // 记录文件状态用于检测变化
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
install(framework) {
|
|
395
|
+
this._framework = framework
|
|
396
|
+
return this
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
start(framework) {
|
|
400
|
+
// 从 agentsDir 加载子Agent配置
|
|
401
|
+
this._loadAgentsFromDir()
|
|
402
|
+
|
|
403
|
+
// 创建所有配置的子Agent
|
|
404
|
+
for (const agentConfig of this.agents) {
|
|
405
|
+
const plugin = new SubAgentPlugin(agentConfig)
|
|
406
|
+
plugin.install(framework)
|
|
407
|
+
plugin.start(framework)
|
|
408
|
+
this._subAgents.set(agentConfig.name, plugin)
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// 注册管理工具
|
|
412
|
+
// 如果主 Agent 已存在,直接注册到它(因为它不会自动同步框架工具)
|
|
413
|
+
if (framework._mainAgent) {
|
|
414
|
+
this._registerToolsToAgent(framework._mainAgent)
|
|
415
|
+
}
|
|
416
|
+
// 同时注册到框架(供后续创建的 Agent 同步)
|
|
417
|
+
this._registerTools(framework)
|
|
418
|
+
|
|
419
|
+
// 监听主 Agent 创建事件,为后续创建的主 Agent 注册工具
|
|
420
|
+
framework.on('agent:created', (agent) => {
|
|
421
|
+
if (framework._mainAgent && agent === framework._mainAgent) {
|
|
422
|
+
this._registerToolsToAgent(agent)
|
|
423
|
+
}
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
// 启动文件监控
|
|
427
|
+
this._startFileWatcher()
|
|
428
|
+
|
|
429
|
+
return this
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* 注册管理工具到指定 Agent
|
|
434
|
+
*/
|
|
435
|
+
_registerToolsToAgent(agent) {
|
|
436
|
+
agent.registerTool({
|
|
437
|
+
name: 'subagent_list',
|
|
438
|
+
description: '列出所有子Agent',
|
|
439
|
+
inputSchema: z.object({}),
|
|
440
|
+
execute: async () => {
|
|
441
|
+
const agents = Array.from(this._subAgents.values()).map(p => ({
|
|
442
|
+
name: p.name,
|
|
443
|
+
role: p.role,
|
|
444
|
+
description: p.description,
|
|
445
|
+
toolCount: p._agent ? p._agent.getTools().length : 0
|
|
446
|
+
}))
|
|
447
|
+
return { success: true, agents }
|
|
448
|
+
}
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
agent.registerTool({
|
|
452
|
+
name: 'subagent_call',
|
|
453
|
+
description: '调用指定的子Agent处理任务',
|
|
454
|
+
inputSchema: z.object({
|
|
455
|
+
agentName: z.string().describe('子Agent名称'),
|
|
456
|
+
task: z.string().describe('任务描述')
|
|
457
|
+
}),
|
|
458
|
+
execute: async (args) => {
|
|
459
|
+
const plugin = this._subAgents.get(args.agentName)
|
|
460
|
+
if (!plugin) {
|
|
461
|
+
return { success: false, error: `SubAgent ${args.agentName} not found` }
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
try {
|
|
465
|
+
const result = await plugin.chat(args.task)
|
|
466
|
+
return {
|
|
467
|
+
success: true,
|
|
468
|
+
agent: args.agentName,
|
|
469
|
+
result: result.message || result,
|
|
470
|
+
success: result.success !== false
|
|
471
|
+
}
|
|
472
|
+
} catch (err) {
|
|
473
|
+
return { success: false, error: err.message }
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
})
|
|
477
|
+
|
|
478
|
+
agent.registerTool({
|
|
479
|
+
name: 'subagent_reload',
|
|
480
|
+
description: '重新加载子Agent配置(当新增或删除.agent/agents下的文件后使用)',
|
|
481
|
+
inputSchema: z.object({}),
|
|
482
|
+
execute: async () => {
|
|
483
|
+
try {
|
|
484
|
+
this.reload(this._framework)
|
|
485
|
+
return { success: true, message: '子Agent配置已重新加载' }
|
|
486
|
+
} catch (err) {
|
|
487
|
+
return { success: false, error: err.message }
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
})
|
|
491
|
+
|
|
492
|
+
//log.info(' Management tools registered to agent')
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* 注册管理工具到框架工具注册表
|
|
497
|
+
*/
|
|
498
|
+
_registerTools(framework) {
|
|
499
|
+
framework.registerTool({
|
|
500
|
+
name: 'subagent_list',
|
|
501
|
+
description: '列出所有子Agent',
|
|
502
|
+
inputSchema: z.object({}),
|
|
503
|
+
execute: async () => {
|
|
504
|
+
const agents = Array.from(this._subAgents.values()).map(p => ({
|
|
505
|
+
name: p.name,
|
|
506
|
+
role: p.role,
|
|
507
|
+
description: p.description,
|
|
508
|
+
toolCount: p._agent ? p._agent.getTools().length : 0
|
|
509
|
+
}))
|
|
510
|
+
return { success: true, agents }
|
|
511
|
+
}
|
|
512
|
+
})
|
|
513
|
+
|
|
514
|
+
framework.registerTool({
|
|
515
|
+
name: 'subagent_call',
|
|
516
|
+
description: '调用指定的子Agent处理任务',
|
|
517
|
+
inputSchema: z.object({
|
|
518
|
+
agentName: z.string().describe('子Agent名称'),
|
|
519
|
+
task: z.string().describe('任务描述')
|
|
520
|
+
}),
|
|
521
|
+
execute: async (args) => {
|
|
522
|
+
const plugin = this._subAgents.get(args.agentName)
|
|
523
|
+
if (!plugin) {
|
|
524
|
+
return { success: false, error: `SubAgent ${args.agentName} not found` }
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
try {
|
|
528
|
+
const result = await plugin.chat(args.task)
|
|
529
|
+
return {
|
|
530
|
+
success: true,
|
|
531
|
+
agent: args.agentName,
|
|
532
|
+
result: result.message || result,
|
|
533
|
+
success: result.success !== false
|
|
534
|
+
}
|
|
535
|
+
} catch (err) {
|
|
536
|
+
return { success: false, error: err.message }
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
})
|
|
540
|
+
|
|
541
|
+
framework.registerTool({
|
|
542
|
+
name: 'subagent_reload',
|
|
543
|
+
description: '重新加载子Agent配置(当新增或删除.agent/agents下的文件后使用)',
|
|
544
|
+
inputSchema: z.object({}),
|
|
545
|
+
execute: async () => {
|
|
546
|
+
try {
|
|
547
|
+
this.reload(this._framework)
|
|
548
|
+
return { success: true, message: '子Agent配置已重新加载' }
|
|
549
|
+
} catch (err) {
|
|
550
|
+
return { success: false, error: err.message }
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
})
|
|
554
|
+
|
|
555
|
+
//log.info(' Management tools registered to framework')
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* 从 agentsDir 目录加载子Agent配置
|
|
560
|
+
*/
|
|
561
|
+
_loadAgentsFromDir() {
|
|
562
|
+
const agentsDir = this.config.agentsDir
|
|
563
|
+
//log.info(' _loadAgentsFromDir called, agentsDir:', agentsDir)
|
|
564
|
+
if (!agentsDir || !fs.existsSync(agentsDir)) {
|
|
565
|
+
log.info(' agentsDir not found or does not exist')
|
|
566
|
+
return
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
const entries = fs.readdirSync(agentsDir, { withFileTypes: true })
|
|
570
|
+
//log.info(' Found entries:', entries.length)
|
|
571
|
+
|
|
572
|
+
for (const entry of entries) {
|
|
573
|
+
if (entry.isFile() && (entry.name.endsWith('.js') || entry.name.endsWith('.json') || entry.name.endsWith('.md'))) {
|
|
574
|
+
const baseName = entry.name.replace(/\.(js|json|md)$/, '')
|
|
575
|
+
const filePath = path.join(agentsDir, entry.name)
|
|
576
|
+
|
|
577
|
+
// 跳过与已有配置同名的
|
|
578
|
+
if (this.agents.some(a => a.name === baseName)) {
|
|
579
|
+
continue
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
try {
|
|
583
|
+
let agentConfig
|
|
584
|
+
if (entry.name.endsWith('.json')) {
|
|
585
|
+
const content = fs.readFileSync(filePath, 'utf-8')
|
|
586
|
+
agentConfig = JSON.parse(content)
|
|
587
|
+
} else if (entry.name.endsWith('.md')) {
|
|
588
|
+
// 解析 markdown 文件,提取 JSON 配置
|
|
589
|
+
agentConfig = this._parseMarkdownConfig(filePath, baseName)
|
|
590
|
+
//log.info(' Parsed md:', baseName, agentConfig)
|
|
591
|
+
} else {
|
|
592
|
+
// 清除缓存并加载
|
|
593
|
+
delete require.cache[require.resolve(filePath)]
|
|
594
|
+
const mod = require(filePath)
|
|
595
|
+
agentConfig = typeof mod === 'function' ? mod() : mod
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if (agentConfig && agentConfig.name) {
|
|
599
|
+
agentConfig._fromDir = true
|
|
600
|
+
this.agents.push(agentConfig)
|
|
601
|
+
//log.info(' Loaded agent:', agentConfig.name)
|
|
602
|
+
} else {
|
|
603
|
+
log.warn(' Agent config has no name:', baseName, agentConfig)
|
|
604
|
+
}
|
|
605
|
+
} catch (err) {
|
|
606
|
+
log.warn(` Failed to load agent config from ${filePath}:`, err.message, err.stack)
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* 解析 markdown 文件中的配置
|
|
614
|
+
* 支持格式:
|
|
615
|
+
* ```json
|
|
616
|
+
* { "name": "xxx", "role": "xxx" }
|
|
617
|
+
* ```
|
|
618
|
+
* 或直接用 frontmatter 格式
|
|
619
|
+
*/
|
|
620
|
+
_parseMarkdownConfig(filePath, defaultName) {
|
|
621
|
+
const content = fs.readFileSync(filePath, 'utf-8')
|
|
622
|
+
//log.info(' _parseMarkdownConfig:', filePath)
|
|
623
|
+
|
|
624
|
+
// 尝试从 code block 中提取 JSON
|
|
625
|
+
const jsonMatch = content.match(/```(?:json)?\s*\n([\s\S]*?)\n\s*```/)
|
|
626
|
+
if (jsonMatch) {
|
|
627
|
+
try {
|
|
628
|
+
const config = JSON.parse(jsonMatch[1].trim())
|
|
629
|
+
//log.info(' Found JSON in code block')
|
|
630
|
+
return config
|
|
631
|
+
} catch (err) {
|
|
632
|
+
// JSON 解析失败,继续
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// 尝试从 frontmatter 格式中提取
|
|
637
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/)
|
|
638
|
+
if (frontmatterMatch) {
|
|
639
|
+
//log.info(' Found frontmatter')
|
|
640
|
+
const frontmatterContent = frontmatterMatch[1]
|
|
641
|
+
const parsed = this._parseYamlLike(frontmatterContent)
|
|
642
|
+
if (parsed && parsed.name) {
|
|
643
|
+
//log.info(' Parsed frontmatter:', parsed)
|
|
644
|
+
return { name: parsed.name || defaultName, ...parsed }
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// 从 markdown 内容中提取配置(key: value 格式)
|
|
649
|
+
const config = { name: defaultName }
|
|
650
|
+
const yamlMatch = content.match(/^(name|role|description|parentTools|tools):\s*(.+)$/m)
|
|
651
|
+
if (yamlMatch) {
|
|
652
|
+
config[yamlMatch[1]] = yamlMatch[2].trim()
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// 如果没有找到配置,使用文件名作为 name
|
|
656
|
+
return config
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* 获取所有 subAgent 的基本信息列表
|
|
661
|
+
*/
|
|
662
|
+
getAllSubAgents() {
|
|
663
|
+
return Array.from(this._subAgents.values());
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* 解析类似 YAML 的配置
|
|
668
|
+
*/
|
|
669
|
+
_parseYamlLike(content) {
|
|
670
|
+
const result = {}
|
|
671
|
+
const lines = content.split('\n')
|
|
672
|
+
|
|
673
|
+
for (const line of lines) {
|
|
674
|
+
const match = line.match(/^(\w+):\s*(.+)$/)
|
|
675
|
+
if (match) {
|
|
676
|
+
const key = match[1]
|
|
677
|
+
let value = match[2].trim()
|
|
678
|
+
|
|
679
|
+
// 处理数组格式 [a, b, c] 或 [a b c]
|
|
680
|
+
if (value.startsWith('[') && value.endsWith(']')) {
|
|
681
|
+
value = value.slice(1, -1).split(/[,\s]+/).filter(Boolean)
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
result[key] = value
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
return result
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* 启动文件监控
|
|
693
|
+
*/
|
|
694
|
+
_startFileWatcher() {
|
|
695
|
+
const agentsDir = this.config.agentsDir
|
|
696
|
+
if (!agentsDir || !fs.existsSync(agentsDir)) {
|
|
697
|
+
return
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// 记录初始文件状态
|
|
701
|
+
this._updateFileStates()
|
|
702
|
+
|
|
703
|
+
// 使用 fs.watch 监控目录变化
|
|
704
|
+
this._fileWatcher = fs.watch(agentsDir, { recursive: false }, (eventType, filename) => {
|
|
705
|
+
if (filename && (filename.endsWith('.js') || filename.endsWith('.json') || filename.endsWith('.md'))) {
|
|
706
|
+
//console.log(`[SubAgentManager] Detected change in agents dir: ${eventType} - ${filename}`)
|
|
707
|
+
|
|
708
|
+
// 防抖:延迟处理,等文件操作完成
|
|
709
|
+
setTimeout(() => {
|
|
710
|
+
this._checkAndReload()
|
|
711
|
+
}, 500)
|
|
712
|
+
}
|
|
713
|
+
})
|
|
714
|
+
|
|
715
|
+
//console.log(`[SubAgentManager] Watching agents dir for changes: ${agentsDir}`)
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* 更新文件状态
|
|
720
|
+
*/
|
|
721
|
+
_updateFileStates() {
|
|
722
|
+
const agentsDir = this.config.agentsDir
|
|
723
|
+
if (!agentsDir || !fs.existsSync(agentsDir)) {
|
|
724
|
+
return
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
this._lastFileStates.clear()
|
|
728
|
+
const entries = fs.readdirSync(agentsDir, { withFileTypes: true })
|
|
729
|
+
|
|
730
|
+
for (const entry of entries) {
|
|
731
|
+
if (entry.isFile() && (entry.name.endsWith('.js') || entry.name.endsWith('.json') || entry.name.endsWith('.md'))) {
|
|
732
|
+
const filePath = path.join(agentsDir, entry.name)
|
|
733
|
+
try {
|
|
734
|
+
const stat = fs.statSync(filePath)
|
|
735
|
+
this._lastFileStates.set(entry.name, {
|
|
736
|
+
mtime: stat.mtime.getTime(),
|
|
737
|
+
size: stat.size
|
|
738
|
+
})
|
|
739
|
+
} catch (err) {
|
|
740
|
+
// 忽略
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* 检查并重载(如果有变化)
|
|
748
|
+
*/
|
|
749
|
+
_checkAndReload() {
|
|
750
|
+
const agentsDir = this.config.agentsDir
|
|
751
|
+
if (!agentsDir || !fs.existsSync(agentsDir)) {
|
|
752
|
+
return
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
const currentStates = new Map()
|
|
756
|
+
const entries = fs.readdirSync(agentsDir, { withFileTypes: true })
|
|
757
|
+
let hasChanges = false
|
|
758
|
+
|
|
759
|
+
// 检查当前文件
|
|
760
|
+
for (const entry of entries) {
|
|
761
|
+
if (entry.isFile() && (entry.name.endsWith('.js') || entry.name.endsWith('.json') || entry.name.endsWith('.md'))) {
|
|
762
|
+
const filePath = path.join(agentsDir, entry.name)
|
|
763
|
+
try {
|
|
764
|
+
const stat = fs.statSync(filePath)
|
|
765
|
+
currentStates.set(entry.name, {
|
|
766
|
+
mtime: stat.mtime.getTime(),
|
|
767
|
+
size: stat.size
|
|
768
|
+
})
|
|
769
|
+
|
|
770
|
+
const lastState = this._lastFileStates.get(entry.name)
|
|
771
|
+
if (!lastState || lastState.mtime !== stat.mtime.getTime() || lastState.size !== stat.size) {
|
|
772
|
+
hasChanges = true
|
|
773
|
+
}
|
|
774
|
+
} catch (err) {
|
|
775
|
+
// 忽略
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// 检查删除的文件
|
|
781
|
+
for (const [name] of this._lastFileStates) {
|
|
782
|
+
if (!currentStates.has(name)) {
|
|
783
|
+
hasChanges = true
|
|
784
|
+
break
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (hasChanges) {
|
|
789
|
+
//log.info(' File changes detected, reloading...')
|
|
790
|
+
this._updateFileStates()
|
|
791
|
+
this.reload(this._framework)
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* 获取子Agent
|
|
797
|
+
*/
|
|
798
|
+
getSubAgent(name) {
|
|
799
|
+
const plugin = this._subAgents.get(name)
|
|
800
|
+
return plugin ? plugin.getAgent() : null
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
reload(framework) {
|
|
804
|
+
// 停止文件监控
|
|
805
|
+
if (this._fileWatcher) {
|
|
806
|
+
this._fileWatcher.close()
|
|
807
|
+
this._fileWatcher = null
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// 销毁旧的子Agent
|
|
811
|
+
for (const plugin of this._subAgents.values()) {
|
|
812
|
+
plugin.uninstall(framework)
|
|
813
|
+
}
|
|
814
|
+
this._subAgents.clear()
|
|
815
|
+
|
|
816
|
+
// 清空从目录加载的配置,重新加载
|
|
817
|
+
this.agents = this.agents.filter(a => !a._fromDir)
|
|
818
|
+
|
|
819
|
+
// 刷新 SubAgentConfigManager(如果存在)
|
|
820
|
+
if (framework._subAgentConfigManager) {
|
|
821
|
+
framework._subAgentConfigManager.refresh()
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// 重新创建
|
|
825
|
+
this.start(framework)
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
uninstall(framework) {
|
|
829
|
+
// 停止文件监控
|
|
830
|
+
if (this._fileWatcher) {
|
|
831
|
+
this._fileWatcher.close()
|
|
832
|
+
this._fileWatcher = null
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
for (const plugin of this._subAgents.values()) {
|
|
836
|
+
plugin.uninstall(framework)
|
|
837
|
+
}
|
|
838
|
+
this._subAgents.clear()
|
|
839
|
+
this._framework = null
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
module.exports = {
|
|
844
|
+
SubAgentPlugin,
|
|
845
|
+
SubAgentManagerPlugin
|
|
846
846
|
}
|